| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/ui/gtk/browser_titlebar.h" | 5 #include "chrome/browser/ui/gtk/browser_titlebar.h" |
| 6 | 6 |
| 7 #include <gdk/gdkkeysyms.h> | 7 #include <gdk/gdkkeysyms.h> |
| 8 #include <gtk/gtk.h> | 8 #include <gtk/gtk.h> |
| 9 | 9 |
| 10 #include <string> | 10 #include <string> |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 49 #include "grit/theme_resources.h" | 49 #include "grit/theme_resources.h" |
| 50 #include "grit/theme_resources_standard.h" | 50 #include "grit/theme_resources_standard.h" |
| 51 #include "grit/ui_resources.h" | 51 #include "grit/ui_resources.h" |
| 52 #include "grit/ui_resources_standard.h" | 52 #include "grit/ui_resources_standard.h" |
| 53 #include "ui/base/gtk/gtk_hig_constants.h" | 53 #include "ui/base/gtk/gtk_hig_constants.h" |
| 54 #include "ui/base/l10n/l10n_util.h" | 54 #include "ui/base/l10n/l10n_util.h" |
| 55 #include "ui/base/resource/resource_bundle.h" | 55 #include "ui/base/resource/resource_bundle.h" |
| 56 #include "ui/base/x/active_window_watcher_x.h" | 56 #include "ui/base/x/active_window_watcher_x.h" |
| 57 #include "ui/gfx/gtk_util.h" | 57 #include "ui/gfx/gtk_util.h" |
| 58 #include "ui/gfx/image/image.h" | 58 #include "ui/gfx/image/image.h" |
| 59 #include "ui/gfx/skbitmap_operations.h" | |
| 60 | 59 |
| 61 using content::WebContents; | 60 using content::WebContents; |
| 62 | 61 |
| 63 namespace { | 62 namespace { |
| 64 | 63 |
| 65 // The space above the titlebars. | 64 // The space above the titlebars. |
| 66 const int kTitlebarHeight = 14; | 65 const int kTitlebarHeight = 14; |
| 67 | 66 |
| 68 // The thickness in pixels of the tab border. | 67 // The thickness in pixels of the tab border. |
| 69 const int kTabOuterThickness = 1; | 68 const int kTabOuterThickness = 1; |
| (...skipping 400 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 470 return titlebar_left_buttons_hbox_; | 469 return titlebar_left_buttons_hbox_; |
| 471 else if (!left_side && titlebar_right_buttons_hbox_) | 470 else if (!left_side && titlebar_right_buttons_hbox_) |
| 472 return titlebar_right_buttons_hbox_; | 471 return titlebar_right_buttons_hbox_; |
| 473 | 472 |
| 474 // We put the min/max/restore/close buttons in a vbox so they are top aligned | 473 // We put the min/max/restore/close buttons in a vbox so they are top aligned |
| 475 // (up to padding) and don't vertically stretch. | 474 // (up to padding) and don't vertically stretch. |
| 476 GtkWidget* vbox = left_side ? titlebar_left_buttons_vbox_ : | 475 GtkWidget* vbox = left_side ? titlebar_left_buttons_vbox_ : |
| 477 titlebar_right_buttons_vbox_; | 476 titlebar_right_buttons_vbox_; |
| 478 | 477 |
| 479 GtkWidget* top_padding = gtk_fixed_new(); | 478 GtkWidget* top_padding = gtk_fixed_new(); |
| 480 gtk_widget_set_size_request(top_padding, -1, GetButtonOuterPadding()); | 479 gtk_widget_set_size_request(top_padding, -1, kButtonOuterPadding); |
| 481 gtk_box_pack_start(GTK_BOX(vbox), top_padding, FALSE, FALSE, 0); | 480 gtk_box_pack_start(GTK_BOX(vbox), top_padding, FALSE, FALSE, 0); |
| 482 | 481 |
| 483 GtkWidget* buttons_hbox = gtk_hbox_new(FALSE, GetButtonSpacing()); | 482 GtkWidget* buttons_hbox = gtk_hbox_new(FALSE, kButtonSpacing); |
| 484 gtk_box_pack_start(GTK_BOX(vbox), buttons_hbox, FALSE, FALSE, 0); | 483 gtk_box_pack_start(GTK_BOX(vbox), buttons_hbox, FALSE, FALSE, 0); |
| 485 | 484 |
| 486 if (left_side) { | 485 if (left_side) { |
| 487 titlebar_left_buttons_hbox_ = buttons_hbox; | 486 titlebar_left_buttons_hbox_ = buttons_hbox; |
| 488 top_padding_left_ = top_padding; | 487 top_padding_left_ = top_padding; |
| 489 } else { | 488 } else { |
| 490 titlebar_right_buttons_hbox_ = buttons_hbox; | 489 titlebar_right_buttons_hbox_ = buttons_hbox; |
| 491 top_padding_right_ = top_padding; | 490 top_padding_right_ = top_padding; |
| 492 } | 491 } |
| 493 | 492 |
| 494 return buttons_hbox; | 493 return buttons_hbox; |
| 495 } | 494 } |
| 496 | 495 |
| 497 int BrowserTitlebar::GetButtonOuterPadding() const { | |
| 498 return kButtonOuterPadding; | |
| 499 } | |
| 500 | |
| 501 int BrowserTitlebar::GetButtonSpacing() const { | |
| 502 return kButtonSpacing; | |
| 503 } | |
| 504 | |
| 505 CustomDrawButton* BrowserTitlebar::CreateTitlebarButton( | 496 CustomDrawButton* BrowserTitlebar::CreateTitlebarButton( |
| 506 const std::string& button_name, bool left_side) { | 497 const std::string& button_name, bool left_side) { |
| 507 int normal_image_id; | 498 int normal_image_id; |
| 508 int pressed_image_id; | 499 int pressed_image_id; |
| 509 int hover_image_id; | 500 int hover_image_id; |
| 510 int tooltip_id; | 501 int tooltip_id; |
| 511 GetButtonResources(button_name, &normal_image_id, &pressed_image_id, | 502 GetButtonResources(button_name, &normal_image_id, &pressed_image_id, |
| 512 &hover_image_id, &tooltip_id); | 503 &hover_image_id, &tooltip_id); |
| 513 | 504 |
| 514 CustomDrawButton* button = new CustomDrawButton(normal_image_id, | 505 CustomDrawButton* button = new CustomDrawButton(normal_image_id, |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 700 gtk_widget_hide(titlebar_alignment_); | 691 gtk_widget_hide(titlebar_alignment_); |
| 701 } | 692 } |
| 702 } | 693 } |
| 703 | 694 |
| 704 // Resize the buttons so that the clickable area extends all the way to the | 695 // Resize the buttons so that the clickable area extends all the way to the |
| 705 // edge of the browser window. | 696 // edge of the browser window. |
| 706 GtkRequisition close_button_req = close_button_req_; | 697 GtkRequisition close_button_req = close_button_req_; |
| 707 GtkRequisition minimize_button_req = minimize_button_req_; | 698 GtkRequisition minimize_button_req = minimize_button_req_; |
| 708 GtkRequisition restore_button_req = restore_button_req_; | 699 GtkRequisition restore_button_req = restore_button_req_; |
| 709 if (using_custom_frame_ && browser_window_->IsMaximized()) { | 700 if (using_custom_frame_ && browser_window_->IsMaximized()) { |
| 710 close_button_req.width += GetButtonOuterPadding(); | 701 close_button_req.width += kButtonOuterPadding; |
| 711 close_button_req.height += GetButtonOuterPadding(); | 702 close_button_req.height += kButtonOuterPadding; |
| 712 minimize_button_req.height += GetButtonOuterPadding(); | 703 minimize_button_req.height += kButtonOuterPadding; |
| 713 restore_button_req.height += GetButtonOuterPadding(); | 704 restore_button_req.height += kButtonOuterPadding; |
| 714 if (top_padding_left_) | 705 if (top_padding_left_) |
| 715 gtk_widget_hide(top_padding_left_); | 706 gtk_widget_hide(top_padding_left_); |
| 716 if (top_padding_right_) | 707 if (top_padding_right_) |
| 717 gtk_widget_hide(top_padding_right_); | 708 gtk_widget_hide(top_padding_right_); |
| 718 } else { | 709 } else { |
| 719 if (top_padding_left_) | 710 if (top_padding_left_) |
| 720 gtk_widget_show(top_padding_left_); | 711 gtk_widget_show(top_padding_left_); |
| 721 if (top_padding_right_) | 712 if (top_padding_right_) |
| 722 gtk_widget_show(top_padding_right_); | 713 gtk_widget_show(top_padding_right_); |
| 723 } | 714 } |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 830 } | 821 } |
| 831 avatar_button_->SetIcon(avatar, is_gaia_picture); | 822 avatar_button_->SetIcon(avatar, is_gaia_picture); |
| 832 | 823 |
| 833 BubbleGtk::ArrowLocationGtk arrow_location = | 824 BubbleGtk::ArrowLocationGtk arrow_location = |
| 834 display_avatar_on_left_ ^ base::i18n::IsRTL() ? | 825 display_avatar_on_left_ ^ base::i18n::IsRTL() ? |
| 835 BubbleGtk::ARROW_LOCATION_TOP_LEFT : | 826 BubbleGtk::ARROW_LOCATION_TOP_LEFT : |
| 836 BubbleGtk::ARROW_LOCATION_TOP_RIGHT; | 827 BubbleGtk::ARROW_LOCATION_TOP_RIGHT; |
| 837 avatar_button_->set_menu_arrow_location(arrow_location); | 828 avatar_button_->set_menu_arrow_location(arrow_location); |
| 838 } | 829 } |
| 839 | 830 |
| 840 void BrowserTitlebar::ShowFaviconMenu(GdkEventButton* event) { | |
| 841 if (!favicon_menu_model_.get()) { | |
| 842 favicon_menu_model_.reset( | |
| 843 new PopupPageMenuModel(this, browser_window_->browser())); | |
| 844 | |
| 845 favicon_menu_.reset(new MenuGtk(NULL, favicon_menu_model_.get())); | |
| 846 } | |
| 847 | |
| 848 favicon_menu_->PopupForWidget(app_mode_favicon_, event->button, event->time); | |
| 849 } | |
| 850 | |
| 851 void BrowserTitlebar::MaximizeButtonClicked() { | 831 void BrowserTitlebar::MaximizeButtonClicked() { |
| 852 GdkEvent* event = gtk_get_current_event(); | 832 GdkEvent* event = gtk_get_current_event(); |
| 853 if (event->button.button == 1) { | 833 if (event->button.button == 1) { |
| 854 gtk_window_maximize(window_); | 834 gtk_window_maximize(window_); |
| 855 } else { | 835 } else { |
| 856 GtkWidget* widget = GTK_WIDGET(window_); | 836 GtkWidget* widget = GTK_WIDGET(window_); |
| 857 GdkScreen* screen = gtk_widget_get_screen(widget); | 837 GdkScreen* screen = gtk_widget_get_screen(widget); |
| 858 gint monitor = gdk_screen_get_monitor_at_window( | 838 gint monitor = gdk_screen_get_monitor_at_window( |
| 859 screen, gtk_widget_get_window(widget)); | 839 screen, gtk_widget_get_window(widget)); |
| 860 GdkRectangle screen_rect; | 840 GdkRectangle screen_rect; |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 908 event->direction == GDK_SCROLL_UP) { | 888 event->direction == GDK_SCROLL_UP) { |
| 909 if (index != 0) | 889 if (index != 0) |
| 910 browser->SelectPreviousTab(); | 890 browser->SelectPreviousTab(); |
| 911 } else if (index + 1 < browser->tab_count()) { | 891 } else if (index + 1 < browser->tab_count()) { |
| 912 browser->SelectNextTab(); | 892 browser->SelectNextTab(); |
| 913 } | 893 } |
| 914 return TRUE; | 894 return TRUE; |
| 915 } | 895 } |
| 916 | 896 |
| 917 void BrowserTitlebar::OnButtonClicked(GtkWidget* button) { | 897 void BrowserTitlebar::OnButtonClicked(GtkWidget* button) { |
| 918 HandleButtonClick(button); | |
| 919 } | |
| 920 | |
| 921 void BrowserTitlebar::HandleButtonClick(GtkWidget* button) { | |
| 922 if (close_button_.get() && close_button_->widget() == button) { | 898 if (close_button_.get() && close_button_->widget() == button) { |
| 923 browser_window_->Close(); | 899 browser_window_->Close(); |
| 924 } else if (restore_button_.get() && restore_button_->widget() == button) { | 900 } else if (restore_button_.get() && restore_button_->widget() == button) { |
| 925 browser_window_->UnMaximize(); | 901 browser_window_->UnMaximize(); |
| 926 } else if (maximize_button_.get() && maximize_button_->widget() == button) { | 902 } else if (maximize_button_.get() && maximize_button_->widget() == button) { |
| 927 MaximizeButtonClicked(); | 903 MaximizeButtonClicked(); |
| 928 } else if (minimize_button_.get() && minimize_button_->widget() == button) { | 904 } else if (minimize_button_.get() && minimize_button_->widget() == button) { |
| 929 gtk_window_iconify(window_); | 905 gtk_window_iconify(window_); |
| 930 } | 906 } |
| 931 } | 907 } |
| 932 | 908 |
| 933 gboolean BrowserTitlebar::OnFaviconMenuButtonPressed(GtkWidget* widget, | 909 gboolean BrowserTitlebar::OnFaviconMenuButtonPressed(GtkWidget* widget, |
| 934 GdkEventButton* event) { | 910 GdkEventButton* event) { |
| 935 if (event->button != 1) | 911 if (event->button != 1) |
| 936 return FALSE; | 912 return FALSE; |
| 937 | 913 |
| 938 ShowFaviconMenu(event); | 914 if (!favicon_menu_model_.get()) { |
| 915 favicon_menu_model_.reset( |
| 916 new PopupPageMenuModel(this, browser_window_->browser())); |
| 917 |
| 918 favicon_menu_.reset(new MenuGtk(NULL, favicon_menu_model_.get())); |
| 919 } |
| 920 |
| 921 favicon_menu_->PopupForWidget(app_mode_favicon_, event->button, event->time); |
| 922 |
| 939 return TRUE; | 923 return TRUE; |
| 940 } | 924 } |
| 941 | 925 |
| 942 void BrowserTitlebar::ShowContextMenu(GdkEventButton* event) { | 926 void BrowserTitlebar::ShowContextMenu(GdkEventButton* event) { |
| 943 if (!context_menu_.get()) { | 927 if (!context_menu_.get()) { |
| 944 context_menu_model_.reset(new ContextMenuModel(this)); | 928 context_menu_model_.reset(new ContextMenuModel(this)); |
| 945 context_menu_.reset(new MenuGtk(NULL, context_menu_model_.get())); | 929 context_menu_.reset(new MenuGtk(NULL, context_menu_model_.get())); |
| 946 } | 930 } |
| 947 | 931 |
| 948 context_menu_->PopupAsContext(gfx::Point(event->x_root, event->y_root), | 932 context_menu_->PopupAsContext(gfx::Point(event->x_root, event->y_root), |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1046 if (!browser_window_->browser()->is_type_tabbed()) | 1030 if (!browser_window_->browser()->is_type_tabbed()) |
| 1047 return false; | 1031 return false; |
| 1048 | 1032 |
| 1049 return AvatarMenuModel::ShouldShowAvatarMenu(); | 1033 return AvatarMenuModel::ShouldShowAvatarMenu(); |
| 1050 } | 1034 } |
| 1051 | 1035 |
| 1052 bool BrowserTitlebar::IsOffTheRecord() { | 1036 bool BrowserTitlebar::IsOffTheRecord() { |
| 1053 return browser_window_->browser()->profile()->IsOffTheRecord(); | 1037 return browser_window_->browser()->profile()->IsOffTheRecord(); |
| 1054 } | 1038 } |
| 1055 | 1039 |
| 1056 /////////////////////////////////////////////////////////////////////////////// | 1040 GtkWidget* BrowserTitlebar::widget() const { |
| 1057 // BrowserTitlebar::Throbber implementation | 1041 return container_; |
| 1058 // TODO(tc): Handle anti-clockwise spinning when waiting for a connection. | |
| 1059 | |
| 1060 // We don't bother to clean up these or the pixbufs they contain when we exit. | |
| 1061 static std::vector<GdkPixbuf*>* g_throbber_frames = NULL; | |
| 1062 static std::vector<GdkPixbuf*>* g_throbber_waiting_frames = NULL; | |
| 1063 | |
| 1064 // Load |resource_id| from the ResourceBundle and split it into a series of | |
| 1065 // square GdkPixbufs that get stored in |frames|. | |
| 1066 static void MakeThrobberFrames(int resource_id, | |
| 1067 std::vector<GdkPixbuf*>* frames) { | |
| 1068 ui::ResourceBundle &rb = ui::ResourceBundle::GetSharedInstance(); | |
| 1069 SkBitmap* frame_strip = rb.GetBitmapNamed(resource_id); | |
| 1070 | |
| 1071 // Each frame of the animation is a square, so we use the height as the | |
| 1072 // frame size. | |
| 1073 int frame_size = frame_strip->height(); | |
| 1074 size_t num_frames = frame_strip->width() / frame_size; | |
| 1075 | |
| 1076 // Make a separate GdkPixbuf for each frame of the animation. | |
| 1077 for (size_t i = 0; i < num_frames; ++i) { | |
| 1078 SkBitmap frame = SkBitmapOperations::CreateTiledBitmap(*frame_strip, | |
| 1079 i * frame_size, 0, frame_size, frame_size); | |
| 1080 frames->push_back(gfx::GdkPixbufFromSkBitmap(frame)); | |
| 1081 } | |
| 1082 } | 1042 } |
| 1083 | 1043 |
| 1084 GdkPixbuf* BrowserTitlebar::Throbber::GetNextFrame(bool is_waiting) { | 1044 void BrowserTitlebar::set_window(GtkWindow* window) { |
| 1085 Throbber::InitFrames(); | 1045 window_ = window; |
| 1086 if (is_waiting) { | |
| 1087 return (*g_throbber_waiting_frames)[current_waiting_frame_++ % | |
| 1088 g_throbber_waiting_frames->size()]; | |
| 1089 } else { | |
| 1090 return (*g_throbber_frames)[current_frame_++ % g_throbber_frames->size()]; | |
| 1091 } | |
| 1092 } | 1046 } |
| 1093 | 1047 |
| 1094 void BrowserTitlebar::Throbber::Reset() { | 1048 AvatarMenuButtonGtk* BrowserTitlebar::avatar_button() const { |
| 1095 current_frame_ = 0; | 1049 return avatar_button_.get(); |
| 1096 current_waiting_frame_ = 0; | |
| 1097 } | |
| 1098 | |
| 1099 // static | |
| 1100 void BrowserTitlebar::Throbber::InitFrames() { | |
| 1101 if (g_throbber_frames) | |
| 1102 return; | |
| 1103 | |
| 1104 // We load the light version of the throbber since it'll be in the titlebar. | |
| 1105 g_throbber_frames = new std::vector<GdkPixbuf*>; | |
| 1106 MakeThrobberFrames(IDR_THROBBER_LIGHT, g_throbber_frames); | |
| 1107 | |
| 1108 g_throbber_waiting_frames = new std::vector<GdkPixbuf*>; | |
| 1109 MakeThrobberFrames(IDR_THROBBER_WAITING_LIGHT, g_throbber_waiting_frames); | |
| 1110 } | 1050 } |
| 1111 | 1051 |
| 1112 BrowserTitlebar::ContextMenuModel::ContextMenuModel( | 1052 BrowserTitlebar::ContextMenuModel::ContextMenuModel( |
| 1113 ui::SimpleMenuModel::Delegate* delegate) | 1053 ui::SimpleMenuModel::Delegate* delegate) |
| 1114 : SimpleMenuModel(delegate) { | 1054 : SimpleMenuModel(delegate) { |
| 1115 AddItemWithStringId(IDC_NEW_TAB, IDS_TAB_CXMENU_NEWTAB); | 1055 AddItemWithStringId(IDC_NEW_TAB, IDS_TAB_CXMENU_NEWTAB); |
| 1116 AddItemWithStringId(IDC_RESTORE_TAB, IDS_RESTORE_TAB); | 1056 AddItemWithStringId(IDC_RESTORE_TAB, IDS_RESTORE_TAB); |
| 1117 AddSeparator(); | 1057 AddSeparator(); |
| 1118 AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER); | 1058 AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER); |
| 1119 AddSeparator(); | 1059 AddSeparator(); |
| 1120 AddCheckItemWithStringId(kShowWindowDecorationsCommand, | 1060 AddCheckItemWithStringId(kShowWindowDecorationsCommand, |
| 1121 IDS_SHOW_WINDOW_DECORATIONS_MENU); | 1061 IDS_SHOW_WINDOW_DECORATIONS_MENU); |
| 1122 } | 1062 } |
| OLD | NEW |