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