1 // Copyright (c) 2011 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_toolbar_gtk.h"
6
7 #include <X11/XF86keysym.h>
8 #include <gdk/gdkkeysyms.h>
9 #include <gtk/gtk.h>
10
11 #include "base/base_paths.h"
12 #include "base/command_line.h"
13 #include "base/i18n/rtl.h"
14 #include "base/logging.h"
15 #include "base/memory/singleton.h"
16 #include "base/path_service.h"
17 #include "chrome/app/chrome_command_ids.h"
18 #include "chrome/browser/metrics/user_metrics.h"
19 #include "chrome/browser/net/url_fixer_upper.h"
20 #include "chrome/browser/prefs/pref_service.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/themes/theme_service.h"
23 #include "chrome/browser/ui/browser.h"
24 #include "chrome/browser/ui/gtk/accelerators_gtk.h"
25 #include "chrome/browser/ui/gtk/back_forward_button_gtk.h"
26 #include "chrome/browser/ui/gtk/browser_actions_toolbar_gtk.h"
27 #include "chrome/browser/ui/gtk/browser_window_gtk.h"
28 #include "chrome/browser/ui/gtk/cairo_cached_surface.h"
29 #include "chrome/browser/ui/gtk/custom_button.h"
30 #include "chrome/browser/ui/gtk/gtk_chrome_button.h"
31 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
32 #include "chrome/browser/ui/gtk/gtk_util.h"
33 #include "chrome/browser/ui/gtk/location_bar_view_gtk.h"
34 #include "chrome/browser/ui/gtk/reload_button_gtk.h"
35 #include "chrome/browser/ui/gtk/rounded_window.h"
36 #include "chrome/browser/ui/gtk/tabs/tab_strip_gtk.h"
37 #include "chrome/browser/ui/gtk/view_id_util.h"
38 #include "chrome/browser/ui/toolbar/encoding_menu_controller.h"
39 #include "chrome/browser/upgrade_detector.h"
40 #include "chrome/common/pref_names.h"
41 #include "chrome/common/url_constants.h"
42 #include "content/browser/tab_contents/tab_contents.h"
43 #include "content/common/notification_details.h"
44 #include "content/common/notification_service.h"
45 #include "content/common/notification_type.h"
46 #include "grit/chromium_strings.h"
47 #include "grit/generated_resources.h"
48 #include "grit/theme_resources.h"
49 #include "ui/base/dragdrop/gtk_dnd_util.h"
50 #include "ui/base/l10n/l10n_util.h"
51 #include "ui/base/models/accelerator_gtk.h"
52 #include "ui/gfx/canvas_skia_paint.h"
53 #include "ui/gfx/gtk_util.h"
54 #include "ui/gfx/skbitmap_operations.h"
55
56 namespace {
57
58 // Padding on left and right of the left toolbar buttons (back, forward, reload,
59 // etc.).
60 const int kToolbarLeftAreaPadding = 4;
61
62 // Height of the toolbar in pixels (not counting padding).
63 const int kToolbarHeight = 29;
64
65 // Padding within the toolbar above the buttons and location bar.
66 const int kTopBottomPadding = 3;
67
68 // Height of the toolbar in pixels when we only show the location bar.
69 const int kToolbarHeightLocationBarOnly = kToolbarHeight - 2;
70
71 // Interior spacing between toolbar widgets.
72 const int kToolbarWidgetSpacing = 1;
73
74 // Amount of rounding on top corners of toolbar. Only used in Gtk theme mode.
75 const int kToolbarCornerSize = 3;
76
SetWidgetHeightRequest(GtkWidget * widget,gpointer user_data)77 void SetWidgetHeightRequest(GtkWidget* widget, gpointer user_data) {
78 gtk_widget_set_size_request(widget, -1, GPOINTER_TO_INT(user_data));
79 }
80
81 } // namespace
82
83 // BrowserToolbarGtk, public ---------------------------------------------------
84
BrowserToolbarGtk(Browser * browser,BrowserWindowGtk * window)85 BrowserToolbarGtk::BrowserToolbarGtk(Browser* browser, BrowserWindowGtk* window)
86 : toolbar_(NULL),
87 location_bar_(new LocationBarViewGtk(browser)),
88 model_(browser->toolbar_model()),
89 wrench_menu_model_(this, browser),
90 browser_(browser),
91 window_(window),
92 profile_(NULL) {
93 browser_->command_updater()->AddCommandObserver(IDC_BACK, this);
94 browser_->command_updater()->AddCommandObserver(IDC_FORWARD, this);
95 browser_->command_updater()->AddCommandObserver(IDC_HOME, this);
96 browser_->command_updater()->AddCommandObserver(IDC_BOOKMARK_PAGE, this);
97
98 registrar_.Add(this,
99 NotificationType::BROWSER_THEME_CHANGED,
100 NotificationService::AllSources());
101 registrar_.Add(this,
102 NotificationType::UPGRADE_RECOMMENDED,
103 NotificationService::AllSources());
104 }
105
~BrowserToolbarGtk()106 BrowserToolbarGtk::~BrowserToolbarGtk() {
107 browser_->command_updater()->RemoveCommandObserver(IDC_BACK, this);
108 browser_->command_updater()->RemoveCommandObserver(IDC_FORWARD, this);
109 browser_->command_updater()->RemoveCommandObserver(IDC_HOME, this);
110 browser_->command_updater()->RemoveCommandObserver(IDC_BOOKMARK_PAGE, this);
111
112 offscreen_entry_.Destroy();
113
114 wrench_menu_.reset();
115 }
116
Init(Profile * profile,GtkWindow * top_level_window)117 void BrowserToolbarGtk::Init(Profile* profile,
118 GtkWindow* top_level_window) {
119 // Make sure to tell the location bar the profile before calling its Init.
120 SetProfile(profile);
121
122 theme_service_ = GtkThemeService::GetFrom(profile);
123 offscreen_entry_.Own(gtk_entry_new());
124
125 show_home_button_.Init(prefs::kShowHomeButton, profile->GetPrefs(), this);
126 home_page_.Init(prefs::kHomePage, profile->GetPrefs(), this);
127 home_page_is_new_tab_page_.Init(prefs::kHomePageIsNewTabPage,
128 profile->GetPrefs(), this);
129
130 event_box_ = gtk_event_box_new();
131 // Make the event box transparent so themes can use transparent toolbar
132 // backgrounds.
133 if (!theme_service_->UseGtkTheme())
134 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_), FALSE);
135
136 toolbar_ = gtk_hbox_new(FALSE, 0);
137 alignment_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0);
138 UpdateForBookmarkBarVisibility(false);
139 g_signal_connect(alignment_, "expose-event",
140 G_CALLBACK(&OnAlignmentExposeThunk), this);
141 gtk_container_add(GTK_CONTAINER(event_box_), alignment_);
142 gtk_container_add(GTK_CONTAINER(alignment_), toolbar_);
143
144 toolbar_left_ = gtk_hbox_new(FALSE, kToolbarWidgetSpacing);
145
146 back_.reset(new BackForwardButtonGtk(browser_, false));
147 g_signal_connect(back_->widget(), "clicked",
148 G_CALLBACK(OnButtonClickThunk), this);
149 gtk_box_pack_start(GTK_BOX(toolbar_left_), back_->widget(), FALSE,
150 FALSE, 0);
151
152 forward_.reset(new BackForwardButtonGtk(browser_, true));
153 g_signal_connect(forward_->widget(), "clicked",
154 G_CALLBACK(OnButtonClickThunk), this);
155 gtk_box_pack_start(GTK_BOX(toolbar_left_), forward_->widget(), FALSE,
156 FALSE, 0);
157
158 reload_.reset(new ReloadButtonGtk(location_bar_.get(), browser_));
159 gtk_box_pack_start(GTK_BOX(toolbar_left_), reload_->widget(), FALSE, FALSE,
160 0);
161
162 home_.reset(new CustomDrawButton(GtkThemeService::GetFrom(profile_),
163 IDR_HOME, IDR_HOME_P, IDR_HOME_H, 0, GTK_STOCK_HOME,
164 GTK_ICON_SIZE_SMALL_TOOLBAR));
165 gtk_widget_set_tooltip_text(home_->widget(),
166 l10n_util::GetStringUTF8(IDS_TOOLTIP_HOME).c_str());
167 g_signal_connect(home_->widget(), "clicked",
168 G_CALLBACK(OnButtonClickThunk), this);
169 gtk_box_pack_start(GTK_BOX(toolbar_left_), home_->widget(), FALSE, FALSE,
170 kToolbarWidgetSpacing);
171 gtk_util::SetButtonTriggersNavigation(home_->widget());
172
173 gtk_box_pack_start(GTK_BOX(toolbar_), toolbar_left_, FALSE, FALSE,
174 kToolbarLeftAreaPadding);
175
176 location_hbox_ = gtk_hbox_new(FALSE, 0);
177 location_bar_->Init(ShouldOnlyShowLocation());
178 gtk_box_pack_start(GTK_BOX(location_hbox_), location_bar_->widget(), TRUE,
179 TRUE, 0);
180
181 g_signal_connect(location_hbox_, "expose-event",
182 G_CALLBACK(OnLocationHboxExposeThunk), this);
183 gtk_box_pack_start(GTK_BOX(toolbar_), location_hbox_, TRUE, TRUE,
184 ShouldOnlyShowLocation() ? 1 : 0);
185
186 if (!ShouldOnlyShowLocation()) {
187 actions_toolbar_.reset(new BrowserActionsToolbarGtk(browser_));
188 gtk_box_pack_start(GTK_BOX(toolbar_), actions_toolbar_->widget(),
189 FALSE, FALSE, 0);
190 }
191
192 wrench_menu_image_ = gtk_image_new_from_pixbuf(
193 theme_service_->GetRTLEnabledPixbufNamed(IDR_TOOLS));
194 wrench_menu_button_.reset(new CustomDrawButton(
195 GtkThemeService::GetFrom(profile_),
196 IDR_TOOLS, IDR_TOOLS_P, IDR_TOOLS_H, 0,
197 wrench_menu_image_));
198 GtkWidget* wrench_button = wrench_menu_button_->widget();
199
200 gtk_widget_set_tooltip_text(
201 wrench_button,
202 l10n_util::GetStringFUTF8(IDS_APPMENU_TOOLTIP,
203 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)).c_str());
204 g_signal_connect(wrench_button, "button-press-event",
205 G_CALLBACK(OnMenuButtonPressEventThunk), this);
206 GTK_WIDGET_UNSET_FLAGS(wrench_button, GTK_CAN_FOCUS);
207
208 // Put the wrench button in a box so that we can paint the update notification
209 // over it.
210 GtkWidget* wrench_box = gtk_alignment_new(0, 0, 1, 1);
211 g_signal_connect_after(wrench_box, "expose-event",
212 G_CALLBACK(OnWrenchMenuButtonExposeThunk), this);
213 gtk_container_add(GTK_CONTAINER(wrench_box), wrench_button);
214 gtk_box_pack_start(GTK_BOX(toolbar_), wrench_box, FALSE, FALSE, 4);
215
216 wrench_menu_.reset(new MenuGtk(this, &wrench_menu_model_));
217 registrar_.Add(this, NotificationType::ZOOM_LEVEL_CHANGED,
218 Source<Profile>(browser_->profile()));
219
220 if (ShouldOnlyShowLocation()) {
221 gtk_widget_show(event_box_);
222 gtk_widget_show(alignment_);
223 gtk_widget_show(toolbar_);
224 gtk_widget_show_all(location_hbox_);
225 gtk_widget_hide(reload_->widget());
226 } else {
227 gtk_widget_show_all(event_box_);
228 if (actions_toolbar_->button_count() == 0)
229 gtk_widget_hide(actions_toolbar_->widget());
230 }
231 // Initialize pref-dependent UI state.
232 NotifyPrefChanged(NULL);
233
234 // Because the above does a recursive show all on all widgets we need to
235 // update the icon visibility to hide them.
236 location_bar_->UpdateContentSettingsIcons();
237
238 SetViewIDs();
239 theme_service_->InitThemesFor(this);
240 }
241
SetViewIDs()242 void BrowserToolbarGtk::SetViewIDs() {
243 ViewIDUtil::SetID(widget(), VIEW_ID_TOOLBAR);
244 ViewIDUtil::SetID(back_->widget(), VIEW_ID_BACK_BUTTON);
245 ViewIDUtil::SetID(forward_->widget(), VIEW_ID_FORWARD_BUTTON);
246 ViewIDUtil::SetID(reload_->widget(), VIEW_ID_RELOAD_BUTTON);
247 ViewIDUtil::SetID(home_->widget(), VIEW_ID_HOME_BUTTON);
248 ViewIDUtil::SetID(location_bar_->widget(), VIEW_ID_LOCATION_BAR);
249 ViewIDUtil::SetID(wrench_menu_button_->widget(), VIEW_ID_APP_MENU);
250 }
251
Show()252 void BrowserToolbarGtk::Show() {
253 gtk_widget_show(toolbar_);
254 }
255
Hide()256 void BrowserToolbarGtk::Hide() {
257 gtk_widget_hide(toolbar_);
258 }
259
GetLocationBar() const260 LocationBar* BrowserToolbarGtk::GetLocationBar() const {
261 return location_bar_.get();
262 }
263
UpdateForBookmarkBarVisibility(bool show_bottom_padding)264 void BrowserToolbarGtk::UpdateForBookmarkBarVisibility(
265 bool show_bottom_padding) {
266 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment_),
267 ShouldOnlyShowLocation() ? 0 : kTopBottomPadding,
268 !show_bottom_padding || ShouldOnlyShowLocation() ? 0 : kTopBottomPadding,
269 0, 0);
270 }
271
ShowAppMenu()272 void BrowserToolbarGtk::ShowAppMenu() {
273 wrench_menu_->Cancel();
274 wrench_menu_button_->SetPaintOverride(GTK_STATE_ACTIVE);
275 UserMetrics::RecordAction(UserMetricsAction("ShowAppMenu"));
276 wrench_menu_->PopupAsFromKeyEvent(wrench_menu_button_->widget());
277 }
278
279 // CommandUpdater::CommandObserver ---------------------------------------------
280
EnabledStateChangedForCommand(int id,bool enabled)281 void BrowserToolbarGtk::EnabledStateChangedForCommand(int id, bool enabled) {
282 GtkWidget* widget = NULL;
283 switch (id) {
284 case IDC_BACK:
285 widget = back_->widget();
286 break;
287 case IDC_FORWARD:
288 widget = forward_->widget();
289 break;
290 case IDC_HOME:
291 if (home_.get())
292 widget = home_->widget();
293 break;
294 }
295 if (widget) {
296 if (!enabled && GTK_WIDGET_STATE(widget) == GTK_STATE_PRELIGHT) {
297 // If we're disabling a widget, GTK will helpfully restore it to its
298 // previous state when we re-enable it, even if that previous state
299 // is the prelight. This looks bad. See the bug for a simple repro.
300 // http://code.google.com/p/chromium/issues/detail?id=13729
301 gtk_widget_set_state(widget, GTK_STATE_NORMAL);
302 }
303 gtk_widget_set_sensitive(widget, enabled);
304 }
305 }
306
307 // MenuGtk::Delegate -----------------------------------------------------------
308
StoppedShowing()309 void BrowserToolbarGtk::StoppedShowing() {
310 // Without these calls, the hover state can get stuck since the leave-notify
311 // event is not sent when clicking a button brings up the menu.
312 gtk_chrome_button_set_hover_state(
313 GTK_CHROME_BUTTON(wrench_menu_button_->widget()), 0.0);
314 wrench_menu_button_->UnsetPaintOverride();
315 }
316
GetIconSetForId(int idr)317 GtkIconSet* BrowserToolbarGtk::GetIconSetForId(int idr) {
318 return theme_service_->GetIconSetForId(idr);
319 }
320
321 // Always show images because we desire that some icons always show
322 // regardless of the system setting.
AlwaysShowIconForCmd(int command_id) const323 bool BrowserToolbarGtk::AlwaysShowIconForCmd(int command_id) const {
324 return command_id == IDC_UPGRADE_DIALOG;
325 }
326
327 // ui::AcceleratorProvider
328
GetAcceleratorForCommandId(int id,ui::Accelerator * accelerator)329 bool BrowserToolbarGtk::GetAcceleratorForCommandId(
330 int id,
331 ui::Accelerator* accelerator) {
332 const ui::AcceleratorGtk* accelerator_gtk =
333 AcceleratorsGtk::GetInstance()->GetPrimaryAcceleratorForCommand(id);
334 if (accelerator_gtk)
335 *accelerator = *accelerator_gtk;
336 return !!accelerator_gtk;
337 }
338
339 // NotificationObserver --------------------------------------------------------
340
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)341 void BrowserToolbarGtk::Observe(NotificationType type,
342 const NotificationSource& source,
343 const NotificationDetails& details) {
344 if (type == NotificationType::PREF_CHANGED) {
345 NotifyPrefChanged(Details<std::string>(details).ptr());
346 } else if (type == NotificationType::BROWSER_THEME_CHANGED) {
347 // Update the spacing around the menu buttons
348 bool use_gtk = theme_service_->UseGtkTheme();
349 int border = use_gtk ? 0 : 2;
350 gtk_container_set_border_width(
351 GTK_CONTAINER(wrench_menu_button_->widget()), border);
352
353 // Force the height of the toolbar so we get the right amount of padding
354 // above and below the location bar. We always force the size of the widgets
355 // to either side of the location box, but we only force the location box
356 // size in chrome-theme mode because that's the only time we try to control
357 // the font size.
358 int toolbar_height = ShouldOnlyShowLocation() ?
359 kToolbarHeightLocationBarOnly : kToolbarHeight;
360 gtk_container_foreach(GTK_CONTAINER(toolbar_), SetWidgetHeightRequest,
361 GINT_TO_POINTER(toolbar_height));
362 gtk_widget_set_size_request(location_hbox_, -1,
363 use_gtk ? -1 : toolbar_height);
364
365 // When using the GTK+ theme, we need to have the event box be visible so
366 // buttons don't get a halo color from the background. When using Chromium
367 // themes, we want to let the background show through the toolbar.
368 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_), use_gtk);
369
370 if (use_gtk) {
371 // We need to manually update the icon if we are in GTK mode. (Note that
372 // we set the initial value in Init()).
373 gtk_image_set_from_pixbuf(
374 GTK_IMAGE(wrench_menu_image_),
375 theme_service_->GetRTLEnabledPixbufNamed(IDR_TOOLS));
376 }
377
378 UpdateRoundedness();
379 } else if (type == NotificationType::UPGRADE_RECOMMENDED) {
380 // Redraw the wrench menu to update the badge.
381 gtk_widget_queue_draw(wrench_menu_button_->widget());
382 } else if (type == NotificationType::ZOOM_LEVEL_CHANGED) {
383 // If our zoom level changed, we need to tell the menu to update its state,
384 // since the menu could still be open.
385 wrench_menu_->UpdateMenu();
386 } else {
387 NOTREACHED();
388 }
389 }
390
391 // BrowserToolbarGtk, public ---------------------------------------------------
392
SetProfile(Profile * profile)393 void BrowserToolbarGtk::SetProfile(Profile* profile) {
394 if (profile == profile_)
395 return;
396
397 profile_ = profile;
398 location_bar_->SetProfile(profile);
399 }
400
UpdateTabContents(TabContents * contents,bool should_restore_state)401 void BrowserToolbarGtk::UpdateTabContents(TabContents* contents,
402 bool should_restore_state) {
403 location_bar_->Update(should_restore_state ? contents : NULL);
404
405 if (actions_toolbar_.get())
406 actions_toolbar_->Update();
407 }
408
409 // BrowserToolbarGtk, private --------------------------------------------------
410
SetUpDragForHomeButton(bool enable)411 void BrowserToolbarGtk::SetUpDragForHomeButton(bool enable) {
412 if (enable) {
413 gtk_drag_dest_set(home_->widget(), GTK_DEST_DEFAULT_ALL,
414 NULL, 0, GDK_ACTION_COPY);
415 static const int targets[] = { ui::TEXT_PLAIN, ui::TEXT_URI_LIST, -1 };
416 ui::SetDestTargetList(home_->widget(), targets);
417
418 drop_handler_.reset(new ui::GtkSignalRegistrar());
419 drop_handler_->Connect(home_->widget(), "drag-data-received",
420 G_CALLBACK(OnDragDataReceivedThunk), this);
421 } else {
422 gtk_drag_dest_unset(home_->widget());
423 drop_handler_.reset(NULL);
424 }
425 }
426
UpdateRoundedness()427 bool BrowserToolbarGtk::UpdateRoundedness() {
428 // We still round the corners if we are in chrome theme mode, but we do it by
429 // drawing theme resources rather than changing the physical shape of the
430 // widget.
431 bool should_be_rounded = theme_service_->UseGtkTheme() &&
432 window_->ShouldDrawContentDropShadow();
433
434 if (should_be_rounded == gtk_util::IsActingAsRoundedWindow(alignment_))
435 return false;
436
437 if (should_be_rounded) {
438 gtk_util::ActAsRoundedWindow(alignment_, GdkColor(), kToolbarCornerSize,
439 gtk_util::ROUNDED_TOP,
440 gtk_util::BORDER_NONE);
441 } else {
442 gtk_util::StopActingAsRoundedWindow(alignment_);
443 }
444
445 return true;
446 }
447
OnAlignmentExpose(GtkWidget * widget,GdkEventExpose * e)448 gboolean BrowserToolbarGtk::OnAlignmentExpose(GtkWidget* widget,
449 GdkEventExpose* e) {
450 // We may need to update the roundedness of the toolbar's top corners. In
451 // this case, don't draw; we'll be called again soon enough.
452 if (UpdateRoundedness())
453 return TRUE;
454
455 // We don't need to render the toolbar image in GTK mode.
456 if (theme_service_->UseGtkTheme())
457 return FALSE;
458
459 cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(widget->window));
460 gdk_cairo_rectangle(cr, &e->area);
461 cairo_clip(cr);
462
463 gfx::Point tabstrip_origin =
464 window_->tabstrip()->GetTabStripOriginForWidget(widget);
465 // Fill the entire region with the toolbar color.
466 GdkColor color = theme_service_->GetGdkColor(
467 ThemeService::COLOR_TOOLBAR);
468 gdk_cairo_set_source_color(cr, &color);
469 cairo_fill(cr);
470
471 // The horizontal size of the top left and right corner images.
472 const int kCornerWidth = 4;
473 // The thickness of the shadow outside the toolbar's bounds; the offset
474 // between the edge of the toolbar and where we anchor the corner images.
475 const int kShadowThickness = 2;
476
477 gfx::Rect area(e->area);
478 gfx::Rect right(widget->allocation.x + widget->allocation.width -
479 kCornerWidth,
480 widget->allocation.y - kShadowThickness,
481 kCornerWidth,
482 widget->allocation.height + kShadowThickness);
483 gfx::Rect left(widget->allocation.x - kShadowThickness,
484 widget->allocation.y - kShadowThickness,
485 kCornerWidth,
486 widget->allocation.height + kShadowThickness);
487
488 if (window_->ShouldDrawContentDropShadow()) {
489 // Leave room to draw rounded corners.
490 area = area.Subtract(right).Subtract(left);
491 }
492
493 CairoCachedSurface* background = theme_service_->GetSurfaceNamed(
494 IDR_THEME_TOOLBAR, widget);
495 background->SetSource(cr, tabstrip_origin.x(), tabstrip_origin.y());
496 cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT);
497 cairo_rectangle(cr, area.x(), area.y(), area.width(), area.height());
498 cairo_fill(cr);
499
500 if (!window_->ShouldDrawContentDropShadow()) {
501 // The rest of this function is for rounded corners. Our work is done here.
502 cairo_destroy(cr);
503 return FALSE;
504 }
505
506 bool draw_left_corner = left.Intersects(gfx::Rect(e->area));
507 bool draw_right_corner = right.Intersects(gfx::Rect(e->area));
508
509 if (draw_left_corner || draw_right_corner) {
510 // Create a mask which is composed of the left and/or right corners.
511 cairo_surface_t* target = cairo_surface_create_similar(
512 cairo_get_target(cr),
513 CAIRO_CONTENT_COLOR_ALPHA,
514 widget->allocation.x + widget->allocation.width,
515 widget->allocation.y + widget->allocation.height);
516 cairo_t* copy_cr = cairo_create(target);
517
518 cairo_set_operator(copy_cr, CAIRO_OPERATOR_SOURCE);
519 if (draw_left_corner) {
520 CairoCachedSurface* left_corner = theme_service_->GetSurfaceNamed(
521 IDR_CONTENT_TOP_LEFT_CORNER_MASK, widget);
522 left_corner->SetSource(copy_cr, left.x(), left.y());
523 cairo_paint(copy_cr);
524 }
525 if (draw_right_corner) {
526 CairoCachedSurface* right_corner = theme_service_->GetSurfaceNamed(
527 IDR_CONTENT_TOP_RIGHT_CORNER_MASK, widget);
528 right_corner->SetSource(copy_cr, right.x(), right.y());
529 // We fill a path rather than just painting because we don't want to
530 // overwrite the left corner.
531 cairo_rectangle(copy_cr, right.x(), right.y(),
532 right.width(), right.height());
533 cairo_fill(copy_cr);
534 }
535
536 // Draw the background. CAIRO_OPERATOR_IN uses the existing pixel data as
537 // an alpha mask.
538 background->SetSource(copy_cr, tabstrip_origin.x(), tabstrip_origin.y());
539 cairo_set_operator(copy_cr, CAIRO_OPERATOR_IN);
540 cairo_pattern_set_extend(cairo_get_source(copy_cr), CAIRO_EXTEND_REPEAT);
541 cairo_paint(copy_cr);
542 cairo_destroy(copy_cr);
543
544 // Copy the temporary surface to the screen.
545 cairo_set_source_surface(cr, target, 0, 0);
546 cairo_paint(cr);
547 cairo_surface_destroy(target);
548 }
549
550 cairo_destroy(cr);
551
552 return FALSE; // Allow subwidgets to paint.
553 }
554
OnLocationHboxExpose(GtkWidget * location_hbox,GdkEventExpose * e)555 gboolean BrowserToolbarGtk::OnLocationHboxExpose(GtkWidget* location_hbox,
556 GdkEventExpose* e) {
557 if (theme_service_->UseGtkTheme()) {
558 gtk_util::DrawTextEntryBackground(offscreen_entry_.get(),
559 location_hbox, &e->area,
560 &location_hbox->allocation);
561 }
562
563 return FALSE;
564 }
565
OnButtonClick(GtkWidget * button)566 void BrowserToolbarGtk::OnButtonClick(GtkWidget* button) {
567 if ((button == back_->widget()) || (button == forward_->widget())) {
568 if (gtk_util::DispositionForCurrentButtonPressEvent() == CURRENT_TAB)
569 location_bar_->Revert();
570 return;
571 }
572
573 DCHECK(home_.get() && button == home_->widget()) <<
574 "Unexpected button click callback";
575 browser_->Home(gtk_util::DispositionForCurrentButtonPressEvent());
576 }
577
OnMenuButtonPressEvent(GtkWidget * button,GdkEventButton * event)578 gboolean BrowserToolbarGtk::OnMenuButtonPressEvent(GtkWidget* button,
579 GdkEventButton* event) {
580 if (event->button != 1)
581 return FALSE;
582
583 wrench_menu_button_->SetPaintOverride(GTK_STATE_ACTIVE);
584 wrench_menu_->PopupForWidget(button, event->button, event->time);
585
586 return TRUE;
587 }
588
OnDragDataReceived(GtkWidget * widget,GdkDragContext * drag_context,gint x,gint y,GtkSelectionData * data,guint info,guint time)589 void BrowserToolbarGtk::OnDragDataReceived(GtkWidget* widget,
590 GdkDragContext* drag_context, gint x, gint y,
591 GtkSelectionData* data, guint info, guint time) {
592 if (info != ui::TEXT_PLAIN) {
593 NOTIMPLEMENTED() << "Only support plain text drops for now, sorry!";
594 return;
595 }
596
597 GURL url(reinterpret_cast<char*>(data->data));
598 if (!url.is_valid())
599 return;
600
601 bool url_is_newtab = url.spec() == chrome::kChromeUINewTabURL;
602 home_page_is_new_tab_page_.SetValue(url_is_newtab);
603 if (!url_is_newtab)
604 home_page_.SetValue(url.spec());
605 }
606
NotifyPrefChanged(const std::string * pref)607 void BrowserToolbarGtk::NotifyPrefChanged(const std::string* pref) {
608 if (!pref || *pref == prefs::kShowHomeButton) {
609 if (show_home_button_.GetValue() && !ShouldOnlyShowLocation()) {
610 gtk_widget_show(home_->widget());
611 } else {
612 gtk_widget_hide(home_->widget());
613 }
614 }
615
616 if (!pref ||
617 *pref == prefs::kHomePage ||
618 *pref == prefs::kHomePageIsNewTabPage)
619 SetUpDragForHomeButton(!home_page_.IsManaged() &&
620 !home_page_is_new_tab_page_.IsManaged());
621 }
622
ShouldOnlyShowLocation() const623 bool BrowserToolbarGtk::ShouldOnlyShowLocation() const {
624 // If we're a popup window, only show the location bar (omnibox).
625 return browser_->type() != Browser::TYPE_NORMAL;
626 }
627
OnWrenchMenuButtonExpose(GtkWidget * sender,GdkEventExpose * expose)628 gboolean BrowserToolbarGtk::OnWrenchMenuButtonExpose(GtkWidget* sender,
629 GdkEventExpose* expose) {
630 const SkBitmap* badge = NULL;
631 if (UpgradeDetector::GetInstance()->notify_upgrade()) {
632 badge = theme_service_->GetBitmapNamed(IDR_UPDATE_BADGE);
633 } else {
634 return FALSE;
635 }
636
637 // Draw the chrome app menu icon onto the canvas.
638 gfx::CanvasSkiaPaint canvas(expose, false);
639 int x_offset = base::i18n::IsRTL() ? 0 :
640 sender->allocation.width - badge->width();
641 int y_offset = 0;
642 canvas.DrawBitmapInt(
643 *badge,
644 sender->allocation.x + x_offset,
645 sender->allocation.y + y_offset);
646
647 return FALSE;
648 }
649