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 |