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.
5 #include "chrome/browser/ui/gtk/browser_toolbar_gtk.h"
7 #include <gdk/gdkkeysyms.h>
9 #include <X11/XF86keysym.h>
11 #include "base/base_paths.h"
12 #include "base/command_line.h"
13 #include "base/debug/trace_event.h"
14 #include "base/i18n/rtl.h"
15 #include "base/logging.h"
16 #include "base/memory/singleton.h"
17 #include "base/path_service.h"
18 #include "base/prefs/pref_service.h"
19 #include "chrome/app/chrome_command_ids.h"
20 #include "chrome/browser/chrome_notification_types.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/themes/theme_properties.h"
23 #include "chrome/browser/themes/theme_service.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/browser_commands.h"
26 #include "chrome/browser/ui/global_error/global_error.h"
27 #include "chrome/browser/ui/global_error/global_error_service.h"
28 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
29 #include "chrome/browser/ui/gtk/accelerators_gtk.h"
30 #include "chrome/browser/ui/gtk/back_forward_button_gtk.h"
31 #include "chrome/browser/ui/gtk/bookmarks/bookmark_sub_menu_model_gtk.h"
32 #include "chrome/browser/ui/gtk/browser_actions_toolbar_gtk.h"
33 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
34 #include "chrome/browser/ui/gtk/custom_button.h"
35 #include "chrome/browser/ui/gtk/event_utils.h"
36 #include "chrome/browser/ui/gtk/gtk_chrome_button.h"
37 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
38 #include "chrome/browser/ui/gtk/gtk_util.h"
39 #include "chrome/browser/ui/gtk/location_bar_view_gtk.h"
40 #include "chrome/browser/ui/gtk/reload_button_gtk.h"
41 #include "chrome/browser/ui/gtk/rounded_window.h"
42 #include "chrome/browser/ui/gtk/tabs/tab_strip_gtk.h"
43 #include "chrome/browser/ui/gtk/view_id_util.h"
44 #include "chrome/browser/ui/toolbar/encoding_menu_controller.h"
45 #include "chrome/browser/upgrade_detector.h"
46 #include "chrome/common/net/url_fixer_upper.h"
47 #include "chrome/common/pref_names.h"
48 #include "chrome/common/url_constants.h"
49 #include "content/public/browser/host_zoom_map.h"
50 #include "content/public/browser/notification_details.h"
51 #include "content/public/browser/notification_service.h"
52 #include "content/public/browser/user_metrics.h"
53 #include "content/public/browser/web_contents.h"
54 #include "grit/chromium_strings.h"
55 #include "grit/generated_resources.h"
56 #include "grit/theme_resources.h"
57 #include "ui/base/dragdrop/gtk_dnd_util.h"
58 #include "ui/base/l10n/l10n_util.h"
59 #include "ui/base/resource/resource_bundle.h"
60 #include "ui/gfx/canvas_skia_paint.h"
61 #include "ui/gfx/gtk_util.h"
62 #include "ui/gfx/image/cairo_cached_surface.h"
63 #include "ui/gfx/skbitmap_operations.h"
65 using base::UserMetricsAction
;
66 using content::HostZoomMap
;
67 using content::WebContents
;
71 // Padding on left and right of the left toolbar buttons (back, forward, reload,
73 const int kToolbarLeftAreaPadding
= 4;
75 // Height of the toolbar in pixels (not counting padding).
76 const int kToolbarHeight
= 29;
78 // Padding within the toolbar above the buttons and location bar.
79 const int kTopBottomPadding
= 3;
81 // Height of the toolbar in pixels when we only show the location bar.
82 const int kToolbarHeightLocationBarOnly
= kToolbarHeight
- 2;
84 // Interior spacing between toolbar widgets.
85 const int kToolbarWidgetSpacing
= 1;
87 // Amount of rounding on top corners of toolbar. Only used in Gtk theme mode.
88 const int kToolbarCornerSize
= 3;
90 void SetWidgetHeightRequest(GtkWidget
* widget
, gpointer user_data
) {
91 gtk_widget_set_size_request(widget
, -1, GPOINTER_TO_INT(user_data
));
96 // BrowserToolbarGtk, public ---------------------------------------------------
98 BrowserToolbarGtk::BrowserToolbarGtk(Browser
* browser
, BrowserWindowGtk
* window
)
100 location_bar_(new LocationBarViewGtk(browser
)),
101 is_wrench_menu_model_valid_(true),
104 wrench_menu_model_
.reset(new WrenchMenuModel(this, browser_
, false));
106 chrome::AddCommandObserver(browser_
, IDC_BACK
, this);
107 chrome::AddCommandObserver(browser_
, IDC_FORWARD
, this);
108 chrome::AddCommandObserver(browser_
, IDC_HOME
, this);
109 chrome::AddCommandObserver(browser_
, IDC_BOOKMARK_PAGE
, this);
112 chrome::NOTIFICATION_UPGRADE_RECOMMENDED
,
113 content::NotificationService::AllSources());
115 chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED
,
116 content::Source
<Profile
>(browser_
->profile()));
119 BrowserToolbarGtk::~BrowserToolbarGtk() {
120 chrome::RemoveCommandObserver(browser_
, IDC_BACK
, this);
121 chrome::RemoveCommandObserver(browser_
, IDC_FORWARD
, this);
122 chrome::RemoveCommandObserver(browser_
, IDC_HOME
, this);
123 chrome::RemoveCommandObserver(browser_
, IDC_BOOKMARK_PAGE
, this);
125 offscreen_entry_
.Destroy();
127 wrench_menu_
.reset();
130 void BrowserToolbarGtk::Init(GtkWindow
* top_level_window
) {
131 Profile
* profile
= browser_
->profile();
132 theme_service_
= GtkThemeService::GetFrom(profile
);
134 chrome::NOTIFICATION_BROWSER_THEME_CHANGED
,
135 content::Source
<ThemeService
>(theme_service_
));
137 offscreen_entry_
.Own(gtk_entry_new());
139 base::Closure callback
=
140 base::Bind(&BrowserToolbarGtk::SetUpDragForHomeButton
,
141 base::Unretained(this));
143 show_home_button_
.Init(prefs::kShowHomeButton
, profile
->GetPrefs(),
144 base::Bind(&BrowserToolbarGtk::UpdateShowHomeButton
,
145 base::Unretained(this)));
146 home_page_
.Init(prefs::kHomePage
, profile
->GetPrefs(), callback
);
147 home_page_is_new_tab_page_
.Init(prefs::kHomePageIsNewTabPage
,
151 event_box_
= gtk_event_box_new();
152 // Make the event box transparent so themes can use transparent toolbar
154 if (!theme_service_
->UsingNativeTheme())
155 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_
), FALSE
);
157 toolbar_
= gtk_hbox_new(FALSE
, 0);
158 alignment_
= gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
159 UpdateForBookmarkBarVisibility(false);
160 g_signal_connect(alignment_
, "expose-event",
161 G_CALLBACK(&OnAlignmentExposeThunk
), this);
162 gtk_container_add(GTK_CONTAINER(event_box_
), alignment_
);
163 gtk_container_add(GTK_CONTAINER(alignment_
), toolbar_
);
165 toolbar_left_
= gtk_hbox_new(FALSE
, kToolbarWidgetSpacing
);
167 GtkSizeGroup
* size_group
= gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL
);
168 back_
.reset(new BackForwardButtonGtk(browser_
, false));
169 g_signal_connect(back_
->widget(), "clicked",
170 G_CALLBACK(OnButtonClickThunk
), this);
171 gtk_size_group_add_widget(size_group
, back_
->widget());
172 gtk_box_pack_start(GTK_BOX(toolbar_left_
), back_
->widget(), FALSE
,
175 forward_
.reset(new BackForwardButtonGtk(browser_
, true));
176 g_signal_connect(forward_
->widget(), "clicked",
177 G_CALLBACK(OnButtonClickThunk
), this);
178 gtk_size_group_add_widget(size_group
, forward_
->widget());
179 gtk_box_pack_start(GTK_BOX(toolbar_left_
), forward_
->widget(), FALSE
,
182 reload_
.reset(new ReloadButtonGtk(location_bar_
.get(), browser_
));
183 gtk_size_group_add_widget(size_group
, reload_
->widget());
184 gtk_box_pack_start(GTK_BOX(toolbar_left_
), reload_
->widget(), FALSE
, FALSE
,
187 home_
.reset(new CustomDrawButton(theme_service_
, IDR_HOME
, IDR_HOME_P
,
188 IDR_HOME_H
, 0, GTK_STOCK_HOME
, GTK_ICON_SIZE_SMALL_TOOLBAR
));
189 gtk_widget_set_tooltip_text(home_
->widget(),
190 l10n_util::GetStringUTF8(IDS_TOOLTIP_HOME
).c_str());
191 g_signal_connect(home_
->widget(), "clicked",
192 G_CALLBACK(OnButtonClickThunk
), this);
193 gtk_size_group_add_widget(size_group
, home_
->widget());
194 gtk_box_pack_start(GTK_BOX(toolbar_left_
), home_
->widget(), FALSE
, FALSE
,
195 kToolbarWidgetSpacing
);
196 gtk_util::SetButtonTriggersNavigation(home_
->widget());
198 gtk_box_pack_start(GTK_BOX(toolbar_
), toolbar_left_
, FALSE
, FALSE
,
199 kToolbarLeftAreaPadding
);
201 g_object_unref(size_group
);
203 location_hbox_
= gtk_hbox_new(FALSE
, 0);
204 location_bar_
->Init(ShouldOnlyShowLocation());
205 gtk_box_pack_start(GTK_BOX(location_hbox_
), location_bar_
->widget(), TRUE
,
208 g_signal_connect(location_hbox_
, "expose-event",
209 G_CALLBACK(OnLocationHboxExposeThunk
), this);
210 gtk_box_pack_start(GTK_BOX(toolbar_
), location_hbox_
, TRUE
, TRUE
,
211 ShouldOnlyShowLocation() ? 1 : 0);
213 if (!ShouldOnlyShowLocation()) {
214 actions_toolbar_
.reset(new BrowserActionsToolbarGtk(browser_
));
215 gtk_box_pack_start(GTK_BOX(toolbar_
), actions_toolbar_
->widget(),
219 wrench_menu_image_
= gtk_image_new_from_pixbuf(
220 theme_service_
->GetRTLEnabledPixbufNamed(IDR_TOOLS
));
221 wrench_menu_button_
.reset(new CustomDrawButton(theme_service_
, IDR_TOOLS
,
222 IDR_TOOLS_P
, IDR_TOOLS_H
, 0, wrench_menu_image_
));
223 GtkWidget
* wrench_button
= wrench_menu_button_
->widget();
225 gtk_widget_set_tooltip_text(
227 l10n_util::GetStringUTF8(IDS_APPMENU_TOOLTIP
).c_str());
228 g_signal_connect(wrench_button
, "button-press-event",
229 G_CALLBACK(OnMenuButtonPressEventThunk
), this);
230 gtk_widget_set_can_focus(wrench_button
, FALSE
);
232 // Put the wrench button in a box so that we can paint the update notification
234 GtkWidget
* wrench_box
= gtk_alignment_new(0, 0, 1, 1);
235 g_signal_connect_after(wrench_box
, "expose-event",
236 G_CALLBACK(OnWrenchMenuButtonExposeThunk
), this);
237 gtk_container_add(GTK_CONTAINER(wrench_box
), wrench_button
);
238 gtk_box_pack_start(GTK_BOX(toolbar_
), wrench_box
, FALSE
, FALSE
, 4);
240 wrench_menu_
.reset(new MenuGtk(this, wrench_menu_model_
.get()));
241 // The bookmark menu model needs to be able to force the wrench menu to close.
242 wrench_menu_model_
->bookmark_sub_menu_model()->SetMenuGtk(wrench_menu_
.get());
244 zoom_subscription_
= HostZoomMap::GetForBrowserContext(
245 browser()->profile())->AddZoomLevelChangedCallback(
246 base::Bind(&BrowserToolbarGtk::OnZoomLevelChanged
,
247 base::Unretained(this)));
249 if (ShouldOnlyShowLocation()) {
250 gtk_widget_show(event_box_
);
251 gtk_widget_show(alignment_
);
252 gtk_widget_show(toolbar_
);
253 gtk_widget_show_all(location_hbox_
);
254 gtk_widget_hide(reload_
->widget());
256 gtk_widget_show_all(event_box_
);
257 if (actions_toolbar_
->button_count() == 0)
258 gtk_widget_hide(actions_toolbar_
->widget());
261 // Initialize pref-dependent UI state.
262 UpdateShowHomeButton();
263 SetUpDragForHomeButton();
265 // Because the above does a recursive show all on all widgets we need to
266 // update the icon visibility to hide them.
267 location_bar_
->UpdateContentSettingsIcons();
270 theme_service_
->InitThemesFor(this);
273 void BrowserToolbarGtk::SetViewIDs() {
274 ViewIDUtil::SetID(widget(), VIEW_ID_TOOLBAR
);
275 ViewIDUtil::SetID(back_
->widget(), VIEW_ID_BACK_BUTTON
);
276 ViewIDUtil::SetID(forward_
->widget(), VIEW_ID_FORWARD_BUTTON
);
277 ViewIDUtil::SetID(reload_
->widget(), VIEW_ID_RELOAD_BUTTON
);
278 ViewIDUtil::SetID(home_
->widget(), VIEW_ID_HOME_BUTTON
);
279 ViewIDUtil::SetID(location_bar_
->widget(), VIEW_ID_OMNIBOX
);
280 ViewIDUtil::SetID(wrench_menu_button_
->widget(), VIEW_ID_APP_MENU
);
283 void BrowserToolbarGtk::Show() {
284 gtk_widget_show(toolbar_
);
287 void BrowserToolbarGtk::Hide() {
288 gtk_widget_hide(toolbar_
);
291 LocationBar
* BrowserToolbarGtk::GetLocationBar() const {
292 return location_bar_
.get();
295 void BrowserToolbarGtk::UpdateForBookmarkBarVisibility(
296 bool show_bottom_padding
) {
297 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment_
),
298 ShouldOnlyShowLocation() ? 0 : kTopBottomPadding
,
299 !show_bottom_padding
|| ShouldOnlyShowLocation() ? 0 : kTopBottomPadding
,
303 void BrowserToolbarGtk::ShowAppMenu() {
304 wrench_menu_
->Cancel();
306 if (!is_wrench_menu_model_valid_
)
309 wrench_menu_button_
->SetPaintOverride(GTK_STATE_ACTIVE
);
310 content::RecordAction(UserMetricsAction("ShowAppMenu"));
311 wrench_menu_
->PopupAsFromKeyEvent(wrench_menu_button_
->widget());
314 // CommandObserver -------------------------------------------------------------
316 void BrowserToolbarGtk::EnabledStateChangedForCommand(int id
, bool enabled
) {
317 GtkWidget
* widget
= NULL
;
320 widget
= back_
->widget();
323 widget
= forward_
->widget();
327 widget
= home_
->widget();
331 if (!enabled
&& gtk_widget_get_state(widget
) == GTK_STATE_PRELIGHT
) {
332 // If we're disabling a widget, GTK will helpfully restore it to its
333 // previous state when we re-enable it, even if that previous state
334 // is the prelight. This looks bad. See the bug for a simple repro.
335 // http://code.google.com/p/chromium/issues/detail?id=13729
336 gtk_widget_set_state(widget
, GTK_STATE_NORMAL
);
338 gtk_widget_set_sensitive(widget
, enabled
);
342 // MenuGtk::Delegate -----------------------------------------------------------
344 void BrowserToolbarGtk::StoppedShowing() {
345 // Without these calls, the hover state can get stuck since the leave-notify
346 // event is not sent when clicking a button brings up the menu.
347 gtk_chrome_button_set_hover_state(
348 GTK_CHROME_BUTTON(wrench_menu_button_
->widget()), 0.0);
349 wrench_menu_button_
->UnsetPaintOverride();
352 GtkIconSet
* BrowserToolbarGtk::GetIconSetForId(int idr
) {
353 return theme_service_
->GetIconSetForId(idr
);
356 // Always show images because we desire that some icons always show
357 // regardless of the system setting.
358 bool BrowserToolbarGtk::AlwaysShowIconForCmd(int command_id
) const {
359 return command_id
== IDC_UPGRADE_DIALOG
||
360 BookmarkSubMenuModel::IsBookmarkItemCommandId(command_id
);
363 // ui::AcceleratorProvider
365 bool BrowserToolbarGtk::GetAcceleratorForCommandId(
367 ui::Accelerator
* out_accelerator
) {
368 const ui::Accelerator
* accelerator
=
369 AcceleratorsGtk::GetInstance()->GetPrimaryAcceleratorForCommand(id
);
372 *out_accelerator
= *accelerator
;
376 // content::NotificationObserver -----------------------------------------------
378 void BrowserToolbarGtk::Observe(int type
,
379 const content::NotificationSource
& source
,
380 const content::NotificationDetails
& details
) {
381 if (type
== chrome::NOTIFICATION_BROWSER_THEME_CHANGED
) {
382 // Update the spacing around the menu buttons
383 bool use_gtk
= theme_service_
->UsingNativeTheme();
384 int border
= use_gtk
? 0 : 2;
385 gtk_container_set_border_width(
386 GTK_CONTAINER(wrench_menu_button_
->widget()), border
);
388 // Force the height of the toolbar so we get the right amount of padding
389 // above and below the location bar. We always force the size of the widgets
390 // to either side of the location box, but we only force the location box
391 // size in chrome-theme mode because that's the only time we try to control
393 int toolbar_height
= ShouldOnlyShowLocation() ?
394 kToolbarHeightLocationBarOnly
: kToolbarHeight
;
395 gtk_container_foreach(GTK_CONTAINER(toolbar_
), SetWidgetHeightRequest
,
396 GINT_TO_POINTER(toolbar_height
));
397 gtk_widget_set_size_request(location_hbox_
, -1,
398 use_gtk
? -1 : toolbar_height
);
400 // When using the GTK+ theme, we need to have the event box be visible so
401 // buttons don't get a halo color from the background. When using Chromium
402 // themes, we want to let the background show through the toolbar.
403 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_
), use_gtk
);
406 // We need to manually update the icon if we are in GTK mode. (Note that
407 // we set the initial value in Init()).
408 gtk_image_set_from_pixbuf(
409 GTK_IMAGE(wrench_menu_image_
),
410 theme_service_
->GetRTLEnabledPixbufNamed(IDR_TOOLS
));
414 } else if (type
== chrome::NOTIFICATION_UPGRADE_RECOMMENDED
) {
415 // Redraw the wrench menu to update the badge.
416 gtk_widget_queue_draw(wrench_menu_button_
->widget());
417 } else if (type
== chrome::NOTIFICATION_GLOBAL_ERRORS_CHANGED
) {
418 is_wrench_menu_model_valid_
= false;
419 gtk_widget_queue_draw(wrench_menu_button_
->widget());
425 // BrowserToolbarGtk, public ---------------------------------------------------
427 void BrowserToolbarGtk::UpdateWebContents(WebContents
* contents
) {
428 location_bar_
->Update(contents
);
430 if (actions_toolbar_
.get())
431 actions_toolbar_
->Update();
434 bool BrowserToolbarGtk::IsWrenchMenuShowing() const {
435 return wrench_menu_
.get() && gtk_widget_get_visible(wrench_menu_
->widget());
438 // BrowserToolbarGtk, private --------------------------------------------------
440 void BrowserToolbarGtk::OnZoomLevelChanged(
441 const HostZoomMap::ZoomLevelChange
& change
) {
442 // Since BrowserToolbarGtk create a new |wrench_menu_model_| in
443 // RebuildWrenchMenu(), the ordering of the observers of HostZoomMap
444 // can change, and result in subtle bugs like http://crbug.com/118823.
445 // Rather than depending on the ordering of the observers, always update
446 // the WrenchMenuModel before updating the WrenchMenu.
447 wrench_menu_model_
->UpdateZoomControls();
449 // If our zoom level changed, we need to tell the menu to update its state,
450 // since the menu could still be open.
451 wrench_menu_
->UpdateMenu();
455 void BrowserToolbarGtk::SetUpDragForHomeButton() {
456 if (!home_page_
.IsManaged() && !home_page_is_new_tab_page_
.IsManaged()) {
457 gtk_drag_dest_set(home_
->widget(), GTK_DEST_DEFAULT_ALL
,
458 NULL
, 0, GDK_ACTION_COPY
);
459 static const int targets
[] = { ui::TEXT_PLAIN
, ui::TEXT_URI_LIST
, -1 };
460 ui::SetDestTargetList(home_
->widget(), targets
);
462 drop_handler_
.reset(new ui::GtkSignalRegistrar());
463 drop_handler_
->Connect(home_
->widget(), "drag-data-received",
464 G_CALLBACK(OnDragDataReceivedThunk
), this);
466 gtk_drag_dest_unset(home_
->widget());
467 drop_handler_
.reset(NULL
);
471 bool BrowserToolbarGtk::UpdateRoundedness() {
472 // We still round the corners if we are in chrome theme mode, but we do it by
473 // drawing theme resources rather than changing the physical shape of the
475 bool should_be_rounded
= theme_service_
->UsingNativeTheme() &&
476 window_
->ShouldDrawContentDropShadow();
478 if (should_be_rounded
== gtk_util::IsActingAsRoundedWindow(alignment_
))
481 if (should_be_rounded
) {
482 gtk_util::ActAsRoundedWindow(alignment_
, GdkColor(), kToolbarCornerSize
,
483 gtk_util::ROUNDED_TOP
,
484 gtk_util::BORDER_NONE
);
486 gtk_util::StopActingAsRoundedWindow(alignment_
);
492 gboolean
BrowserToolbarGtk::OnAlignmentExpose(GtkWidget
* widget
,
494 TRACE_EVENT0("ui::gtk", "BrowserToolbarGtk::OnAlignmentExpose");
496 // We may need to update the roundedness of the toolbar's top corners. In
497 // this case, don't draw; we'll be called again soon enough.
498 if (UpdateRoundedness())
501 // We don't need to render the toolbar image in GTK mode.
502 if (theme_service_
->UsingNativeTheme())
505 cairo_t
* cr
= gdk_cairo_create(gtk_widget_get_window(widget
));
506 gdk_cairo_rectangle(cr
, &e
->area
);
509 gfx::Point tabstrip_origin
=
510 window_
->tabstrip()->GetTabStripOriginForWidget(widget
);
511 // Fill the entire region with the toolbar color.
512 GdkColor color
= theme_service_
->GetGdkColor(
513 ThemeProperties::COLOR_TOOLBAR
);
514 gdk_cairo_set_source_color(cr
, &color
);
517 // The horizontal size of the top left and right corner images.
518 const int kCornerWidth
= 4;
519 // The thickness of the shadow outside the toolbar's bounds; the offset
520 // between the edge of the toolbar and where we anchor the corner images.
521 const int kShadowThickness
= 2;
523 GtkAllocation allocation
;
524 gtk_widget_get_allocation(widget
, &allocation
);
525 gfx::Rect
area(e
->area
);
526 gfx::Rect
right(allocation
.x
+ allocation
.width
- kCornerWidth
,
527 allocation
.y
- kShadowThickness
,
529 allocation
.height
+ kShadowThickness
);
530 gfx::Rect
left(allocation
.x
- kShadowThickness
,
531 allocation
.y
- kShadowThickness
,
533 allocation
.height
+ kShadowThickness
);
535 if (window_
->ShouldDrawContentDropShadow()) {
536 // Leave room to draw rounded corners.
537 area
.Subtract(right
);
541 gfx::Image background
= theme_service_
->GetImageNamed(IDR_THEME_TOOLBAR
);
542 background
.ToCairo()->SetSource(
543 cr
, widget
, tabstrip_origin
.x(), tabstrip_origin
.y());
544 cairo_pattern_set_extend(cairo_get_source(cr
), CAIRO_EXTEND_REPEAT
);
545 cairo_rectangle(cr
, area
.x(), area
.y(), area
.width(), area
.height());
548 if (!window_
->ShouldDrawContentDropShadow()) {
549 // The rest of this function is for rounded corners. Our work is done here.
554 bool draw_left_corner
= left
.Intersects(gfx::Rect(e
->area
));
555 bool draw_right_corner
= right
.Intersects(gfx::Rect(e
->area
));
557 if (draw_left_corner
|| draw_right_corner
) {
558 // Create a mask which is composed of the left and/or right corners.
559 cairo_surface_t
* target
= cairo_surface_create_similar(
560 cairo_get_target(cr
),
561 CAIRO_CONTENT_COLOR_ALPHA
,
562 allocation
.x
+ allocation
.width
,
563 allocation
.y
+ allocation
.height
);
564 cairo_t
* copy_cr
= cairo_create(target
);
566 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
568 cairo_set_operator(copy_cr
, CAIRO_OPERATOR_SOURCE
);
569 if (draw_left_corner
) {
570 rb
.GetNativeImageNamed(IDR_CONTENT_TOP_LEFT_CORNER_MASK
).ToCairo()->
571 SetSource(copy_cr
, widget
, left
.x(), left
.y());
572 cairo_paint(copy_cr
);
574 if (draw_right_corner
) {
575 rb
.GetNativeImageNamed(IDR_CONTENT_TOP_RIGHT_CORNER_MASK
).ToCairo()->
576 SetSource(copy_cr
, widget
, right
.x(), right
.y());
577 // We fill a path rather than just painting because we don't want to
578 // overwrite the left corner.
579 cairo_rectangle(copy_cr
, right
.x(), right
.y(),
580 right
.width(), right
.height());
584 // Draw the background. CAIRO_OPERATOR_IN uses the existing pixel data as
586 background
.ToCairo()->SetSource(copy_cr
, widget
,
587 tabstrip_origin
.x(), tabstrip_origin
.y());
588 cairo_set_operator(copy_cr
, CAIRO_OPERATOR_IN
);
589 cairo_pattern_set_extend(cairo_get_source(copy_cr
), CAIRO_EXTEND_REPEAT
);
590 cairo_paint(copy_cr
);
591 cairo_destroy(copy_cr
);
593 // Copy the temporary surface to the screen.
594 cairo_set_source_surface(cr
, target
, 0, 0);
596 cairo_surface_destroy(target
);
601 return FALSE
; // Allow subwidgets to paint.
604 gboolean
BrowserToolbarGtk::OnLocationHboxExpose(GtkWidget
* location_hbox
,
606 TRACE_EVENT0("ui::gtk", "BrowserToolbarGtk::OnLocationHboxExpose");
607 if (theme_service_
->UsingNativeTheme()) {
608 GtkAllocation allocation
;
609 gtk_widget_get_allocation(location_hbox
, &allocation
);
610 gtk_util::DrawTextEntryBackground(offscreen_entry_
.get(),
611 location_hbox
, &e
->area
,
618 void BrowserToolbarGtk::OnButtonClick(GtkWidget
* button
) {
619 if ((button
== back_
->widget()) || (button
== forward_
->widget())) {
620 if (event_utils::DispositionForCurrentButtonPressEvent() == CURRENT_TAB
)
621 location_bar_
->Revert();
625 DCHECK(home_
.get() && button
== home_
->widget()) <<
626 "Unexpected button click callback";
627 chrome::Home(browser_
, event_utils::DispositionForCurrentButtonPressEvent());
630 gboolean
BrowserToolbarGtk::OnMenuButtonPressEvent(GtkWidget
* button
,
631 GdkEventButton
* event
) {
632 if (event
->button
!= 1)
635 if (!is_wrench_menu_model_valid_
)
638 wrench_menu_button_
->SetPaintOverride(GTK_STATE_ACTIVE
);
639 wrench_menu_
->PopupForWidget(button
, event
->button
, event
->time
);
644 void BrowserToolbarGtk::OnDragDataReceived(GtkWidget
* widget
,
645 GdkDragContext
* drag_context
, gint x
, gint y
,
646 GtkSelectionData
* data
, guint info
, guint time
) {
647 if (info
!= ui::TEXT_PLAIN
) {
648 NOTIMPLEMENTED() << "Only support plain text drops for now, sorry!";
652 GURL
url(reinterpret_cast<const char*>(gtk_selection_data_get_data(data
)));
656 bool url_is_newtab
= url
.SchemeIs(chrome::kChromeUIScheme
) &&
657 url
.host() == chrome::kChromeUINewTabHost
;
658 home_page_is_new_tab_page_
.SetValue(url_is_newtab
);
660 home_page_
.SetValue(url
.spec());
663 bool BrowserToolbarGtk::ShouldOnlyShowLocation() const {
664 // If we're a popup window, only show the location bar (omnibox).
665 return !browser_
->is_type_tabbed();
668 void BrowserToolbarGtk::RebuildWrenchMenu() {
669 wrench_menu_model_
.reset(new WrenchMenuModel(this, browser_
, false));
670 wrench_menu_
.reset(new MenuGtk(this, wrench_menu_model_
.get()));
671 // The bookmark menu model needs to be able to force the wrench menu to close.
672 wrench_menu_model_
->bookmark_sub_menu_model()->SetMenuGtk(wrench_menu_
.get());
673 is_wrench_menu_model_valid_
= true;
676 gboolean
BrowserToolbarGtk::OnWrenchMenuButtonExpose(GtkWidget
* sender
,
677 GdkEventExpose
* expose
) {
678 TRACE_EVENT0("ui::gtk", "BrowserToolbarGtk::OnWrenchMenuButtonExpose");
680 if (UpgradeDetector::GetInstance()->notify_upgrade()) {
681 resource_id
= UpgradeDetector::GetInstance()->GetIconResourceID(
682 UpgradeDetector::UPGRADE_ICON_TYPE_BADGE
);
684 GlobalError
* error
= GlobalErrorServiceFactory::GetForProfile(
685 browser_
->profile())->GetHighestSeverityGlobalErrorWithWrenchMenuItem();
687 switch (error
->GetSeverity()) {
688 case GlobalError::SEVERITY_LOW
:
689 resource_id
= IDR_UPDATE_BADGE
;
691 case GlobalError::SEVERITY_MEDIUM
:
692 resource_id
= IDR_UPDATE_BADGE4
;
694 case GlobalError::SEVERITY_HIGH
:
695 resource_id
= IDR_UPDATE_BADGE3
;
704 GtkAllocation allocation
;
705 gtk_widget_get_allocation(sender
, &allocation
);
707 // Draw the chrome app menu icon onto the canvas.
708 const gfx::ImageSkia
* badge
= theme_service_
->GetImageSkiaNamed(resource_id
);
709 gfx::CanvasSkiaPaint
canvas(expose
, false);
710 int x_offset
= base::i18n::IsRTL() ? 0 : allocation
.width
- badge
->width();
712 canvas
.DrawImageInt(*badge
,
713 allocation
.x
+ x_offset
,
714 allocation
.y
+ y_offset
);
719 void BrowserToolbarGtk::UpdateShowHomeButton() {
720 bool visible
= show_home_button_
.GetValue() && !ShouldOnlyShowLocation();
721 gtk_widget_set_visible(home_
->widget(), visible
);