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
BackForwardButtonGtk(Browser * browser,bool is_forward)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
~BackForwardButtonGtk()70 BackForwardButtonGtk::~BackForwardButtonGtk() {
71 }
72
StoppedShowing()73 void BackForwardButtonGtk::StoppedShowing() {
74 button_->UnsetPaintOverride();
75 }
76
AlwaysShowIconForCmd(int command_id) const77 bool BackForwardButtonGtk::AlwaysShowIconForCmd(int command_id) const {
78 return true;
79 }
80
ShowBackForwardMenu(int button,guint32 event_time)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
OnClick(GtkWidget * widget)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
OnButtonPress(GtkWidget * widget,GdkEventButton * event)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
OnMouseMove(GtkWidget * widget,GdkEventMotion * event)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