| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/ui/gtk/browser_titlebar.h" | |
| 6 | |
| 7 #include <gdk/gdkkeysyms.h> | |
| 8 #include <gtk/gtk.h> | |
| 9 | |
| 10 #include <string> | |
| 11 #include <vector> | |
| 12 | |
| 13 #include "base/command_line.h" | |
| 14 #include "base/i18n/rtl.h" | |
| 15 #include "base/memory/singleton.h" | |
| 16 #include "base/prefs/pref_service.h" | |
| 17 #include "base/strings/string_piece.h" | |
| 18 #include "base/strings/string_tokenizer.h" | |
| 19 #include "base/strings/utf_string_conversions.h" | |
| 20 #include "chrome/app/chrome_command_ids.h" | |
| 21 #include "chrome/browser/browser_process.h" | |
| 22 #include "chrome/browser/chrome_notification_types.h" | |
| 23 #include "chrome/browser/profiles/avatar_menu.h" | |
| 24 #include "chrome/browser/profiles/profile.h" | |
| 25 #include "chrome/browser/profiles/profile_info_cache.h" | |
| 26 #include "chrome/browser/profiles/profile_manager.h" | |
| 27 #include "chrome/browser/themes/theme_properties.h" | |
| 28 #include "chrome/browser/ui/browser.h" | |
| 29 #include "chrome/browser/ui/browser_commands.h" | |
| 30 #include "chrome/browser/ui/gtk/accelerators_gtk.h" | |
| 31 #include "chrome/browser/ui/gtk/avatar_menu_bubble_gtk.h" | |
| 32 #include "chrome/browser/ui/gtk/avatar_menu_button_gtk.h" | |
| 33 #include "chrome/browser/ui/gtk/browser_window_gtk.h" | |
| 34 #include "chrome/browser/ui/gtk/custom_button.h" | |
| 35 #if defined(USE_GCONF) | |
| 36 #include "chrome/browser/ui/gtk/gconf_titlebar_listener.h" | |
| 37 #endif | |
| 38 #include "chrome/browser/ui/gtk/gtk_theme_service.h" | |
| 39 #include "chrome/browser/ui/gtk/gtk_util.h" | |
| 40 #include "chrome/browser/ui/gtk/menu_gtk.h" | |
| 41 #include "chrome/browser/ui/gtk/nine_box.h" | |
| 42 #include "chrome/browser/ui/gtk/tabs/tab_strip_gtk.h" | |
| 43 #include "chrome/browser/ui/gtk/unity_service.h" | |
| 44 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
| 45 #include "chrome/browser/ui/toolbar/encoding_menu_controller.h" | |
| 46 #include "chrome/browser/ui/toolbar/wrench_menu_model.h" | |
| 47 #include "chrome/common/chrome_switches.h" | |
| 48 #include "chrome/common/pref_names.h" | |
| 49 #include "content/public/browser/notification_service.h" | |
| 50 #include "content/public/browser/web_contents.h" | |
| 51 #include "grit/generated_resources.h" | |
| 52 #include "grit/theme_resources.h" | |
| 53 #include "grit/ui_resources.h" | |
| 54 #include "ui/base/gtk/gtk_hig_constants.h" | |
| 55 #include "ui/base/l10n/l10n_util.h" | |
| 56 #include "ui/base/resource/resource_bundle.h" | |
| 57 #include "ui/base/x/active_window_watcher_x.h" | |
| 58 #include "ui/gfx/image/image.h" | |
| 59 | |
| 60 using content::WebContents; | |
| 61 | |
| 62 namespace { | |
| 63 | |
| 64 // The space above the titlebars. | |
| 65 const int kTitlebarHeight = 14; | |
| 66 | |
| 67 // The thickness in pixels of the tab border. | |
| 68 const int kTabOuterThickness = 1; | |
| 69 | |
| 70 // Amount to offset the tab images relative to the background. | |
| 71 const int kNormalVerticalOffset = kTitlebarHeight + kTabOuterThickness; | |
| 72 | |
| 73 // A linux specific menu item for toggling window decorations. | |
| 74 const int kShowWindowDecorationsCommand = 200; | |
| 75 | |
| 76 const int kAvatarBottomSpacing = 1; | |
| 77 // There are 2 px on each side of the avatar (between the frame border and | |
| 78 // it on the left, and between it and the tabstrip on the right). | |
| 79 const int kAvatarSideSpacing = 2; | |
| 80 | |
| 81 // There is a 4px gap between the icon and the title text. | |
| 82 const int kIconTitleSpacing = 4; | |
| 83 | |
| 84 // Padding around the icon when in app mode or popup mode. | |
| 85 const int kAppModePaddingTop = 5; | |
| 86 const int kAppModePaddingBottom = 4; | |
| 87 const int kAppModePaddingLeft = 2; | |
| 88 | |
| 89 // The left padding of the tab strip. In Views, the tab strip has a left | |
| 90 // margin of FrameBorderThickness + kClientEdgeThickness. This offset is to | |
| 91 // account for kClientEdgeThickness. | |
| 92 const int kTabStripLeftPadding = 1; | |
| 93 | |
| 94 // Spacing between buttons of the titlebar. | |
| 95 const int kButtonSpacing = 2; | |
| 96 | |
| 97 // Spacing around outside of titlebar buttons. | |
| 98 const int kButtonOuterPadding = 2; | |
| 99 | |
| 100 // Spacing between tabstrip and window control buttons (when the window is | |
| 101 // maximized). | |
| 102 const int kMaximizedTabstripPadding = 16; | |
| 103 | |
| 104 gboolean OnMouseMoveEvent(GtkWidget* widget, GdkEventMotion* event, | |
| 105 BrowserWindowGtk* browser_window) { | |
| 106 // Reset to the default mouse cursor. | |
| 107 browser_window->ResetCustomFrameCursor(); | |
| 108 return TRUE; | |
| 109 } | |
| 110 | |
| 111 // Converts a GdkColor to a color_utils::HSL. | |
| 112 color_utils::HSL GdkColorToHSL(const GdkColor* color) { | |
| 113 color_utils::HSL hsl; | |
| 114 color_utils::SkColorToHSL(SkColorSetRGB(color->red >> 8, | |
| 115 color->green >> 8, | |
| 116 color->blue >> 8), &hsl); | |
| 117 return hsl; | |
| 118 } | |
| 119 | |
| 120 // Returns either |one| or |two| based on which has a greater difference in | |
| 121 // luminosity. | |
| 122 GdkColor PickLuminosityContrastingColor(const GdkColor* base, | |
| 123 const GdkColor* one, | |
| 124 const GdkColor* two) { | |
| 125 // Convert all GdkColors to color_utils::HSLs. | |
| 126 color_utils::HSL baseHSL = GdkColorToHSL(base); | |
| 127 color_utils::HSL oneHSL = GdkColorToHSL(one); | |
| 128 color_utils::HSL twoHSL = GdkColorToHSL(two); | |
| 129 double one_difference = fabs(baseHSL.l - oneHSL.l); | |
| 130 double two_difference = fabs(baseHSL.l - twoHSL.l); | |
| 131 | |
| 132 // Be biased towards the first color presented. | |
| 133 if (two_difference > one_difference + 0.1) | |
| 134 return *two; | |
| 135 else | |
| 136 return *one; | |
| 137 } | |
| 138 | |
| 139 } // namespace | |
| 140 | |
| 141 //////////////////////////////////////////////////////////////////////////////// | |
| 142 // PopupPageMenuModel | |
| 143 | |
| 144 // A menu model that builds the contents of the menu shown for popups (when the | |
| 145 // user clicks on the favicon) and all of its submenus. | |
| 146 class PopupPageMenuModel : public ui::SimpleMenuModel { | |
| 147 public: | |
| 148 PopupPageMenuModel(ui::SimpleMenuModel::Delegate* delegate, Browser* browser); | |
| 149 virtual ~PopupPageMenuModel() { } | |
| 150 | |
| 151 private: | |
| 152 void Build(); | |
| 153 | |
| 154 // Models for submenus referenced by this model. SimpleMenuModel only uses | |
| 155 // weak references so these must be kept for the lifetime of the top-level | |
| 156 // model. | |
| 157 scoped_ptr<ZoomMenuModel> zoom_menu_model_; | |
| 158 scoped_ptr<EncodingMenuModel> encoding_menu_model_; | |
| 159 Browser* browser_; // weak | |
| 160 | |
| 161 DISALLOW_COPY_AND_ASSIGN(PopupPageMenuModel); | |
| 162 }; | |
| 163 | |
| 164 PopupPageMenuModel::PopupPageMenuModel( | |
| 165 ui::SimpleMenuModel::Delegate* delegate, | |
| 166 Browser* browser) | |
| 167 : ui::SimpleMenuModel(delegate), browser_(browser) { | |
| 168 Build(); | |
| 169 } | |
| 170 | |
| 171 void PopupPageMenuModel::Build() { | |
| 172 AddItemWithStringId(IDC_BACK, IDS_CONTENT_CONTEXT_BACK); | |
| 173 AddItemWithStringId(IDC_FORWARD, IDS_CONTENT_CONTEXT_FORWARD); | |
| 174 AddItemWithStringId(IDC_RELOAD, IDS_APP_MENU_RELOAD); | |
| 175 AddSeparator(ui::NORMAL_SEPARATOR); | |
| 176 AddItemWithStringId(IDC_SHOW_AS_TAB, IDS_SHOW_AS_TAB); | |
| 177 AddSeparator(ui::NORMAL_SEPARATOR); | |
| 178 AddItemWithStringId(IDC_CUT, IDS_CUT); | |
| 179 AddItemWithStringId(IDC_COPY, IDS_COPY); | |
| 180 AddItemWithStringId(IDC_PASTE, IDS_PASTE); | |
| 181 AddSeparator(ui::NORMAL_SEPARATOR); | |
| 182 AddItemWithStringId(IDC_FIND, IDS_FIND); | |
| 183 AddItemWithStringId(IDC_PRINT, IDS_PRINT); | |
| 184 zoom_menu_model_.reset(new ZoomMenuModel(delegate())); | |
| 185 AddSubMenuWithStringId(IDC_ZOOM_MENU, IDS_ZOOM_MENU, zoom_menu_model_.get()); | |
| 186 | |
| 187 encoding_menu_model_.reset(new EncodingMenuModel(browser_)); | |
| 188 AddSubMenuWithStringId(IDC_ENCODING_MENU, IDS_ENCODING_MENU, | |
| 189 encoding_menu_model_.get()); | |
| 190 | |
| 191 AddSeparator(ui::NORMAL_SEPARATOR); | |
| 192 AddItemWithStringId(IDC_CLOSE_WINDOW, IDS_CLOSE); | |
| 193 } | |
| 194 | |
| 195 //////////////////////////////////////////////////////////////////////////////// | |
| 196 // BrowserTitlebar | |
| 197 | |
| 198 // static | |
| 199 const char BrowserTitlebar::kDefaultButtonString[] = ":minimize,maximize,close"; | |
| 200 | |
| 201 BrowserTitlebar::BrowserTitlebar(BrowserWindowGtk* browser_window, | |
| 202 GtkWindow* window) | |
| 203 : browser_window_(browser_window), | |
| 204 window_(window), | |
| 205 container_(NULL), | |
| 206 container_hbox_(NULL), | |
| 207 titlebar_left_buttons_vbox_(NULL), | |
| 208 titlebar_right_buttons_vbox_(NULL), | |
| 209 titlebar_left_buttons_hbox_(NULL), | |
| 210 titlebar_right_buttons_hbox_(NULL), | |
| 211 titlebar_left_avatar_frame_(NULL), | |
| 212 titlebar_right_avatar_frame_(NULL), | |
| 213 titlebar_left_label_frame_(NULL), | |
| 214 titlebar_right_label_frame_(NULL), | |
| 215 avatar_(NULL), | |
| 216 avatar_label_(NULL), | |
| 217 avatar_label_bg_(NULL), | |
| 218 top_padding_left_(NULL), | |
| 219 top_padding_right_(NULL), | |
| 220 titlebar_alignment_(NULL), | |
| 221 app_mode_favicon_(NULL), | |
| 222 app_mode_title_(NULL), | |
| 223 using_custom_frame_(false), | |
| 224 window_has_focus_(false), | |
| 225 display_avatar_on_left_(false), | |
| 226 theme_service_(NULL) { | |
| 227 } | |
| 228 | |
| 229 void BrowserTitlebar::Init() { | |
| 230 // The widget hierarchy is shown below. | |
| 231 // | |
| 232 // +- EventBox (container_) ------------------------------------------------+ | |
| 233 // +- HBox (container_hbox_) -----------------------------------------------+ | |
| 234 // |+ VBox ---++- Algn. -++- Alignment --------------++- Algn. -++ VBox ---+| | |
| 235 // || titlebar||titlebar || (titlebar_alignment_) ||titlebar || titlebar|| | |
| 236 // || left ||left || ||right || right || | |
| 237 // || button ||spy || ||spy || button || | |
| 238 // || vbox ||frame ||+- TabStripGtk ---------+||frame || vbox || | |
| 239 // || || || tab tab tabclose ||| || || | |
| 240 // || || ||+------------------------+|| || || | |
| 241 // |+---------++---------++--------------------------++---------++---------+| | |
| 242 // +------------------------------------------------------------------------+ | |
| 243 // | |
| 244 // There are two vboxes on either side of |container_hbox_| because when the | |
| 245 // desktop is GNOME, the button placement is configurable based on a metacity | |
| 246 // gconf key. We can't just have one vbox and move it back and forth because | |
| 247 // the gconf language allows you to place buttons on both sides of the | |
| 248 // window. This being Linux, I'm sure there's a bunch of people who have | |
| 249 // already configured their window manager to do so and we don't want to get | |
| 250 // confused when that happens. The actual contents of these vboxes are lazily | |
| 251 // generated so they don't interfere with alignment when everything is | |
| 252 // stuffed in the other box. | |
| 253 // | |
| 254 // Each vbox has the following hierarchy if it contains buttons: | |
| 255 // | |
| 256 // +- VBox (titlebar_{l,r}_buttons_vbox_) ---------+ | |
| 257 // |+- Fixed (top_padding_{l,r}_) ----------------+| | |
| 258 // ||+- HBox (titlebar_{l,r}_buttons_hbox_ ------+|| | |
| 259 // ||| (buttons of a configurable layout) ||| | |
| 260 // ||+-------------------------------------------+|| | |
| 261 // |+---------------------------------------------+| | |
| 262 // +-----------------------------------------------+ | |
| 263 // | |
| 264 // The two spy alignments are only allocated if this window is an incognito | |
| 265 // window. Only one of them holds the spy image. | |
| 266 // | |
| 267 // If we're a popup window or in app mode, we don't display the spy guy or | |
| 268 // the tab strip. Instead, put an hbox in titlebar_alignment_ in place of | |
| 269 // the tab strip. | |
| 270 // +- Alignment (titlebar_alignment_) -----------------------------------+ | |
| 271 // |+ HBox -------------------------------------------------------------+| | |
| 272 // ||+- TabStripGtk -++- Image ----------------++- Label --------------+|| | |
| 273 // ||| hidden ++ (app_mode_favicon_) || (app_mode_title_) ||| | |
| 274 // ||| || favicon || page title ||| | |
| 275 // ||+---------------++------------------------++----------------------+|| | |
| 276 // |+-------------------------------------------------------------------+| | |
| 277 // +---------------------------------------------------------------------+ | |
| 278 container_hbox_ = gtk_hbox_new(FALSE, 0); | |
| 279 | |
| 280 container_ = gtk_event_box_new(); | |
| 281 gtk_widget_set_name(container_, "chrome-browser-titlebar"); | |
| 282 gtk_event_box_set_visible_window(GTK_EVENT_BOX(container_), FALSE); | |
| 283 gtk_container_add(GTK_CONTAINER(container_), container_hbox_); | |
| 284 | |
| 285 g_signal_connect(container_, "scroll-event", G_CALLBACK(OnScrollThunk), this); | |
| 286 | |
| 287 g_signal_connect(window_, "window-state-event", | |
| 288 G_CALLBACK(OnWindowStateChangedThunk), this); | |
| 289 | |
| 290 // Allocate the two button boxes on the left and right parts of the bar. These | |
| 291 // are always allocated, but only displayed in incognito mode or when using | |
| 292 // multiple profiles. | |
| 293 titlebar_left_buttons_vbox_ = gtk_vbox_new(FALSE, 0); | |
| 294 gtk_box_pack_start(GTK_BOX(container_hbox_), titlebar_left_buttons_vbox_, | |
| 295 FALSE, FALSE, 0); | |
| 296 titlebar_left_avatar_frame_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); | |
| 297 gtk_widget_set_no_show_all(titlebar_left_avatar_frame_, TRUE); | |
| 298 gtk_alignment_set_padding(GTK_ALIGNMENT(titlebar_left_avatar_frame_), 0, | |
| 299 kAvatarBottomSpacing, kAvatarSideSpacing, kAvatarSideSpacing); | |
| 300 gtk_box_pack_start(GTK_BOX(container_hbox_), titlebar_left_avatar_frame_, | |
| 301 FALSE, FALSE, 0); | |
| 302 | |
| 303 titlebar_left_label_frame_ = gtk_alignment_new(0.0, 0.5, 0.0, 0.0); | |
| 304 gtk_widget_set_no_show_all(titlebar_left_label_frame_, TRUE); | |
| 305 gtk_alignment_set_padding(GTK_ALIGNMENT(titlebar_left_label_frame_), 0, | |
| 306 kAvatarBottomSpacing, kAvatarSideSpacing, kAvatarSideSpacing); | |
| 307 gtk_box_pack_start(GTK_BOX(container_hbox_), titlebar_left_label_frame_, | |
| 308 FALSE, FALSE, 0); | |
| 309 | |
| 310 titlebar_right_avatar_frame_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); | |
| 311 gtk_widget_set_no_show_all(titlebar_right_avatar_frame_, TRUE); | |
| 312 gtk_alignment_set_padding(GTK_ALIGNMENT(titlebar_right_avatar_frame_), 0, | |
| 313 kAvatarBottomSpacing, kAvatarSideSpacing, kAvatarSideSpacing); | |
| 314 gtk_box_pack_end(GTK_BOX(container_hbox_), titlebar_right_avatar_frame_, | |
| 315 FALSE, FALSE, 0); | |
| 316 | |
| 317 titlebar_right_label_frame_ = gtk_alignment_new(0.0, 0.5, 0.0, 0.0); | |
| 318 gtk_widget_set_no_show_all(titlebar_right_label_frame_, TRUE); | |
| 319 gtk_alignment_set_padding(GTK_ALIGNMENT(titlebar_right_label_frame_), 0, | |
| 320 kAvatarBottomSpacing, kAvatarSideSpacing, kAvatarSideSpacing); | |
| 321 gtk_box_pack_end(GTK_BOX(container_hbox_), titlebar_right_label_frame_, | |
| 322 FALSE, FALSE, 0); | |
| 323 | |
| 324 titlebar_right_buttons_vbox_ = gtk_vbox_new(FALSE, 0); | |
| 325 gtk_box_pack_end(GTK_BOX(container_hbox_), titlebar_right_buttons_vbox_, | |
| 326 FALSE, FALSE, 0); | |
| 327 | |
| 328 // Create the Avatar button and listen for notifications. It must always be | |
| 329 // created because a new profile can be added at any time. | |
| 330 avatar_button_.reset(new AvatarMenuButtonGtk(browser_window_->browser())); | |
| 331 | |
| 332 registrar_.Add(this, chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, | |
| 333 content::NotificationService::AllSources()); | |
| 334 | |
| 335 #if defined(USE_GCONF) | |
| 336 // Either read the gconf database and register for updates (on GNOME), or use | |
| 337 // the default value (anywhere else). | |
| 338 GConfTitlebarListener::GetInstance()->SetTitlebarButtons(this); | |
| 339 #else | |
| 340 BuildButtons(kDefaultButtonString); | |
| 341 #endif | |
| 342 | |
| 343 UpdateAvatar(); | |
| 344 | |
| 345 // We use an alignment to control the titlebar height. | |
| 346 titlebar_alignment_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); | |
| 347 if (browser_window_->browser()->is_type_tabbed()) { | |
| 348 gtk_box_pack_start(GTK_BOX(container_hbox_), titlebar_alignment_, TRUE, | |
| 349 TRUE, 0); | |
| 350 | |
| 351 // Put the tab strip in the titlebar. | |
| 352 gtk_container_add(GTK_CONTAINER(titlebar_alignment_), | |
| 353 browser_window_->tabstrip()->widget()); | |
| 354 } else { | |
| 355 // App mode specific widgets. | |
| 356 gtk_box_pack_start(GTK_BOX(container_hbox_), titlebar_alignment_, TRUE, | |
| 357 TRUE, 0); | |
| 358 GtkWidget* app_mode_hbox = gtk_hbox_new(FALSE, kIconTitleSpacing); | |
| 359 gtk_container_add(GTK_CONTAINER(titlebar_alignment_), app_mode_hbox); | |
| 360 | |
| 361 // Put the tab strip in the hbox even though we don't show it. Sometimes | |
| 362 // we need the position of the tab strip so make sure it's in our widget | |
| 363 // hierarchy. | |
| 364 gtk_box_pack_start(GTK_BOX(app_mode_hbox), | |
| 365 browser_window_->tabstrip()->widget(), FALSE, FALSE, 0); | |
| 366 | |
| 367 GtkWidget* favicon_event_box = gtk_event_box_new(); | |
| 368 gtk_event_box_set_visible_window(GTK_EVENT_BOX(favicon_event_box), FALSE); | |
| 369 g_signal_connect(favicon_event_box, "button-press-event", | |
| 370 G_CALLBACK(OnFaviconMenuButtonPressedThunk), this); | |
| 371 gtk_box_pack_start(GTK_BOX(app_mode_hbox), favicon_event_box, FALSE, | |
| 372 FALSE, 0); | |
| 373 // We use the app logo as a placeholder image so the title doesn't jump | |
| 374 // around. | |
| 375 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
| 376 app_mode_favicon_ = gtk_image_new_from_pixbuf(rb.GetNativeImageNamed( | |
| 377 IDR_PRODUCT_LOGO_16, ui::ResourceBundle::RTL_ENABLED).ToGdkPixbuf()); | |
| 378 g_object_set_data(G_OBJECT(app_mode_favicon_), "left-align-popup", | |
| 379 reinterpret_cast<void*>(true)); | |
| 380 gtk_container_add(GTK_CONTAINER(favicon_event_box), app_mode_favicon_); | |
| 381 | |
| 382 app_mode_title_ = gtk_label_new(NULL); | |
| 383 gtk_label_set_ellipsize(GTK_LABEL(app_mode_title_), PANGO_ELLIPSIZE_END); | |
| 384 gtk_misc_set_alignment(GTK_MISC(app_mode_title_), 0.0, 0.5); | |
| 385 gtk_box_pack_start(GTK_BOX(app_mode_hbox), app_mode_title_, TRUE, TRUE, | |
| 386 0); | |
| 387 | |
| 388 UpdateTitleAndIcon(); | |
| 389 } | |
| 390 | |
| 391 theme_service_ = GtkThemeService::GetFrom( | |
| 392 browser_window_->browser()->profile()); | |
| 393 registrar_.Add(this, chrome::NOTIFICATION_BROWSER_THEME_CHANGED, | |
| 394 content::Source<ThemeService>(theme_service_)); | |
| 395 theme_service_->InitThemesFor(this); | |
| 396 | |
| 397 gtk_widget_show_all(container_); | |
| 398 | |
| 399 ui::ActiveWindowWatcherX::AddObserver(this); | |
| 400 } | |
| 401 | |
| 402 BrowserTitlebar::~BrowserTitlebar() { | |
| 403 ui::ActiveWindowWatcherX::RemoveObserver(this); | |
| 404 #if defined(USE_GCONF) | |
| 405 GConfTitlebarListener::GetInstance()->RemoveObserver(this); | |
| 406 #endif | |
| 407 } | |
| 408 | |
| 409 void BrowserTitlebar::BuildButtons(const std::string& button_string) { | |
| 410 // Clear out all previous data. | |
| 411 close_button_.reset(); | |
| 412 restore_button_.reset(); | |
| 413 maximize_button_.reset(); | |
| 414 minimize_button_.reset(); | |
| 415 gtk_util::RemoveAllChildren(titlebar_left_buttons_vbox_); | |
| 416 gtk_util::RemoveAllChildren(titlebar_right_buttons_vbox_); | |
| 417 titlebar_left_buttons_hbox_ = NULL; | |
| 418 titlebar_right_buttons_hbox_ = NULL; | |
| 419 top_padding_left_ = NULL; | |
| 420 top_padding_right_ = NULL; | |
| 421 | |
| 422 bool left_side = true; | |
| 423 base::StringTokenizer tokenizer(button_string, ":,"); | |
| 424 tokenizer.set_options(base::StringTokenizer::RETURN_DELIMS); | |
| 425 int left_count = 0; | |
| 426 int right_count = 0; | |
| 427 while (tokenizer.GetNext()) { | |
| 428 if (tokenizer.token_is_delim()) { | |
| 429 if (*tokenizer.token_begin() == ':') | |
| 430 left_side = false; | |
| 431 } else { | |
| 432 base::StringPiece token = tokenizer.token_piece(); | |
| 433 if (BuildButton(token.as_string(), left_side)) | |
| 434 (left_side ? left_count : right_count)++; | |
| 435 } | |
| 436 } | |
| 437 | |
| 438 // If we are in incognito mode, add the spy guy to either the end of the left | |
| 439 // or the beginning of the right depending on which side has fewer buttons. | |
| 440 display_avatar_on_left_ = right_count > left_count; | |
| 441 | |
| 442 // Now show the correct widgets in the two hierarchies. | |
| 443 if (using_custom_frame_) { | |
| 444 gtk_widget_show_all(titlebar_left_buttons_vbox_); | |
| 445 gtk_widget_show_all(titlebar_right_buttons_vbox_); | |
| 446 } | |
| 447 UpdateMaximizeRestoreVisibility(); | |
| 448 } | |
| 449 | |
| 450 bool BrowserTitlebar::BuildButton(const std::string& button_token, | |
| 451 bool left_side) { | |
| 452 if (button_token == "minimize") { | |
| 453 minimize_button_.reset(CreateTitlebarButton("minimize", left_side)); | |
| 454 | |
| 455 gtk_widget_size_request(minimize_button_->widget(), | |
| 456 &minimize_button_req_); | |
| 457 return true; | |
| 458 } else if (button_token == "maximize") { | |
| 459 restore_button_.reset(CreateTitlebarButton("unmaximize", left_side)); | |
| 460 maximize_button_.reset(CreateTitlebarButton("maximize", left_side)); | |
| 461 | |
| 462 gtk_util::SetButtonClickableByMouseButtons(maximize_button_->widget(), | |
| 463 true, true, true); | |
| 464 gtk_widget_size_request(restore_button_->widget(), | |
| 465 &restore_button_req_); | |
| 466 return true; | |
| 467 } else if (button_token == "close") { | |
| 468 close_button_.reset(CreateTitlebarButton("close", left_side)); | |
| 469 close_button_->set_flipped(left_side); | |
| 470 | |
| 471 gtk_widget_size_request(close_button_->widget(), &close_button_req_); | |
| 472 return true; | |
| 473 } | |
| 474 // Ignore any other values like "pin" since we don't have images for | |
| 475 // those. | |
| 476 return false; | |
| 477 } | |
| 478 | |
| 479 GtkWidget* BrowserTitlebar::GetButtonHBox(bool left_side) { | |
| 480 if (left_side && titlebar_left_buttons_hbox_) | |
| 481 return titlebar_left_buttons_hbox_; | |
| 482 else if (!left_side && titlebar_right_buttons_hbox_) | |
| 483 return titlebar_right_buttons_hbox_; | |
| 484 | |
| 485 // We put the min/max/restore/close buttons in a vbox so they are top aligned | |
| 486 // (up to padding) and don't vertically stretch. | |
| 487 GtkWidget* vbox = left_side ? titlebar_left_buttons_vbox_ : | |
| 488 titlebar_right_buttons_vbox_; | |
| 489 | |
| 490 GtkWidget* top_padding = gtk_fixed_new(); | |
| 491 gtk_widget_set_size_request(top_padding, -1, kButtonOuterPadding); | |
| 492 gtk_box_pack_start(GTK_BOX(vbox), top_padding, FALSE, FALSE, 0); | |
| 493 | |
| 494 GtkWidget* buttons_hbox = gtk_hbox_new(FALSE, kButtonSpacing); | |
| 495 gtk_box_pack_start(GTK_BOX(vbox), buttons_hbox, FALSE, FALSE, 0); | |
| 496 | |
| 497 if (left_side) { | |
| 498 titlebar_left_buttons_hbox_ = buttons_hbox; | |
| 499 top_padding_left_ = top_padding; | |
| 500 } else { | |
| 501 titlebar_right_buttons_hbox_ = buttons_hbox; | |
| 502 top_padding_right_ = top_padding; | |
| 503 } | |
| 504 | |
| 505 return buttons_hbox; | |
| 506 } | |
| 507 | |
| 508 CustomDrawButton* BrowserTitlebar::CreateTitlebarButton( | |
| 509 const std::string& button_name, bool left_side) { | |
| 510 int normal_image_id; | |
| 511 int pressed_image_id; | |
| 512 int hover_image_id; | |
| 513 int tooltip_id; | |
| 514 GetButtonResources(button_name, &normal_image_id, &pressed_image_id, | |
| 515 &hover_image_id, &tooltip_id); | |
| 516 | |
| 517 CustomDrawButton* button = new CustomDrawButton(normal_image_id, | |
| 518 pressed_image_id, | |
| 519 hover_image_id, | |
| 520 0); | |
| 521 gtk_widget_add_events(GTK_WIDGET(button->widget()), GDK_POINTER_MOTION_MASK); | |
| 522 g_signal_connect(button->widget(), "clicked", | |
| 523 G_CALLBACK(OnButtonClickedThunk), this); | |
| 524 g_signal_connect(button->widget(), "motion-notify-event", | |
| 525 G_CALLBACK(OnMouseMoveEvent), browser_window_); | |
| 526 | |
| 527 std::string localized_tooltip = l10n_util::GetStringUTF8(tooltip_id); | |
| 528 gtk_widget_set_tooltip_text(button->widget(), | |
| 529 localized_tooltip.c_str()); | |
| 530 | |
| 531 GtkWidget* box = GetButtonHBox(left_side); | |
| 532 gtk_box_pack_start(GTK_BOX(box), button->widget(), FALSE, FALSE, 0); | |
| 533 return button; | |
| 534 } | |
| 535 | |
| 536 void BrowserTitlebar::GetButtonResources(const std::string& button_name, | |
| 537 int* normal_image_id, | |
| 538 int* pressed_image_id, | |
| 539 int* hover_image_id, | |
| 540 int* tooltip_id) const { | |
| 541 if (button_name == "close") { | |
| 542 *normal_image_id = IDR_CLOSE; | |
| 543 *pressed_image_id = IDR_CLOSE_P; | |
| 544 *hover_image_id = IDR_CLOSE_H; | |
| 545 *tooltip_id = IDS_XPFRAME_CLOSE_TOOLTIP; | |
| 546 } else if (button_name == "minimize") { | |
| 547 *normal_image_id = IDR_MINIMIZE; | |
| 548 *pressed_image_id = IDR_MINIMIZE_P; | |
| 549 *hover_image_id = IDR_MINIMIZE_H; | |
| 550 *tooltip_id = IDS_XPFRAME_MINIMIZE_TOOLTIP; | |
| 551 } else if (button_name == "maximize") { | |
| 552 *normal_image_id = IDR_MAXIMIZE; | |
| 553 *pressed_image_id = IDR_MAXIMIZE_P; | |
| 554 *hover_image_id = IDR_MAXIMIZE_H; | |
| 555 *tooltip_id = IDS_XPFRAME_MAXIMIZE_TOOLTIP; | |
| 556 } else if (button_name == "unmaximize") { | |
| 557 *normal_image_id = IDR_RESTORE; | |
| 558 *pressed_image_id = IDR_RESTORE_P; | |
| 559 *hover_image_id = IDR_RESTORE_H; | |
| 560 *tooltip_id = IDS_XPFRAME_RESTORE_TOOLTIP; | |
| 561 } | |
| 562 } | |
| 563 | |
| 564 void BrowserTitlebar::UpdateButtonBackground(CustomDrawButton* button) { | |
| 565 SkColor color = theme_service_->GetColor( | |
| 566 ThemeProperties::COLOR_BUTTON_BACKGROUND); | |
| 567 SkBitmap background = theme_service_->GetImageNamed( | |
| 568 IDR_THEME_WINDOW_CONTROL_BACKGROUND).AsBitmap(); | |
| 569 | |
| 570 // TODO(erg): For now, we just use a completely black mask and we can get | |
| 571 // away with this in the short term because our buttons are rectangles. We | |
| 572 // should get Glen to make properly hinted masks that match our min/max/close | |
| 573 // buttons (which have some odd alpha blending around the | |
| 574 // edges). http://crbug.com/103661 | |
| 575 SkBitmap mask; | |
| 576 mask.setConfig(SkBitmap::kARGB_8888_Config, | |
| 577 button->SurfaceWidth(), button->SurfaceHeight(), 0); | |
| 578 mask.allocPixels(); | |
| 579 mask.eraseColor(SK_ColorBLACK); | |
| 580 | |
| 581 button->SetBackground(color, background, mask); | |
| 582 } | |
| 583 | |
| 584 void BrowserTitlebar::UpdateCustomFrame(bool use_custom_frame) { | |
| 585 using_custom_frame_ = use_custom_frame; | |
| 586 if (!use_custom_frame || | |
| 587 (browser_window_->IsMaximized() && unity::IsRunning())) { | |
| 588 if (titlebar_left_buttons_vbox_) | |
| 589 gtk_widget_hide(titlebar_left_buttons_vbox_); | |
| 590 if (titlebar_right_buttons_vbox_) | |
| 591 gtk_widget_hide(titlebar_right_buttons_vbox_); | |
| 592 } else { | |
| 593 if (titlebar_left_buttons_vbox_) | |
| 594 gtk_widget_show_all(titlebar_left_buttons_vbox_); | |
| 595 if (titlebar_right_buttons_vbox_) | |
| 596 gtk_widget_show_all(titlebar_right_buttons_vbox_); | |
| 597 } | |
| 598 UpdateTitlebarAlignment(); | |
| 599 UpdateMaximizeRestoreVisibility(); | |
| 600 } | |
| 601 | |
| 602 void BrowserTitlebar::UpdateTitleAndIcon() { | |
| 603 if (!app_mode_title_) | |
| 604 return; | |
| 605 | |
| 606 // Get the page title and elide it to the available space. | |
| 607 base::string16 title = | |
| 608 browser_window_->browser()->GetWindowTitleForCurrentTab(); | |
| 609 gtk_label_set_text(GTK_LABEL(app_mode_title_), | |
| 610 base::UTF16ToUTF8(title).c_str()); | |
| 611 | |
| 612 if (browser_window_->browser()->is_app()) { | |
| 613 switch (browser_window_->browser()->type()) { | |
| 614 case Browser::TYPE_POPUP: { | |
| 615 // Update the system app icon. We don't need to update the icon in the | |
| 616 // top left of the custom frame, that will get updated when the | |
| 617 // throbber is updated. | |
| 618 Profile* profile = browser_window_->browser()->profile(); | |
| 619 gfx::Image icon = browser_window_->browser()->GetCurrentPageIcon(); | |
| 620 if (icon.IsEmpty()) { | |
| 621 gtk_util::SetWindowIcon(window_, profile); | |
| 622 } else { | |
| 623 gtk_util::SetWindowIcon(window_, profile, icon.ToGdkPixbuf()); | |
| 624 } | |
| 625 break; | |
| 626 } | |
| 627 case Browser::TYPE_TABBED: { | |
| 628 NOTREACHED() << "We should never have a tabbed app window."; | |
| 629 break; | |
| 630 } | |
| 631 } | |
| 632 } | |
| 633 } | |
| 634 | |
| 635 void BrowserTitlebar::UpdateThrobber(WebContents* web_contents) { | |
| 636 DCHECK(app_mode_favicon_); | |
| 637 | |
| 638 if (web_contents && web_contents->IsLoading()) { | |
| 639 GdkPixbuf* icon_pixbuf = | |
| 640 throbber_.GetNextFrame(web_contents->IsWaitingForResponse()); | |
| 641 gtk_image_set_from_pixbuf(GTK_IMAGE(app_mode_favicon_), icon_pixbuf); | |
| 642 } else { | |
| 643 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
| 644 | |
| 645 // Note: we want to exclude the application popup/panel window. | |
| 646 if ((browser_window_->browser()->is_app() && | |
| 647 !browser_window_->browser()->is_type_tabbed()) || | |
| 648 browser_window_->browser()->is_type_popup()) { | |
| 649 gfx::Image icon = browser_window_->browser()->GetCurrentPageIcon(); | |
| 650 if (icon.IsEmpty()) { | |
| 651 // Fallback to the Chromium icon if the page has no icon. | |
| 652 gtk_image_set_from_pixbuf(GTK_IMAGE(app_mode_favicon_), | |
| 653 rb.GetNativeImageNamed(IDR_PRODUCT_LOGO_16).ToGdkPixbuf()); | |
| 654 } else { | |
| 655 gtk_image_set_from_pixbuf(GTK_IMAGE(app_mode_favicon_), | |
| 656 icon.ToGdkPixbuf()); | |
| 657 } | |
| 658 } else { | |
| 659 gtk_image_set_from_pixbuf(GTK_IMAGE(app_mode_favicon_), | |
| 660 rb.GetNativeImageNamed(IDR_PRODUCT_LOGO_16).ToGdkPixbuf()); | |
| 661 } | |
| 662 throbber_.Reset(); | |
| 663 } | |
| 664 } | |
| 665 | |
| 666 void BrowserTitlebar::UpdateTitlebarAlignment() { | |
| 667 if (browser_window_->browser()->is_type_tabbed()) { | |
| 668 int top_padding = 0; | |
| 669 int side_padding = 0; | |
| 670 int vertical_offset = kNormalVerticalOffset; | |
| 671 | |
| 672 if (using_custom_frame_) { | |
| 673 if (!browser_window_->IsMaximized()) { | |
| 674 top_padding = kTitlebarHeight; | |
| 675 } else if (using_custom_frame_ && browser_window_->IsMaximized()) { | |
| 676 vertical_offset = 0; | |
| 677 if (!unity::IsRunning()) | |
| 678 side_padding = kMaximizedTabstripPadding; | |
| 679 } | |
| 680 } | |
| 681 | |
| 682 int right_padding = 0; | |
| 683 int left_padding = kTabStripLeftPadding; | |
| 684 if (titlebar_right_buttons_hbox_) | |
| 685 right_padding = side_padding; | |
| 686 if (titlebar_left_buttons_hbox_) | |
| 687 left_padding = side_padding; | |
| 688 | |
| 689 gtk_alignment_set_padding(GTK_ALIGNMENT(titlebar_alignment_), | |
| 690 top_padding, 0, | |
| 691 left_padding, right_padding); | |
| 692 browser_window_->tabstrip()->SetVerticalOffset(vertical_offset); | |
| 693 } else { | |
| 694 if (using_custom_frame_ && !browser_window_->IsFullscreen()) { | |
| 695 gtk_alignment_set_padding(GTK_ALIGNMENT(titlebar_alignment_), | |
| 696 kAppModePaddingTop, kAppModePaddingBottom, kAppModePaddingLeft, 0); | |
| 697 gtk_widget_show(titlebar_alignment_); | |
| 698 } else { | |
| 699 gtk_widget_hide(titlebar_alignment_); | |
| 700 } | |
| 701 } | |
| 702 | |
| 703 // Resize the buttons so that the clickable area extends all the way to the | |
| 704 // edge of the browser window. | |
| 705 GtkRequisition close_button_req = close_button_req_; | |
| 706 GtkRequisition minimize_button_req = minimize_button_req_; | |
| 707 GtkRequisition restore_button_req = restore_button_req_; | |
| 708 if (using_custom_frame_ && browser_window_->IsMaximized()) { | |
| 709 close_button_req.width += kButtonOuterPadding; | |
| 710 close_button_req.height += kButtonOuterPadding; | |
| 711 minimize_button_req.height += kButtonOuterPadding; | |
| 712 restore_button_req.height += kButtonOuterPadding; | |
| 713 if (top_padding_left_) | |
| 714 gtk_widget_hide(top_padding_left_); | |
| 715 if (top_padding_right_) | |
| 716 gtk_widget_hide(top_padding_right_); | |
| 717 } else { | |
| 718 if (top_padding_left_) | |
| 719 gtk_widget_show(top_padding_left_); | |
| 720 if (top_padding_right_) | |
| 721 gtk_widget_show(top_padding_right_); | |
| 722 } | |
| 723 if (close_button_.get()) { | |
| 724 gtk_widget_set_size_request(close_button_->widget(), | |
| 725 close_button_req.width, | |
| 726 close_button_req.height); | |
| 727 } | |
| 728 if (minimize_button_.get()) { | |
| 729 gtk_widget_set_size_request(minimize_button_->widget(), | |
| 730 minimize_button_req.width, | |
| 731 minimize_button_req.height); | |
| 732 } | |
| 733 if (maximize_button_.get()) { | |
| 734 gtk_widget_set_size_request(restore_button_->widget(), | |
| 735 restore_button_req.width, | |
| 736 restore_button_req.height); | |
| 737 } | |
| 738 } | |
| 739 | |
| 740 void BrowserTitlebar::UpdateTextColor() { | |
| 741 if (!app_mode_title_) | |
| 742 return; | |
| 743 | |
| 744 if (theme_service_ && theme_service_->UsingNativeTheme()) { | |
| 745 // We don't really have any good options here. | |
| 746 // | |
| 747 // Colors from window manager themes aren't exposed in GTK; the window | |
| 748 // manager is a separate component and when there is information sharing | |
| 749 // (in the case of metacity), it's one way where the window manager reads | |
| 750 // data from the GTK theme (which allows us to do a decent job with | |
| 751 // picking the frame color). | |
| 752 // | |
| 753 // We probably won't match in the majority of cases, but we can at the | |
| 754 // very least make things legible. The default metacity and xfwm themes | |
| 755 // on ubuntu have white text hardcoded. Determine whether black or white | |
| 756 // has more luminosity contrast and then set that color as the text | |
| 757 // color. | |
| 758 GdkColor frame_color; | |
| 759 if (window_has_focus_) { | |
| 760 frame_color = theme_service_->GetGdkColor( | |
| 761 ThemeProperties::COLOR_FRAME); | |
| 762 } else { | |
| 763 frame_color = theme_service_->GetGdkColor( | |
| 764 ThemeProperties::COLOR_FRAME_INACTIVE); | |
| 765 } | |
| 766 GdkColor text_color = PickLuminosityContrastingColor( | |
| 767 &frame_color, &ui::kGdkWhite, &ui::kGdkBlack); | |
| 768 gtk_util::SetLabelColor(app_mode_title_, &text_color); | |
| 769 } else { | |
| 770 gtk_util::SetLabelColor(app_mode_title_, &ui::kGdkWhite); | |
| 771 } | |
| 772 } | |
| 773 | |
| 774 void BrowserTitlebar::UpdateAvatarLabel() { | |
| 775 if (theme_service_ && avatar_label_) { | |
| 776 GdkColor text_color = | |
| 777 theme_service_->GetGdkColor(ThemeProperties::COLOR_MANAGED_USER_LABEL); | |
| 778 GdkColor label_background = theme_service_->GetGdkColor( | |
| 779 ThemeProperties::COLOR_MANAGED_USER_LABEL_BACKGROUND); | |
| 780 gtk_util::SetLabelColor(avatar_label_, &text_color); | |
| 781 gtk_widget_modify_bg( | |
| 782 GTK_WIDGET(avatar_label_bg_), GTK_STATE_NORMAL, &label_background); | |
| 783 char* markup = g_markup_printf_escaped( | |
| 784 "<span size='small'>%s</span>", | |
| 785 l10n_util::GetStringUTF8(IDS_MANAGED_USER_AVATAR_LABEL).c_str()); | |
| 786 gtk_label_set_markup(GTK_LABEL(avatar_label_), markup); | |
| 787 g_free(markup); | |
| 788 } | |
| 789 } | |
| 790 | |
| 791 void BrowserTitlebar::UpdateAvatar() { | |
| 792 // Remove previous state. | |
| 793 gtk_util::RemoveAllChildren(titlebar_left_avatar_frame_); | |
| 794 gtk_util::RemoveAllChildren(titlebar_right_avatar_frame_); | |
| 795 gtk_util::RemoveAllChildren(titlebar_left_label_frame_); | |
| 796 gtk_util::RemoveAllChildren(titlebar_right_label_frame_); | |
| 797 | |
| 798 if (!ShouldDisplayAvatar()) | |
| 799 return; | |
| 800 | |
| 801 if (!avatar_) { | |
| 802 if (IsOffTheRecord()) { | |
| 803 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
| 804 gfx::Image avatar_image = | |
| 805 rb.GetNativeImageNamed(IDR_OTR_ICON, ui::ResourceBundle::RTL_ENABLED); | |
| 806 avatar_ = gtk_image_new_from_pixbuf(avatar_image.ToGdkPixbuf()); | |
| 807 gtk_misc_set_alignment(GTK_MISC(avatar_), 0.0, 1.0); | |
| 808 gtk_widget_set_size_request(avatar_, -1, 0); | |
| 809 } else { | |
| 810 // Use a clickable avatar. | |
| 811 avatar_ = avatar_button_->widget(); | |
| 812 } | |
| 813 } | |
| 814 | |
| 815 gtk_widget_show_all(avatar_); | |
| 816 | |
| 817 Profile* profile = browser_window_->browser()->profile(); | |
| 818 if (profile->IsManaged()) { | |
| 819 avatar_label_ = gtk_label_new(NULL); | |
| 820 gtk_misc_set_padding(GTK_MISC(avatar_label_), 10, 2); | |
| 821 avatar_label_bg_ = gtk_event_box_new(); | |
| 822 gtk_container_add(GTK_CONTAINER(avatar_label_bg_), avatar_label_); | |
| 823 g_signal_connect(avatar_label_bg_, "button-press-event", | |
| 824 G_CALLBACK(OnAvatarLabelButtonPressedThunk), this); | |
| 825 UpdateAvatarLabel(); | |
| 826 gtk_widget_show(avatar_label_bg_); | |
| 827 gtk_widget_show(avatar_label_); | |
| 828 if (display_avatar_on_left_) { | |
| 829 gtk_container_add(GTK_CONTAINER(titlebar_left_label_frame_), | |
| 830 avatar_label_bg_); | |
| 831 gtk_widget_show(titlebar_left_label_frame_); | |
| 832 gtk_widget_hide(titlebar_right_label_frame_); | |
| 833 } else { | |
| 834 gtk_container_add(GTK_CONTAINER(titlebar_right_label_frame_), | |
| 835 avatar_label_bg_); | |
| 836 gtk_widget_show(titlebar_right_label_frame_); | |
| 837 gtk_widget_hide(titlebar_left_label_frame_); | |
| 838 } | |
| 839 } | |
| 840 | |
| 841 if (display_avatar_on_left_) { | |
| 842 gtk_container_add(GTK_CONTAINER(titlebar_left_avatar_frame_), avatar_); | |
| 843 gtk_widget_show(titlebar_left_avatar_frame_); | |
| 844 gtk_widget_hide(titlebar_right_avatar_frame_); | |
| 845 } else { | |
| 846 gtk_container_add(GTK_CONTAINER(titlebar_right_avatar_frame_), avatar_); | |
| 847 gtk_widget_show(titlebar_right_avatar_frame_); | |
| 848 gtk_widget_hide(titlebar_left_avatar_frame_); | |
| 849 } | |
| 850 | |
| 851 if (IsOffTheRecord()) | |
| 852 return; | |
| 853 | |
| 854 bool is_gaia_picture = false; | |
| 855 gfx::Image avatar; | |
| 856 ProfileInfoCache& cache = | |
| 857 g_browser_process->profile_manager()->GetProfileInfoCache(); | |
| 858 size_t index = cache.GetIndexOfProfileWithPath(profile->GetPath()); | |
| 859 if (index == std::string::npos) | |
| 860 return; | |
| 861 | |
| 862 is_gaia_picture = | |
| 863 cache.IsUsingGAIAPictureOfProfileAtIndex(index) && | |
| 864 cache.GetGAIAPictureOfProfileAtIndex(index); | |
| 865 avatar = cache.GetAvatarIconOfProfileAtIndex(index); | |
| 866 avatar_button_->SetIcon(avatar, is_gaia_picture); | |
| 867 avatar_button_->set_menu_frame_style(display_avatar_on_left_ ? | |
| 868 BubbleGtk::ANCHOR_TOP_LEFT : BubbleGtk::ANCHOR_TOP_RIGHT); | |
| 869 } | |
| 870 | |
| 871 void BrowserTitlebar::MaximizeButtonClicked() { | |
| 872 GdkEvent* event = gtk_get_current_event(); | |
| 873 if (event->button.button == 1) { | |
| 874 gtk_window_maximize(window_); | |
| 875 } else { | |
| 876 GtkWidget* widget = GTK_WIDGET(window_); | |
| 877 GdkScreen* screen = gtk_widget_get_screen(widget); | |
| 878 gint monitor = gdk_screen_get_monitor_at_window( | |
| 879 screen, gtk_widget_get_window(widget)); | |
| 880 GdkRectangle screen_rect; | |
| 881 gdk_screen_get_monitor_geometry(screen, monitor, &screen_rect); | |
| 882 | |
| 883 gint x, y; | |
| 884 gtk_window_get_position(window_, &x, &y); | |
| 885 | |
| 886 GtkAllocation allocation; | |
| 887 gtk_widget_get_allocation(widget, &allocation); | |
| 888 gint width = allocation.width; | |
| 889 gint height = allocation.height; | |
| 890 | |
| 891 if (event->button.button == 3) { | |
| 892 x = 0; | |
| 893 width = screen_rect.width; | |
| 894 } else if (event->button.button == 2) { | |
| 895 y = 0; | |
| 896 height = screen_rect.height; | |
| 897 } | |
| 898 | |
| 899 browser_window_->SetBounds(gfx::Rect(x, y, width, height)); | |
| 900 } | |
| 901 gdk_event_free(event); | |
| 902 } | |
| 903 | |
| 904 void BrowserTitlebar::UpdateMaximizeRestoreVisibility() { | |
| 905 if (maximize_button_.get()) { | |
| 906 if (browser_window_->IsMaximized()) { | |
| 907 gtk_widget_hide(maximize_button_->widget()); | |
| 908 gtk_widget_show(restore_button_->widget()); | |
| 909 } else { | |
| 910 gtk_widget_hide(restore_button_->widget()); | |
| 911 gtk_widget_show(maximize_button_->widget()); | |
| 912 } | |
| 913 } | |
| 914 } | |
| 915 | |
| 916 gboolean BrowserTitlebar::OnWindowStateChanged(GtkWindow* window, | |
| 917 GdkEventWindowState* event) { | |
| 918 UpdateMaximizeRestoreVisibility(); | |
| 919 UpdateTitlebarAlignment(); | |
| 920 UpdateTextColor(); | |
| 921 return FALSE; | |
| 922 } | |
| 923 | |
| 924 gboolean BrowserTitlebar::OnScroll(GtkWidget* widget, GdkEventScroll* event) { | |
| 925 Browser* browser = browser_window_->browser(); | |
| 926 int index = browser->tab_strip_model()->active_index(); | |
| 927 if (event->direction == GDK_SCROLL_LEFT || | |
| 928 event->direction == GDK_SCROLL_UP) { | |
| 929 if (index != 0) | |
| 930 chrome::SelectPreviousTab(browser); | |
| 931 } else if (index + 1 < browser->tab_strip_model()->count()) { | |
| 932 chrome::SelectNextTab(browser); | |
| 933 } | |
| 934 return TRUE; | |
| 935 } | |
| 936 | |
| 937 void BrowserTitlebar::OnButtonClicked(GtkWidget* button) { | |
| 938 if (close_button_.get() && close_button_->widget() == button) { | |
| 939 browser_window_->Close(); | |
| 940 } else if (restore_button_.get() && restore_button_->widget() == button) { | |
| 941 browser_window_->UnMaximize(); | |
| 942 } else if (maximize_button_.get() && maximize_button_->widget() == button) { | |
| 943 MaximizeButtonClicked(); | |
| 944 } else if (minimize_button_.get() && minimize_button_->widget() == button) { | |
| 945 gtk_window_iconify(window_); | |
| 946 } | |
| 947 } | |
| 948 | |
| 949 gboolean BrowserTitlebar::OnFaviconMenuButtonPressed(GtkWidget* widget, | |
| 950 GdkEventButton* event) { | |
| 951 if (event->button != 1) | |
| 952 return FALSE; | |
| 953 | |
| 954 if (!favicon_menu_model_.get()) { | |
| 955 favicon_menu_model_.reset( | |
| 956 new PopupPageMenuModel(this, browser_window_->browser())); | |
| 957 | |
| 958 favicon_menu_.reset(new MenuGtk(NULL, favicon_menu_model_.get())); | |
| 959 } | |
| 960 | |
| 961 favicon_menu_->PopupForWidget(app_mode_favicon_, event->button, event->time); | |
| 962 | |
| 963 return TRUE; | |
| 964 } | |
| 965 | |
| 966 gboolean BrowserTitlebar::OnAvatarLabelButtonPressed(GtkWidget* widget, | |
| 967 GdkEventButton* event) { | |
| 968 if (event->button != 1) | |
| 969 return FALSE; | |
| 970 | |
| 971 // Show the avatar menu bubble with the upward arrow at the x position where | |
| 972 // the user has clicked. | |
| 973 gfx::Rect rect = gtk_util::WidgetBounds(widget); | |
| 974 rect.set_x(event->x); | |
| 975 rect.set_width(0); | |
| 976 new AvatarMenuBubbleGtk( | |
| 977 browser_window_->browser(), widget, BubbleGtk::ANCHOR_TOP_RIGHT, &rect); | |
| 978 return TRUE; | |
| 979 } | |
| 980 | |
| 981 void BrowserTitlebar::ShowContextMenu(GdkEventButton* event) { | |
| 982 if (!context_menu_.get()) { | |
| 983 context_menu_model_.reset(new ContextMenuModel(this)); | |
| 984 context_menu_.reset(new MenuGtk(NULL, context_menu_model_.get())); | |
| 985 } | |
| 986 | |
| 987 context_menu_->PopupAsContext(gfx::Point(event->x_root, event->y_root), | |
| 988 event->time); | |
| 989 } | |
| 990 | |
| 991 bool BrowserTitlebar::IsCommandIdEnabled(int command_id) const { | |
| 992 if (command_id == kShowWindowDecorationsCommand) | |
| 993 return true; | |
| 994 | |
| 995 return chrome::IsCommandEnabled(browser_window_->browser(), command_id); | |
| 996 } | |
| 997 | |
| 998 bool BrowserTitlebar::IsCommandIdChecked(int command_id) const { | |
| 999 if (command_id == kShowWindowDecorationsCommand) { | |
| 1000 PrefService* prefs = browser_window_->browser()->profile()->GetPrefs(); | |
| 1001 return !prefs->GetBoolean(prefs::kUseCustomChromeFrame); | |
| 1002 } | |
| 1003 | |
| 1004 EncodingMenuController controller; | |
| 1005 if (controller.DoesCommandBelongToEncodingMenu(command_id)) { | |
| 1006 WebContents* web_contents = | |
| 1007 browser_window_->browser()->tab_strip_model()->GetActiveWebContents(); | |
| 1008 if (web_contents) { | |
| 1009 return controller.IsItemChecked(browser_window_->browser()->profile(), | |
| 1010 web_contents->GetEncoding(), | |
| 1011 command_id); | |
| 1012 } | |
| 1013 return false; | |
| 1014 } | |
| 1015 | |
| 1016 NOTREACHED(); | |
| 1017 return false; | |
| 1018 } | |
| 1019 | |
| 1020 void BrowserTitlebar::ExecuteCommand(int command_id, int event_flags) { | |
| 1021 if (command_id == kShowWindowDecorationsCommand) { | |
| 1022 PrefService* prefs = browser_window_->browser()->profile()->GetPrefs(); | |
| 1023 prefs->SetBoolean(prefs::kUseCustomChromeFrame, | |
| 1024 !prefs->GetBoolean(prefs::kUseCustomChromeFrame)); | |
| 1025 return; | |
| 1026 } | |
| 1027 | |
| 1028 chrome::ExecuteCommand(browser_window_->browser(), command_id); | |
| 1029 } | |
| 1030 | |
| 1031 bool BrowserTitlebar::GetAcceleratorForCommandId( | |
| 1032 int command_id, | |
| 1033 ui::Accelerator* out_accelerator) { | |
| 1034 const ui::Accelerator* accelerator = | |
| 1035 AcceleratorsGtk::GetInstance()->GetPrimaryAcceleratorForCommand( | |
| 1036 command_id); | |
| 1037 if (!accelerator) | |
| 1038 return false; | |
| 1039 *out_accelerator = *accelerator; | |
| 1040 return true; | |
| 1041 } | |
| 1042 | |
| 1043 void BrowserTitlebar::Observe(int type, | |
| 1044 const content::NotificationSource& source, | |
| 1045 const content::NotificationDetails& details) { | |
| 1046 switch (type) { | |
| 1047 case chrome::NOTIFICATION_BROWSER_THEME_CHANGED: { | |
| 1048 UpdateTextColor(); | |
| 1049 UpdateAvatarLabel(); | |
| 1050 | |
| 1051 if (minimize_button_.get()) | |
| 1052 UpdateButtonBackground(minimize_button_.get()); | |
| 1053 if (maximize_button_.get()) | |
| 1054 UpdateButtonBackground(maximize_button_.get()); | |
| 1055 if (restore_button_.get()) | |
| 1056 UpdateButtonBackground(restore_button_.get()); | |
| 1057 if (close_button_.get()) | |
| 1058 UpdateButtonBackground(close_button_.get()); | |
| 1059 break; | |
| 1060 } | |
| 1061 | |
| 1062 case chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED: | |
| 1063 if (!IsOffTheRecord()) | |
| 1064 UpdateAvatar(); | |
| 1065 break; | |
| 1066 | |
| 1067 default: | |
| 1068 NOTREACHED(); | |
| 1069 } | |
| 1070 } | |
| 1071 | |
| 1072 void BrowserTitlebar::ActiveWindowChanged(GdkWindow* active_window) { | |
| 1073 // Can be called during shutdown; BrowserWindowGtk will set our |window_| | |
| 1074 // to NULL during that time. | |
| 1075 if (!window_) | |
| 1076 return; | |
| 1077 | |
| 1078 window_has_focus_ = | |
| 1079 gtk_widget_get_window(GTK_WIDGET(window_)) == active_window; | |
| 1080 UpdateTextColor(); | |
| 1081 } | |
| 1082 | |
| 1083 bool BrowserTitlebar::ShouldDisplayAvatar() { | |
| 1084 if (IsOffTheRecord()) | |
| 1085 return true; | |
| 1086 | |
| 1087 if (!browser_window_->browser()->is_type_tabbed()) | |
| 1088 return false; | |
| 1089 | |
| 1090 return AvatarMenu::ShouldShowAvatarMenu(); | |
| 1091 } | |
| 1092 | |
| 1093 bool BrowserTitlebar::IsOffTheRecord() { | |
| 1094 return browser_window_->browser()->profile()->IsOffTheRecord(); | |
| 1095 } | |
| 1096 | |
| 1097 BrowserTitlebar::ContextMenuModel::ContextMenuModel( | |
| 1098 ui::SimpleMenuModel::Delegate* delegate) | |
| 1099 : SimpleMenuModel(delegate) { | |
| 1100 AddItemWithStringId(IDC_NEW_TAB, IDS_TAB_CXMENU_NEWTAB); | |
| 1101 AddItemWithStringId(IDC_RESTORE_TAB, IDS_RESTORE_TAB); | |
| 1102 AddSeparator(ui::NORMAL_SEPARATOR); | |
| 1103 AddItemWithStringId(IDC_TASK_MANAGER, IDS_TASK_MANAGER); | |
| 1104 AddSeparator(ui::NORMAL_SEPARATOR); | |
| 1105 AddCheckItemWithStringId(kShowWindowDecorationsCommand, | |
| 1106 IDS_SHOW_WINDOW_DECORATIONS_MENU); | |
| 1107 } | |
| OLD | NEW |