reload_button.cc revision 23730a6e56a168d1879203e4b3819bb36e3d8f1f
1// Copyright 2013 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/views/toolbar/reload_button.h"
6
7#include "base/strings/utf_string_conversions.h"
8#include "chrome/app/chrome_command_ids.h"
9#include "chrome/browser/command_updater.h"
10#include "chrome/browser/search/search.h"
11#include "chrome/browser/ui/search/search_model.h"
12#include "chrome/browser/ui/views/location_bar/location_bar_view.h"
13#include "grit/generated_resources.h"
14#include "grit/theme_resources.h"
15#include "ui/base/l10n/l10n_util.h"
16#include "ui/base/models/simple_menu_model.h"
17#include "ui/base/theme_provider.h"
18#include "ui/base/window_open_disposition.h"
19#include "ui/views/metrics.h"
20#include "ui/views/widget/widget.h"
21
22
23namespace {
24
25// Contents of the Reload drop-down menu.
26const int kReloadMenuItems[]  = {
27  IDS_RELOAD_MENU_NORMAL_RELOAD_ITEM,
28  IDS_RELOAD_MENU_HARD_RELOAD_ITEM,
29  IDS_RELOAD_MENU_EMPTY_AND_HARD_RELOAD_ITEM,
30};
31
32}  // namespace
33
34
35// ReloadButton ---------------------------------------------------------------
36
37// static
38const char ReloadButton::kViewClassName[] = "ReloadButton";
39
40ReloadButton::ReloadButton(LocationBarView* location_bar,
41                           CommandUpdater* command_updater)
42    : ToolbarButton(this, CreateMenuModel()),
43      location_bar_(location_bar),
44      command_updater_(command_updater),
45      intended_mode_(MODE_RELOAD),
46      visible_mode_(MODE_RELOAD),
47      double_click_timer_delay_(
48          base::TimeDelta::FromMilliseconds(views::GetDoubleClickInterval())),
49      stop_to_reload_timer_delay_(base::TimeDelta::FromMilliseconds(1350)),
50      menu_enabled_(false),
51      testing_mouse_hovered_(false),
52      testing_reload_count_(0) {
53}
54
55ReloadButton::~ReloadButton() {
56}
57
58void ReloadButton::ChangeMode(Mode mode, bool force) {
59  intended_mode_ = mode;
60
61  // If the change is forced, or the user isn't hovering the icon, or it's safe
62  // to change it to the other image type, make the change immediately;
63  // otherwise we'll let it happen later.
64  if (force || (!IsMouseHovered() && !testing_mouse_hovered_) ||
65      ((mode == MODE_STOP) ?
66      !double_click_timer_.IsRunning() : (visible_mode_ != MODE_STOP))) {
67    double_click_timer_.Stop();
68    stop_to_reload_timer_.Stop();
69    if (mode != visible_mode_)
70      ChangeModeInternal(mode);
71    SetEnabled(true);
72
73  // We want to disable the button if we're preventing a change from stop to
74  // reload due to hovering, but not if we're preventing a change from reload to
75  // stop due to the double-click timer running.  (Disabled reload state is only
76  // applicable when instant extended API is enabled and mode is NTP, which is
77  // handled just above.)
78  } else if (visible_mode_ != MODE_RELOAD) {
79    SetEnabled(false);
80
81    // Go ahead and change to reload after a bit, which allows repeated reloads
82    // without moving the mouse.
83    if (!stop_to_reload_timer_.IsRunning()) {
84      stop_to_reload_timer_.Start(FROM_HERE, stop_to_reload_timer_delay_, this,
85                                  &ReloadButton::OnStopToReloadTimer);
86    }
87  }
88}
89
90void ReloadButton::LoadImages() {
91  ChangeModeInternal(visible_mode_);
92
93  SchedulePaint();
94  PreferredSizeChanged();
95}
96
97void ReloadButton::OnMouseExited(const ui::MouseEvent& event) {
98  ToolbarButton::OnMouseExited(event);
99  if (!IsMenuShowing())
100    ChangeMode(intended_mode_, true);
101}
102
103bool ReloadButton::GetTooltipText(const gfx::Point& p,
104                                  base::string16* tooltip) const {
105  int reload_tooltip = menu_enabled_ ?
106      IDS_TOOLTIP_RELOAD_WITH_MENU : IDS_TOOLTIP_RELOAD;
107  int text_id = (visible_mode_ == MODE_RELOAD) ?
108      reload_tooltip : IDS_TOOLTIP_STOP;
109  tooltip->assign(l10n_util::GetStringUTF16(text_id));
110  return true;
111}
112
113const char* ReloadButton::GetClassName() const {
114  return kViewClassName;
115}
116
117void ReloadButton::GetAccessibleState(ui::AXViewState* state) {
118  if (menu_enabled_)
119    ToolbarButton::GetAccessibleState(state);
120  else
121    CustomButton::GetAccessibleState(state);
122}
123
124bool ReloadButton::ShouldShowMenu() {
125  return menu_enabled_ && (visible_mode_ == MODE_RELOAD);
126}
127
128void ReloadButton::ShowDropDownMenu(ui::MenuSourceType source_type) {
129  ToolbarButton::ShowDropDownMenu(source_type);  // Blocks.
130  ChangeMode(intended_mode_, true);
131}
132
133void ReloadButton::ButtonPressed(views::Button* /* button */,
134                                 const ui::Event& event) {
135  ClearPendingMenu();
136
137  if (visible_mode_ == MODE_STOP) {
138    if (command_updater_)
139      command_updater_->ExecuteCommandWithDisposition(IDC_STOP, CURRENT_TAB);
140    // The user has clicked, so we can feel free to update the button,
141    // even if the mouse is still hovering.
142    ChangeMode(MODE_RELOAD, true);
143  } else if (!double_click_timer_.IsRunning()) {
144    // Shift-clicking or ctrl-clicking the reload button means we should ignore
145    // any cached content.
146    int command;
147    int flags = event.flags();
148    if (event.IsShiftDown() || event.IsControlDown()) {
149      command = IDC_RELOAD_IGNORING_CACHE;
150      // Mask off Shift and Control so they don't affect the disposition below.
151      flags &= ~(ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN);
152    } else {
153      command = IDC_RELOAD;
154    }
155
156    // Start a timer - while this timer is running, the reload button cannot be
157    // changed to a stop button.  We do not set |intended_mode_| to MODE_STOP
158    // here as the browser will do that when it actually starts loading (which
159    // may happen synchronously, thus the need to do this before telling the
160    // browser to execute the reload command).
161    double_click_timer_.Start(FROM_HERE, double_click_timer_delay_, this,
162                              &ReloadButton::OnDoubleClickTimer);
163
164    ExecuteBrowserCommand(command, flags);
165    ++testing_reload_count_;
166  }
167}
168
169bool ReloadButton::IsCommandIdChecked(int command_id) const {
170  return false;
171}
172
173bool ReloadButton::IsCommandIdEnabled(int command_id) const {
174  return true;
175}
176
177bool ReloadButton::IsCommandIdVisible(int command_id) const {
178  return true;
179}
180
181bool ReloadButton::GetAcceleratorForCommandId(int command_id,
182    ui::Accelerator* accelerator) {
183  switch (command_id) {
184    case IDS_RELOAD_MENU_NORMAL_RELOAD_ITEM:
185      GetWidget()->GetAccelerator(IDC_RELOAD, accelerator);
186      return true;
187    case IDS_RELOAD_MENU_HARD_RELOAD_ITEM:
188      GetWidget()->GetAccelerator(IDC_RELOAD_IGNORING_CACHE, accelerator);
189      return true;
190  }
191  return GetWidget()->GetAccelerator(command_id, accelerator);
192}
193
194void ReloadButton::ExecuteCommand(int command_id, int event_flags) {
195  int browser_command = 0;
196  switch (command_id) {
197    case IDS_RELOAD_MENU_NORMAL_RELOAD_ITEM:
198      browser_command = IDC_RELOAD;
199      break;
200    case IDS_RELOAD_MENU_HARD_RELOAD_ITEM:
201      browser_command = IDC_RELOAD_IGNORING_CACHE;
202      break;
203    case IDS_RELOAD_MENU_EMPTY_AND_HARD_RELOAD_ITEM:
204      browser_command = IDC_RELOAD_CLEARING_CACHE;
205      break;
206    default:
207      NOTREACHED();
208  }
209  ExecuteBrowserCommand(browser_command, event_flags);
210}
211
212ui::SimpleMenuModel* ReloadButton::CreateMenuModel() {
213  ui::SimpleMenuModel* menu_model = new ui::SimpleMenuModel(this);
214  for (size_t i = 0; i < arraysize(kReloadMenuItems); ++i)
215    menu_model->AddItemWithStringId(kReloadMenuItems[i], kReloadMenuItems[i]);
216
217  return menu_model;
218}
219
220void ReloadButton::ExecuteBrowserCommand(int command, int event_flags) {
221  if (!command_updater_)
222    return;
223  command_updater_->ExecuteCommandWithDisposition(
224      command, ui::DispositionFromEventFlags(event_flags));
225}
226
227void ReloadButton::ChangeModeInternal(Mode mode) {
228  ui::ThemeProvider* tp = GetThemeProvider();
229  // |tp| can be NULL in unit tests.
230  if (tp) {
231    SetImage(views::Button::STATE_NORMAL, *(tp->GetImageSkiaNamed(
232        (mode == MODE_RELOAD) ? IDR_RELOAD : IDR_STOP)));
233    SetImage(views::Button::STATE_DISABLED, *(tp->GetImageSkiaNamed(
234        (mode == MODE_RELOAD) ? IDR_RELOAD_D : IDR_STOP_D)));
235  }
236
237  visible_mode_ = mode;
238  SchedulePaint();
239}
240
241void ReloadButton::OnDoubleClickTimer() {
242  if (!IsMenuShowing())
243    ChangeMode(intended_mode_, false);
244}
245
246void ReloadButton::OnStopToReloadTimer() {
247  DCHECK(!IsMenuShowing());
248  ChangeMode(intended_mode_, true);
249}
250