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