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