Home | History | Annotate | Download | only in views
      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/views/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 
     23 namespace {
     24 
     25 const int kReloadImages[] =
     26     { IDR_RELOAD, IDR_RELOAD_H, IDR_RELOAD_P, IDR_RELOAD_D };
     27 
     28 const int kStopImages[] = { IDR_STOP, IDR_STOP_H, IDR_STOP_P, IDR_STOP_D };
     29 
     30 // Contents of the Reload drop-down menu.
     31 const 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
     43 const char ReloadButton::kViewClassName[] = "ReloadButton";
     44 
     45 ReloadButton::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 
     60 ReloadButton::~ReloadButton() {
     61 }
     62 
     63 void 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 
     94 void ReloadButton::LoadImages(ui::ThemeProvider* tp) {
     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   for (int i = 0; i < STATE_COUNT; i++) {
    104     reload_images[i] = *(tp->GetImageSkiaNamed(kReloadImages[i]));
    105     stop_images[i] = *(tp->GetImageSkiaNamed(kStopImages[i]));
    106   }
    107 
    108   SchedulePaint();
    109   PreferredSizeChanged();
    110 }
    111 
    112 void ReloadButton::OnMouseExited(const ui::MouseEvent& event) {
    113   ButtonDropDown::OnMouseExited(event);
    114   if (!IsMenuShowing())
    115     ChangeMode(intended_mode_, true);
    116 }
    117 
    118 bool ReloadButton::GetTooltipText(const gfx::Point& p,
    119                                   string16* tooltip) const {
    120   int reload_tooltip = menu_enabled_ ?
    121       IDS_TOOLTIP_RELOAD_WITH_MENU : IDS_TOOLTIP_RELOAD;
    122   int text_id = (visible_mode_ == MODE_RELOAD) ?
    123       reload_tooltip : IDS_TOOLTIP_STOP;
    124   tooltip->assign(l10n_util::GetStringUTF16(text_id));
    125   return true;
    126 }
    127 
    128 const char* ReloadButton::GetClassName() const {
    129   return kViewClassName;
    130 }
    131 
    132 void ReloadButton::GetAccessibleState(ui::AccessibleViewState* state) {
    133   if (menu_enabled_)
    134     ButtonDropDown::GetAccessibleState(state);
    135   else
    136     CustomButton::GetAccessibleState(state);
    137 }
    138 
    139 bool ReloadButton::ShouldShowMenu() {
    140   return menu_enabled_ && (visible_mode_ == MODE_RELOAD);
    141 }
    142 
    143 void ReloadButton::ShowDropDownMenu(ui::MenuSourceType source_type) {
    144   ButtonDropDown::ShowDropDownMenu(source_type);  // Blocks.
    145   ChangeMode(intended_mode_, true);
    146 }
    147 
    148 void ReloadButton::ButtonPressed(views::Button* /* button */,
    149                                  const ui::Event& event) {
    150   ClearPendingMenu();
    151 
    152   if (visible_mode_ == MODE_STOP) {
    153     if (command_updater_)
    154       command_updater_->ExecuteCommandWithDisposition(IDC_STOP, CURRENT_TAB);
    155     // The user has clicked, so we can feel free to update the button,
    156     // even if the mouse is still hovering.
    157     ChangeMode(MODE_RELOAD, true);
    158   } else if (!double_click_timer_.IsRunning()) {
    159     // Shift-clicking or ctrl-clicking the reload button means we should ignore
    160     // any cached content.
    161     int command;
    162     int flags = event.flags();
    163     if (event.IsShiftDown() || event.IsControlDown()) {
    164       command = IDC_RELOAD_IGNORING_CACHE;
    165       // Mask off Shift and Control so they don't affect the disposition below.
    166       flags &= ~(ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN);
    167     } else {
    168       command = IDC_RELOAD;
    169     }
    170 
    171     // Start a timer - while this timer is running, the reload button cannot be
    172     // changed to a stop button.  We do not set |intended_mode_| to MODE_STOP
    173     // here as the browser will do that when it actually starts loading (which
    174     // may happen synchronously, thus the need to do this before telling the
    175     // browser to execute the reload command).
    176     double_click_timer_.Start(FROM_HERE, double_click_timer_delay_, this,
    177                               &ReloadButton::OnDoubleClickTimer);
    178 
    179     ExecuteBrowserCommand(command, flags);
    180     ++testing_reload_count_;
    181   }
    182 }
    183 
    184 bool ReloadButton::IsCommandIdChecked(int command_id) const {
    185   return false;
    186 }
    187 
    188 bool ReloadButton::IsCommandIdEnabled(int command_id) const {
    189   return true;
    190 }
    191 
    192 bool ReloadButton::IsCommandIdVisible(int command_id) const {
    193   return true;
    194 }
    195 
    196 bool ReloadButton::GetAcceleratorForCommandId(int command_id,
    197     ui::Accelerator* accelerator) {
    198   switch (command_id) {
    199     case IDS_RELOAD_MENU_NORMAL_RELOAD_ITEM:
    200       GetWidget()->GetAccelerator(IDC_RELOAD, accelerator);
    201       return true;
    202     case IDS_RELOAD_MENU_HARD_RELOAD_ITEM:
    203       GetWidget()->GetAccelerator(IDC_RELOAD_IGNORING_CACHE, accelerator);
    204       return true;
    205   }
    206   return GetWidget()->GetAccelerator(command_id, accelerator);
    207 }
    208 
    209 void ReloadButton::ExecuteCommand(int command_id, int event_flags) {
    210   int browser_command = 0;
    211   switch (command_id) {
    212     case IDS_RELOAD_MENU_NORMAL_RELOAD_ITEM:
    213       browser_command = IDC_RELOAD;
    214       break;
    215     case IDS_RELOAD_MENU_HARD_RELOAD_ITEM:
    216       browser_command = IDC_RELOAD_IGNORING_CACHE;
    217       break;
    218     case IDS_RELOAD_MENU_EMPTY_AND_HARD_RELOAD_ITEM:
    219       browser_command = IDC_RELOAD_CLEARING_CACHE;
    220       break;
    221     default:
    222       NOTREACHED();
    223   }
    224   ExecuteBrowserCommand(browser_command, event_flags);
    225 }
    226 
    227 ui::SimpleMenuModel* ReloadButton::CreateMenuModel() {
    228   ui::SimpleMenuModel* menu_model = new ui::SimpleMenuModel(this);
    229   for (size_t i = 0; i < arraysize(kReloadMenuItems); ++i)
    230     menu_model->AddItemWithStringId(kReloadMenuItems[i], kReloadMenuItems[i]);
    231 
    232   return menu_model;
    233 }
    234 
    235 void ReloadButton::ExecuteBrowserCommand(int command, int event_flags) {
    236   if (!command_updater_)
    237     return;
    238 
    239   WindowOpenDisposition disposition =
    240       ui::DispositionFromEventFlags(event_flags);
    241   if ((disposition == CURRENT_TAB) && location_bar_) {
    242     // Forcibly reset the location bar, since otherwise it won't discard any
    243     // ongoing user edits, since it doesn't realize this is a user-initiated
    244     // action.
    245     location_bar_->Revert();
    246   }
    247   command_updater_->ExecuteCommandWithDisposition(command, disposition);
    248 }
    249 
    250 void ReloadButton::ChangeModeInternal(Mode mode) {
    251   if (visible_mode_ == mode)
    252     return;
    253 
    254   for (size_t i = 0; i < STATE_COUNT; ++i)
    255     std::swap(images_[i], alternate_images_[i]);
    256   visible_mode_ = mode;
    257   SchedulePaint();
    258 }
    259 
    260 void ReloadButton::OnDoubleClickTimer() {
    261   if (!IsMenuShowing())
    262     ChangeMode(intended_mode_, false);
    263 }
    264 
    265 void ReloadButton::OnStopToReloadTimer() {
    266   DCHECK(!IsMenuShowing());
    267   ChangeMode(intended_mode_, true);
    268 }
    269