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/back_forward_button_gtk.h" 6 7 #include <gtk/gtk.h> 8 9 #include "base/message_loop.h" 10 #include "chrome/app/chrome_command_ids.h" 11 #include "chrome/browser/profiles/profile.h" 12 #include "chrome/browser/ui/browser.h" 13 #include "chrome/browser/ui/gtk/gtk_theme_service.h" 14 #include "chrome/browser/ui/gtk/gtk_util.h" 15 #include "chrome/browser/ui/gtk/menu_gtk.h" 16 #include "chrome/browser/ui/toolbar/back_forward_menu_model.h" 17 #include "grit/generated_resources.h" 18 #include "grit/theme_resources.h" 19 #include "ui/base/l10n/l10n_util.h" 20 21 // The time in milliseconds between when the user clicks and the menu appears. 22 static const int kMenuTimerDelay = 500; 23 24 BackForwardButtonGtk::BackForwardButtonGtk(Browser* browser, bool is_forward) 25 : browser_(browser), 26 is_forward_(is_forward), 27 show_menu_factory_(this) { 28 int normal, pushed, hover, disabled, tooltip; 29 const char* stock; 30 if (is_forward) { 31 normal = IDR_FORWARD; 32 pushed = IDR_FORWARD_P; 33 hover = IDR_FORWARD_H; 34 disabled = IDR_FORWARD_D; 35 tooltip = IDS_TOOLTIP_FORWARD; 36 stock = GTK_STOCK_GO_FORWARD; 37 } else { 38 normal = IDR_BACK; 39 pushed = IDR_BACK_P; 40 hover = IDR_BACK_H; 41 disabled = IDR_BACK_D; 42 tooltip = IDS_TOOLTIP_BACK; 43 stock = GTK_STOCK_GO_BACK; 44 } 45 button_.reset(new CustomDrawButton( 46 GtkThemeService::GetFrom(browser_->profile()), 47 normal, pushed, hover, disabled, stock, GTK_ICON_SIZE_SMALL_TOOLBAR)); 48 gtk_widget_set_tooltip_text(widget(), 49 l10n_util::GetStringUTF8(tooltip).c_str()); 50 menu_model_.reset(new BackForwardMenuModel(browser, is_forward ? 51 BackForwardMenuModel::FORWARD_MENU : 52 BackForwardMenuModel::BACKWARD_MENU)); 53 54 g_signal_connect(widget(), "clicked", 55 G_CALLBACK(OnClickThunk), this); 56 g_signal_connect(widget(), "button-press-event", 57 G_CALLBACK(OnButtonPressThunk), this); 58 gtk_widget_add_events(widget(), GDK_POINTER_MOTION_MASK); 59 g_signal_connect(widget(), "motion-notify-event", 60 G_CALLBACK(OnMouseMoveThunk), this); 61 62 // Popup the menu as left-aligned relative to this widget rather than the 63 // default of right aligned. 64 g_object_set_data(G_OBJECT(widget()), "left-align-popup", 65 reinterpret_cast<void*>(true)); 66 67 gtk_util::SetButtonTriggersNavigation(widget()); 68 } 69 70 BackForwardButtonGtk::~BackForwardButtonGtk() { 71 } 72 73 void BackForwardButtonGtk::StoppedShowing() { 74 button_->UnsetPaintOverride(); 75 } 76 77 bool BackForwardButtonGtk::AlwaysShowIconForCmd(int command_id) const { 78 return true; 79 } 80 81 void BackForwardButtonGtk::ShowBackForwardMenu(int button, guint32 event_time) { 82 menu_.reset(new MenuGtk(this, menu_model_.get())); 83 button_->SetPaintOverride(GTK_STATE_ACTIVE); 84 menu_->PopupForWidget(widget(), button, event_time); 85 } 86 87 void BackForwardButtonGtk::OnClick(GtkWidget* widget) { 88 show_menu_factory_.RevokeAll(); 89 90 browser_->ExecuteCommandWithDisposition( 91 is_forward_ ? IDC_FORWARD : IDC_BACK, 92 gtk_util::DispositionForCurrentButtonPressEvent()); 93 } 94 95 gboolean BackForwardButtonGtk::OnButtonPress(GtkWidget* widget, 96 GdkEventButton* event) { 97 if (event->button == 3) 98 ShowBackForwardMenu(event->button, event->time); 99 100 if (event->button != 1) 101 return FALSE; 102 103 y_position_of_last_press_ = static_cast<int>(event->y); 104 MessageLoop::current()->PostDelayedTask(FROM_HERE, 105 show_menu_factory_.NewRunnableMethod( 106 &BackForwardButtonGtk::ShowBackForwardMenu, 107 event->button, event->time), 108 kMenuTimerDelay); 109 return FALSE; 110 } 111 112 gboolean BackForwardButtonGtk::OnMouseMove(GtkWidget* widget, 113 GdkEventMotion* event) { 114 // If we aren't waiting to show the back forward menu, do nothing. 115 if (show_menu_factory_.empty()) 116 return FALSE; 117 118 // We only count moves about a certain threshold. 119 GtkSettings* settings = gtk_widget_get_settings(widget); 120 int drag_min_distance; 121 g_object_get(settings, "gtk-dnd-drag-threshold", &drag_min_distance, NULL); 122 if (event->y - y_position_of_last_press_ < drag_min_distance) 123 return FALSE; 124 125 // We will show the menu now. Cancel the delayed event. 126 show_menu_factory_.RevokeAll(); 127 ShowBackForwardMenu(/* button */ 1, event->time); 128 return FALSE; 129 } 130