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