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