Home | History | Annotate | Download | only in shelf
      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 "ash/shelf/shelf_view.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "ash/ash_constants.h"
     10 #include "ash/ash_switches.h"
     11 #include "ash/drag_drop/drag_image_view.h"
     12 #include "ash/metrics/user_metrics_recorder.h"
     13 #include "ash/root_window_controller.h"
     14 #include "ash/scoped_target_root_window.h"
     15 #include "ash/shelf/app_list_button.h"
     16 #include "ash/shelf/overflow_bubble.h"
     17 #include "ash/shelf/overflow_bubble_view.h"
     18 #include "ash/shelf/overflow_button.h"
     19 #include "ash/shelf/shelf_button.h"
     20 #include "ash/shelf/shelf_constants.h"
     21 #include "ash/shelf/shelf_delegate.h"
     22 #include "ash/shelf/shelf_icon_observer.h"
     23 #include "ash/shelf/shelf_item_delegate.h"
     24 #include "ash/shelf/shelf_item_delegate_manager.h"
     25 #include "ash/shelf/shelf_layout_manager.h"
     26 #include "ash/shelf/shelf_menu_model.h"
     27 #include "ash/shelf/shelf_model.h"
     28 #include "ash/shelf/shelf_tooltip_manager.h"
     29 #include "ash/shelf/shelf_widget.h"
     30 #include "ash/shell.h"
     31 #include "ash/wm/coordinate_conversion.h"
     32 #include "base/auto_reset.h"
     33 #include "base/memory/scoped_ptr.h"
     34 #include "base/metrics/histogram.h"
     35 #include "grit/ash_resources.h"
     36 #include "grit/ash_strings.h"
     37 #include "ui/accessibility/ax_view_state.h"
     38 #include "ui/aura/client/screen_position_client.h"
     39 #include "ui/aura/window.h"
     40 #include "ui/aura/window_event_dispatcher.h"
     41 #include "ui/base/l10n/l10n_util.h"
     42 #include "ui/base/models/simple_menu_model.h"
     43 #include "ui/base/resource/resource_bundle.h"
     44 #include "ui/compositor/layer.h"
     45 #include "ui/compositor/layer_animator.h"
     46 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
     47 #include "ui/gfx/canvas.h"
     48 #include "ui/gfx/point.h"
     49 #include "ui/views/animation/bounds_animator.h"
     50 #include "ui/views/border.h"
     51 #include "ui/views/controls/button/image_button.h"
     52 #include "ui/views/controls/menu/menu_model_adapter.h"
     53 #include "ui/views/controls/menu/menu_runner.h"
     54 #include "ui/views/focus/focus_search.h"
     55 #include "ui/views/view_model.h"
     56 #include "ui/views/view_model_utils.h"
     57 #include "ui/views/widget/widget.h"
     58 
     59 using gfx::Animation;
     60 using views::View;
     61 
     62 namespace ash {
     63 
     64 const int SHELF_ALIGNMENT_UMA_ENUM_VALUE_BOTTOM = 0;
     65 const int SHELF_ALIGNMENT_UMA_ENUM_VALUE_LEFT = 1;
     66 const int SHELF_ALIGNMENT_UMA_ENUM_VALUE_RIGHT = 2;
     67 const int SHELF_ALIGNMENT_UMA_ENUM_VALUE_COUNT = 3;
     68 
     69 // Default amount content is inset on the left edge.
     70 const int kDefaultLeadingInset = 8;
     71 
     72 // Minimum distance before drag starts.
     73 const int kMinimumDragDistance = 8;
     74 
     75 // Additional spacing for the left and right side of icons.
     76 const int kHorizontalIconSpacing = 2;
     77 
     78 // Inset for items which do not have an icon.
     79 const int kHorizontalNoIconInsetSpacing =
     80     kHorizontalIconSpacing + kDefaultLeadingInset;
     81 
     82 // The proportion of the shelf space reserved for non-panel icons. Panels
     83 // may flow into this space but will be put into the overflow bubble if there
     84 // is contention for the space.
     85 const float kReservedNonPanelIconProportion = 0.67f;
     86 
     87 // This is the command id of the menu item which contains the name of the menu.
     88 const int kCommandIdOfMenuName = 0;
     89 
     90 // The background color of the active item in the list.
     91 const SkColor kActiveListItemBackgroundColor = SkColorSetRGB(203 , 219, 241);
     92 
     93 // The background color of the active & hovered item in the list.
     94 const SkColor kFocusedActiveListItemBackgroundColor =
     95     SkColorSetRGB(193, 211, 236);
     96 
     97 // The text color of the caption item in a list.
     98 const SkColor kCaptionItemForegroundColor = SK_ColorBLACK;
     99 
    100 // The maximum allowable length of a menu line of an application menu in pixels.
    101 const int kMaximumAppMenuItemLength = 350;
    102 
    103 // The distance of the cursor from the outer rim of the shelf before it
    104 // separates.
    105 const int kRipOffDistance = 48;
    106 
    107 // The rip off drag and drop proxy image should get scaled by this factor.
    108 const float kDragAndDropProxyScale = 1.5f;
    109 
    110 // The opacity represents that this partially disappeared item will get removed.
    111 const float kDraggedImageOpacity = 0.5f;
    112 
    113 namespace {
    114 
    115 // A class to temporarily disable a given bounds animator.
    116 class BoundsAnimatorDisabler {
    117  public:
    118   BoundsAnimatorDisabler(views::BoundsAnimator* bounds_animator)
    119       : old_duration_(bounds_animator->GetAnimationDuration()),
    120         bounds_animator_(bounds_animator) {
    121     bounds_animator_->SetAnimationDuration(1);
    122   }
    123 
    124   ~BoundsAnimatorDisabler() {
    125     bounds_animator_->SetAnimationDuration(old_duration_);
    126   }
    127 
    128  private:
    129   // The previous animation duration.
    130   int old_duration_;
    131   // The bounds animator which gets used.
    132   views::BoundsAnimator* bounds_animator_;
    133 
    134   DISALLOW_COPY_AND_ASSIGN(BoundsAnimatorDisabler);
    135 };
    136 
    137 // The MenuModelAdapter gets slightly changed to adapt the menu appearance to
    138 // our requirements.
    139 class ShelfMenuModelAdapter : public views::MenuModelAdapter {
    140  public:
    141   explicit ShelfMenuModelAdapter(ShelfMenuModel* menu_model);
    142 
    143   // views::MenuModelAdapter:
    144   virtual const gfx::FontList* GetLabelFontList(int command_id) const OVERRIDE;
    145   virtual bool IsCommandEnabled(int id) const OVERRIDE;
    146   virtual void GetHorizontalIconMargins(int id,
    147                                         int icon_size,
    148                                         int* left_margin,
    149                                         int* right_margin) const OVERRIDE;
    150   virtual bool GetForegroundColor(int command_id,
    151                                   bool is_hovered,
    152                                   SkColor* override_color) const OVERRIDE;
    153   virtual bool GetBackgroundColor(int command_id,
    154                                   bool is_hovered,
    155                                   SkColor* override_color) const OVERRIDE;
    156   virtual int GetMaxWidthForMenu(views::MenuItemView* menu) OVERRIDE;
    157   virtual bool ShouldReserveSpaceForSubmenuIndicator() const OVERRIDE;
    158 
    159  private:
    160   ShelfMenuModel* menu_model_;
    161 
    162   DISALLOW_COPY_AND_ASSIGN(ShelfMenuModelAdapter);
    163 };
    164 
    165 ShelfMenuModelAdapter::ShelfMenuModelAdapter(ShelfMenuModel* menu_model)
    166     : MenuModelAdapter(menu_model),
    167       menu_model_(menu_model) {
    168 }
    169 
    170 const gfx::FontList* ShelfMenuModelAdapter::GetLabelFontList(
    171     int command_id) const {
    172   if (command_id != kCommandIdOfMenuName)
    173     return MenuModelAdapter::GetLabelFontList(command_id);
    174 
    175   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    176   return &rb.GetFontList(ui::ResourceBundle::BoldFont);
    177 }
    178 
    179 bool ShelfMenuModelAdapter::IsCommandEnabled(int id) const {
    180   return id != kCommandIdOfMenuName;
    181 }
    182 
    183 bool ShelfMenuModelAdapter::GetForegroundColor(int command_id,
    184                                                bool is_hovered,
    185                                                SkColor* override_color) const {
    186   if (command_id != kCommandIdOfMenuName)
    187     return false;
    188 
    189   *override_color = kCaptionItemForegroundColor;
    190   return true;
    191 }
    192 
    193 bool ShelfMenuModelAdapter::GetBackgroundColor(int command_id,
    194                                                bool is_hovered,
    195                                                SkColor* override_color) const {
    196   if (!menu_model_->IsCommandActive(command_id))
    197     return false;
    198 
    199   *override_color = is_hovered ? kFocusedActiveListItemBackgroundColor :
    200                                  kActiveListItemBackgroundColor;
    201   return true;
    202 }
    203 
    204 void ShelfMenuModelAdapter::GetHorizontalIconMargins(int command_id,
    205                                                      int icon_size,
    206                                                      int* left_margin,
    207                                                      int* right_margin) const {
    208   *left_margin = kHorizontalIconSpacing;
    209   *right_margin = (command_id != kCommandIdOfMenuName) ?
    210       kHorizontalIconSpacing : -(icon_size + kHorizontalNoIconInsetSpacing);
    211 }
    212 
    213 int ShelfMenuModelAdapter::GetMaxWidthForMenu(views::MenuItemView* menu) {
    214   return kMaximumAppMenuItemLength;
    215 }
    216 
    217 bool ShelfMenuModelAdapter::ShouldReserveSpaceForSubmenuIndicator() const {
    218   return false;
    219 }
    220 
    221 // Custom FocusSearch used to navigate the shelf in the order items are in
    222 // the ViewModel.
    223 class ShelfFocusSearch : public views::FocusSearch {
    224  public:
    225   explicit ShelfFocusSearch(views::ViewModel* view_model)
    226       : FocusSearch(NULL, true, true),
    227         view_model_(view_model) {}
    228   virtual ~ShelfFocusSearch() {}
    229 
    230   // views::FocusSearch overrides:
    231   virtual View* FindNextFocusableView(
    232       View* starting_view,
    233       bool reverse,
    234       Direction direction,
    235       bool check_starting_view,
    236       views::FocusTraversable** focus_traversable,
    237       View** focus_traversable_view) OVERRIDE {
    238     int index = view_model_->GetIndexOfView(starting_view);
    239     if (index == -1)
    240       return view_model_->view_at(0);
    241 
    242     if (reverse) {
    243       --index;
    244       if (index < 0)
    245         index = view_model_->view_size() - 1;
    246     } else {
    247       ++index;
    248       if (index >= view_model_->view_size())
    249         index = 0;
    250     }
    251     return view_model_->view_at(index);
    252   }
    253 
    254  private:
    255   views::ViewModel* view_model_;
    256 
    257   DISALLOW_COPY_AND_ASSIGN(ShelfFocusSearch);
    258 };
    259 
    260 // AnimationDelegate used when inserting a new item. This steadily increases the
    261 // opacity of the layer as the animation progress.
    262 class FadeInAnimationDelegate : public gfx::AnimationDelegate {
    263  public:
    264   explicit FadeInAnimationDelegate(views::View* view) : view_(view) {}
    265   virtual ~FadeInAnimationDelegate() {}
    266 
    267   // AnimationDelegate overrides:
    268   virtual void AnimationProgressed(const Animation* animation) OVERRIDE {
    269     view_->layer()->SetOpacity(animation->GetCurrentValue());
    270     view_->layer()->ScheduleDraw();
    271   }
    272   virtual void AnimationEnded(const Animation* animation) OVERRIDE {
    273     view_->layer()->SetOpacity(1.0f);
    274     view_->layer()->ScheduleDraw();
    275   }
    276   virtual void AnimationCanceled(const Animation* animation) OVERRIDE {
    277     view_->layer()->SetOpacity(1.0f);
    278     view_->layer()->ScheduleDraw();
    279   }
    280 
    281  private:
    282   views::View* view_;
    283 
    284   DISALLOW_COPY_AND_ASSIGN(FadeInAnimationDelegate);
    285 };
    286 
    287 void ReflectItemStatus(const ShelfItem& item, ShelfButton* button) {
    288   switch (item.status) {
    289     case STATUS_CLOSED:
    290       button->ClearState(ShelfButton::STATE_ACTIVE);
    291       button->ClearState(ShelfButton::STATE_RUNNING);
    292       button->ClearState(ShelfButton::STATE_ATTENTION);
    293       break;
    294     case STATUS_RUNNING:
    295       button->ClearState(ShelfButton::STATE_ACTIVE);
    296       button->AddState(ShelfButton::STATE_RUNNING);
    297       button->ClearState(ShelfButton::STATE_ATTENTION);
    298       break;
    299     case STATUS_ACTIVE:
    300       button->AddState(ShelfButton::STATE_ACTIVE);
    301       button->ClearState(ShelfButton::STATE_RUNNING);
    302       button->ClearState(ShelfButton::STATE_ATTENTION);
    303       break;
    304     case STATUS_ATTENTION:
    305       button->ClearState(ShelfButton::STATE_ACTIVE);
    306       button->ClearState(ShelfButton::STATE_RUNNING);
    307       button->AddState(ShelfButton::STATE_ATTENTION);
    308       break;
    309   }
    310 }
    311 
    312 }  // namespace
    313 
    314 // AnimationDelegate used when deleting an item. This steadily decreased the
    315 // opacity of the layer as the animation progress.
    316 class ShelfView::FadeOutAnimationDelegate : public gfx::AnimationDelegate {
    317  public:
    318   FadeOutAnimationDelegate(ShelfView* host, views::View* view)
    319       : shelf_view_(host),
    320         view_(view) {}
    321   virtual ~FadeOutAnimationDelegate() {}
    322 
    323   // AnimationDelegate overrides:
    324   virtual void AnimationProgressed(const Animation* animation) OVERRIDE {
    325     view_->layer()->SetOpacity(1 - animation->GetCurrentValue());
    326     view_->layer()->ScheduleDraw();
    327   }
    328   virtual void AnimationEnded(const Animation* animation) OVERRIDE {
    329     shelf_view_->OnFadeOutAnimationEnded();
    330   }
    331   virtual void AnimationCanceled(const Animation* animation) OVERRIDE {
    332   }
    333 
    334  private:
    335   ShelfView* shelf_view_;
    336   scoped_ptr<views::View> view_;
    337 
    338   DISALLOW_COPY_AND_ASSIGN(FadeOutAnimationDelegate);
    339 };
    340 
    341 // AnimationDelegate used to trigger fading an element in. When an item is
    342 // inserted this delegate is attached to the animation that expands the size of
    343 // the item.  When done it kicks off another animation to fade the item in.
    344 class ShelfView::StartFadeAnimationDelegate : public gfx::AnimationDelegate {
    345  public:
    346   StartFadeAnimationDelegate(ShelfView* host,
    347                              views::View* view)
    348       : shelf_view_(host),
    349         view_(view) {}
    350   virtual ~StartFadeAnimationDelegate() {}
    351 
    352   // AnimationDelegate overrides:
    353   virtual void AnimationEnded(const Animation* animation) OVERRIDE {
    354     shelf_view_->FadeIn(view_);
    355   }
    356   virtual void AnimationCanceled(const Animation* animation) OVERRIDE {
    357     view_->layer()->SetOpacity(1.0f);
    358   }
    359 
    360  private:
    361   ShelfView* shelf_view_;
    362   views::View* view_;
    363 
    364   DISALLOW_COPY_AND_ASSIGN(StartFadeAnimationDelegate);
    365 };
    366 
    367 ShelfView::ShelfView(ShelfModel* model,
    368                      ShelfDelegate* delegate,
    369                      ShelfLayoutManager* manager)
    370     : model_(model),
    371       delegate_(delegate),
    372       view_model_(new views::ViewModel),
    373       first_visible_index_(0),
    374       last_visible_index_(-1),
    375       overflow_button_(NULL),
    376       owner_overflow_bubble_(NULL),
    377       drag_pointer_(NONE),
    378       drag_view_(NULL),
    379       start_drag_index_(-1),
    380       context_menu_id_(0),
    381       leading_inset_(kDefaultLeadingInset),
    382       cancelling_drag_model_changed_(false),
    383       last_hidden_index_(0),
    384       closing_event_time_(base::TimeDelta()),
    385       got_deleted_(NULL),
    386       drag_and_drop_item_pinned_(false),
    387       drag_and_drop_shelf_id_(0),
    388       dragged_off_shelf_(false),
    389       snap_back_from_rip_off_view_(NULL),
    390       item_manager_(Shell::GetInstance()->shelf_item_delegate_manager()),
    391       layout_manager_(manager),
    392       overflow_mode_(false),
    393       main_shelf_(NULL),
    394       dragged_off_from_overflow_to_shelf_(false) {
    395   DCHECK(model_);
    396   bounds_animator_.reset(new views::BoundsAnimator(this));
    397   bounds_animator_->AddObserver(this);
    398   set_context_menu_controller(this);
    399   focus_search_.reset(new ShelfFocusSearch(view_model_.get()));
    400   tooltip_.reset(new ShelfTooltipManager(manager, this));
    401 }
    402 
    403 ShelfView::~ShelfView() {
    404   bounds_animator_->RemoveObserver(this);
    405   model_->RemoveObserver(this);
    406   // If we are inside the MenuRunner, we need to know if we were getting
    407   // deleted while it was running.
    408   if (got_deleted_)
    409     *got_deleted_ = true;
    410 }
    411 
    412 void ShelfView::Init() {
    413   model_->AddObserver(this);
    414 
    415   const ShelfItems& items(model_->items());
    416   for (ShelfItems::const_iterator i = items.begin(); i != items.end(); ++i) {
    417     views::View* child = CreateViewForItem(*i);
    418     child->SetPaintToLayer(true);
    419     view_model_->Add(child, static_cast<int>(i - items.begin()));
    420     AddChildView(child);
    421   }
    422   overflow_button_ = new OverflowButton(this);
    423   overflow_button_->set_context_menu_controller(this);
    424   ConfigureChildView(overflow_button_);
    425   AddChildView(overflow_button_);
    426 
    427   // We'll layout when our bounds change.
    428 }
    429 
    430 void ShelfView::OnShelfAlignmentChanged() {
    431   overflow_button_->OnShelfAlignmentChanged();
    432   LayoutToIdealBounds();
    433   for (int i=0; i < view_model_->view_size(); ++i) {
    434     if (i >= first_visible_index_ && i <= last_visible_index_)
    435       view_model_->view_at(i)->Layout();
    436   }
    437   tooltip_->Close();
    438   if (overflow_bubble_)
    439     overflow_bubble_->Hide();
    440 }
    441 
    442 void ShelfView::SchedulePaintForAllButtons() {
    443   for (int i = 0; i < view_model_->view_size(); ++i) {
    444     if (i >= first_visible_index_ && i <= last_visible_index_)
    445       view_model_->view_at(i)->SchedulePaint();
    446   }
    447   if (overflow_button_ && overflow_button_->visible())
    448     overflow_button_->SchedulePaint();
    449 }
    450 
    451 gfx::Rect ShelfView::GetIdealBoundsOfItemIcon(ShelfID id) {
    452   int index = model_->ItemIndexByID(id);
    453   if (index == -1)
    454     return gfx::Rect();
    455   // Map all items from overflow area to the overflow button. Note that the
    456   // section between last_index_hidden_ and model_->FirstPanelIndex() is the
    457   // list of invisible panel items. However, these items are currently nowhere
    458   // represented and get dropped instead - see (crbug.com/378907). As such there
    459   // is no way to address them or place them. We therefore move them over the
    460   // overflow button.
    461   if (index > last_visible_index_ && index < model_->FirstPanelIndex())
    462     index = last_visible_index_ + 1;
    463   const gfx::Rect& ideal_bounds(view_model_->ideal_bounds(index));
    464   DCHECK_NE(TYPE_APP_LIST, model_->items()[index].type);
    465   ShelfButton* button = static_cast<ShelfButton*>(view_model_->view_at(index));
    466   gfx::Rect icon_bounds = button->GetIconBounds();
    467   return gfx::Rect(GetMirroredXWithWidthInView(
    468                        ideal_bounds.x() + icon_bounds.x(), icon_bounds.width()),
    469                    ideal_bounds.y() + icon_bounds.y(),
    470                    icon_bounds.width(),
    471                    icon_bounds.height());
    472 }
    473 
    474 void ShelfView::UpdatePanelIconPosition(ShelfID id,
    475                                         const gfx::Point& midpoint) {
    476   int current_index = model_->ItemIndexByID(id);
    477   int first_panel_index = model_->FirstPanelIndex();
    478   if (current_index < first_panel_index)
    479     return;
    480 
    481   gfx::Point midpoint_in_view(GetMirroredXInView(midpoint.x()),
    482                               midpoint.y());
    483   int target_index = current_index;
    484   while (target_index > first_panel_index &&
    485          layout_manager_->PrimaryAxisValue(
    486              view_model_->ideal_bounds(target_index).x(),
    487              view_model_->ideal_bounds(target_index).y()) >
    488          layout_manager_->PrimaryAxisValue(midpoint_in_view.x(),
    489                                            midpoint_in_view.y())) {
    490     --target_index;
    491   }
    492   while (target_index < view_model_->view_size() - 1 &&
    493          layout_manager_->PrimaryAxisValue(
    494              view_model_->ideal_bounds(target_index).right(),
    495              view_model_->ideal_bounds(target_index).bottom()) <
    496          layout_manager_->PrimaryAxisValue(midpoint_in_view.x(),
    497                                            midpoint_in_view.y())) {
    498     ++target_index;
    499   }
    500   if (current_index != target_index)
    501     model_->Move(current_index, target_index);
    502 }
    503 
    504 bool ShelfView::IsShowingMenu() const {
    505   return (launcher_menu_runner_.get() &&
    506        launcher_menu_runner_->IsRunning());
    507 }
    508 
    509 bool ShelfView::IsShowingOverflowBubble() const {
    510   return overflow_bubble_.get() && overflow_bubble_->IsShowing();
    511 }
    512 
    513 views::View* ShelfView::GetAppListButtonView() const {
    514   for (int i = 0; i < model_->item_count(); ++i) {
    515     if (model_->items()[i].type == TYPE_APP_LIST)
    516       return view_model_->view_at(i);
    517   }
    518 
    519   NOTREACHED() << "Applist button not found";
    520   return NULL;
    521 }
    522 
    523 ////////////////////////////////////////////////////////////////////////////////
    524 // ShelfView, FocusTraversable implementation:
    525 
    526 views::FocusSearch* ShelfView::GetFocusSearch() {
    527   return focus_search_.get();
    528 }
    529 
    530 views::FocusTraversable* ShelfView::GetFocusTraversableParent() {
    531   return parent()->GetFocusTraversable();
    532 }
    533 
    534 View* ShelfView::GetFocusTraversableParentView() {
    535   return this;
    536 }
    537 
    538 void ShelfView::CreateDragIconProxy(
    539     const gfx::Point& location_in_screen_coordinates,
    540     const gfx::ImageSkia& icon,
    541     views::View* replaced_view,
    542     const gfx::Vector2d& cursor_offset_from_center,
    543     float scale_factor) {
    544   drag_replaced_view_ = replaced_view;
    545   drag_image_.reset(new ash::DragImageView(
    546       drag_replaced_view_->GetWidget()->GetNativeWindow()->GetRootWindow(),
    547       ui::DragDropTypes::DRAG_EVENT_SOURCE_MOUSE));
    548   drag_image_->SetImage(icon);
    549   gfx::Size size = drag_image_->GetPreferredSize();
    550   size.set_width(size.width() * scale_factor);
    551   size.set_height(size.height() * scale_factor);
    552   drag_image_offset_ = gfx::Vector2d(size.width() / 2, size.height() / 2) +
    553                        cursor_offset_from_center;
    554   gfx::Rect drag_image_bounds(
    555       location_in_screen_coordinates - drag_image_offset_,
    556       size);
    557   drag_image_->SetBoundsInScreen(drag_image_bounds);
    558   drag_image_->SetWidgetVisible(true);
    559 }
    560 
    561 void ShelfView::UpdateDragIconProxy(
    562     const gfx::Point& location_in_screen_coordinates) {
    563   // TODO(jennyz): Investigate why drag_image_ becomes NULL at this point per
    564   // crbug.com/34722, while the app list item is still being dragged around.
    565   if (drag_image_) {
    566     drag_image_->SetScreenPosition(
    567         location_in_screen_coordinates - drag_image_offset_);
    568   }
    569 }
    570 
    571 void ShelfView::DestroyDragIconProxy() {
    572   drag_image_.reset();
    573   drag_image_offset_ = gfx::Vector2d(0, 0);
    574 }
    575 
    576 bool ShelfView::StartDrag(const std::string& app_id,
    577                           const gfx::Point& location_in_screen_coordinates) {
    578   // Bail if an operation is already going on - or the cursor is not inside.
    579   // This could happen if mouse / touch operations overlap.
    580   if (drag_and_drop_shelf_id_ ||
    581       !GetBoundsInScreen().Contains(location_in_screen_coordinates))
    582     return false;
    583 
    584   // If the AppsGridView (which was dispatching this event) was opened by our
    585   // button, ShelfView dragging operations are locked and we have to unlock.
    586   CancelDrag(-1);
    587   drag_and_drop_item_pinned_ = false;
    588   drag_and_drop_app_id_ = app_id;
    589   drag_and_drop_shelf_id_ =
    590       delegate_->GetShelfIDForAppID(drag_and_drop_app_id_);
    591   // Check if the application is known and pinned - if not, we have to pin it so
    592   // that we can re-arrange the shelf order accordingly. Note that items have
    593   // to be pinned to give them the same (order) possibilities as a shortcut.
    594   // When an item is dragged from overflow to shelf, IsShowingOverflowBubble()
    595   // returns true. At this time, we don't need to pin the item.
    596   if (!IsShowingOverflowBubble() &&
    597       (!drag_and_drop_shelf_id_ ||
    598        !delegate_->IsAppPinned(app_id))) {
    599     delegate_->PinAppWithID(app_id);
    600     drag_and_drop_shelf_id_ =
    601         delegate_->GetShelfIDForAppID(drag_and_drop_app_id_);
    602     if (!drag_and_drop_shelf_id_)
    603       return false;
    604     drag_and_drop_item_pinned_ = true;
    605   }
    606   views::View* drag_and_drop_view = view_model_->view_at(
    607       model_->ItemIndexByID(drag_and_drop_shelf_id_));
    608   DCHECK(drag_and_drop_view);
    609 
    610   // Since there is already an icon presented by the caller, we hide this item
    611   // for now. That has to be done by reducing the size since the visibility will
    612   // change once a regrouping animation is performed.
    613   pre_drag_and_drop_size_ = drag_and_drop_view->size();
    614   drag_and_drop_view->SetSize(gfx::Size());
    615 
    616   // First we have to center the mouse cursor over the item.
    617   gfx::Point pt = drag_and_drop_view->GetBoundsInScreen().CenterPoint();
    618   views::View::ConvertPointFromScreen(drag_and_drop_view, &pt);
    619   gfx::Point point_in_root = location_in_screen_coordinates;
    620   ash::wm::ConvertPointFromScreen(
    621       ash::wm::GetRootWindowAt(location_in_screen_coordinates),
    622       &point_in_root);
    623   ui::MouseEvent event(ui::ET_MOUSE_PRESSED, pt, point_in_root, 0, 0);
    624   PointerPressedOnButton(drag_and_drop_view,
    625                          ShelfButtonHost::DRAG_AND_DROP,
    626                          event);
    627 
    628   // Drag the item where it really belongs.
    629   Drag(location_in_screen_coordinates);
    630   return true;
    631 }
    632 
    633 bool ShelfView::Drag(const gfx::Point& location_in_screen_coordinates) {
    634   if (!drag_and_drop_shelf_id_ ||
    635       !GetBoundsInScreen().Contains(location_in_screen_coordinates))
    636     return false;
    637 
    638   gfx::Point pt = location_in_screen_coordinates;
    639   views::View* drag_and_drop_view = view_model_->view_at(
    640       model_->ItemIndexByID(drag_and_drop_shelf_id_));
    641   ConvertPointFromScreen(drag_and_drop_view, &pt);
    642   gfx::Point point_in_root = location_in_screen_coordinates;
    643   ash::wm::ConvertPointFromScreen(
    644       ash::wm::GetRootWindowAt(location_in_screen_coordinates),
    645       &point_in_root);
    646   ui::MouseEvent event(ui::ET_MOUSE_DRAGGED, pt, point_in_root, 0, 0);
    647   PointerDraggedOnButton(drag_and_drop_view,
    648                          ShelfButtonHost::DRAG_AND_DROP,
    649                          event);
    650   return true;
    651 }
    652 
    653 void ShelfView::EndDrag(bool cancel) {
    654   if (!drag_and_drop_shelf_id_)
    655     return;
    656 
    657   views::View* drag_and_drop_view = view_model_->view_at(
    658       model_->ItemIndexByID(drag_and_drop_shelf_id_));
    659   PointerReleasedOnButton(
    660       drag_and_drop_view, ShelfButtonHost::DRAG_AND_DROP, cancel);
    661 
    662   // Either destroy the temporarily created item - or - make the item visible.
    663   if (drag_and_drop_item_pinned_ && cancel)
    664     delegate_->UnpinAppWithID(drag_and_drop_app_id_);
    665   else if (drag_and_drop_view) {
    666     if (cancel) {
    667       // When a hosted drag gets canceled, the item can remain in the same slot
    668       // and it might have moved within the bounds. In that case the item need
    669       // to animate back to its correct location.
    670       AnimateToIdealBounds();
    671     } else {
    672       drag_and_drop_view->SetSize(pre_drag_and_drop_size_);
    673     }
    674   }
    675 
    676   drag_and_drop_shelf_id_ = 0;
    677 }
    678 
    679 void ShelfView::LayoutToIdealBounds() {
    680   if (bounds_animator_->IsAnimating()) {
    681     AnimateToIdealBounds();
    682     return;
    683   }
    684 
    685   IdealBounds ideal_bounds;
    686   CalculateIdealBounds(&ideal_bounds);
    687   views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_);
    688   overflow_button_->SetBoundsRect(ideal_bounds.overflow_bounds);
    689 }
    690 
    691 void ShelfView::UpdateAllButtonsVisibilityInOverflowMode() {
    692   // The overflow button is not shown in overflow mode.
    693   overflow_button_->SetVisible(false);
    694   DCHECK_LT(last_visible_index_, view_model_->view_size());
    695   for (int i = 0; i < view_model_->view_size(); ++i) {
    696     bool visible = i >= first_visible_index_ &&
    697         i <= last_visible_index_;
    698     // To track the dragging of |drag_view_| continuously, its visibility
    699     // should be always true regardless of its position.
    700     if (dragged_off_from_overflow_to_shelf_ &&
    701         view_model_->view_at(i) == drag_view_)
    702       view_model_->view_at(i)->SetVisible(true);
    703     else
    704       view_model_->view_at(i)->SetVisible(visible);
    705   }
    706 }
    707 
    708 void ShelfView::CalculateIdealBounds(IdealBounds* bounds) const {
    709   int available_size = layout_manager_->PrimaryAxisValue(width(), height());
    710   DCHECK(model_->item_count() == view_model_->view_size());
    711   if (!available_size)
    712     return;
    713 
    714   int first_panel_index = model_->FirstPanelIndex();
    715   int last_button_index = first_panel_index - 1;
    716 
    717   int x = 0;
    718   int y = 0;
    719   int button_size = kShelfButtonSize;
    720   int button_spacing = kShelfButtonSpacing;
    721 
    722   int w = layout_manager_->PrimaryAxisValue(button_size, width());
    723   int h = layout_manager_->PrimaryAxisValue(height(), button_size);
    724   for (int i = 0; i < view_model_->view_size(); ++i) {
    725     if (i < first_visible_index_) {
    726       view_model_->set_ideal_bounds(i, gfx::Rect(x, y, 0, 0));
    727       continue;
    728     }
    729 
    730     view_model_->set_ideal_bounds(i, gfx::Rect(x, y, w, h));
    731     if (i != last_button_index) {
    732       x = layout_manager_->PrimaryAxisValue(x + w + button_spacing, x);
    733       y = layout_manager_->PrimaryAxisValue(y, y + h + button_spacing);
    734     }
    735   }
    736 
    737   if (is_overflow_mode()) {
    738     const_cast<ShelfView*>(this)->UpdateAllButtonsVisibilityInOverflowMode();
    739     return;
    740   }
    741 
    742   // Right aligned icons.
    743   int end_position = available_size - button_spacing;
    744   x = layout_manager_->PrimaryAxisValue(end_position, 0);
    745   y = layout_manager_->PrimaryAxisValue(0, end_position);
    746   for (int i = view_model_->view_size() - 1;
    747        i >= first_panel_index; --i) {
    748     x = layout_manager_->PrimaryAxisValue(x - w - button_spacing, x);
    749     y = layout_manager_->PrimaryAxisValue(y, y - h - button_spacing);
    750     view_model_->set_ideal_bounds(i, gfx::Rect(x, y, w, h));
    751     end_position = layout_manager_->PrimaryAxisValue(x, y);
    752   }
    753 
    754   // Icons on the left / top are guaranteed up to kLeftIconProportion of
    755   // the available space.
    756   int last_icon_position = layout_manager_->PrimaryAxisValue(
    757       view_model_->ideal_bounds(last_button_index).right(),
    758       view_model_->ideal_bounds(last_button_index).bottom()) + button_size;
    759   int reserved_icon_space = available_size * kReservedNonPanelIconProportion;
    760   if (last_icon_position < reserved_icon_space)
    761     end_position = last_icon_position;
    762   else
    763     end_position = std::max(end_position, reserved_icon_space);
    764 
    765   bounds->overflow_bounds.set_size(
    766       gfx::Size(layout_manager_->PrimaryAxisValue(w, width()),
    767                 layout_manager_->PrimaryAxisValue(height(), h)));
    768 
    769   last_visible_index_ = DetermineLastVisibleIndex(
    770       end_position - button_size);
    771   last_hidden_index_ = DetermineFirstVisiblePanelIndex(end_position) - 1;
    772   bool show_overflow = last_visible_index_ < last_button_index ||
    773       last_hidden_index_ >= first_panel_index;
    774 
    775   // Create Space for the overflow button
    776   if (show_overflow &&
    777       last_visible_index_ > 0 && last_visible_index_ < last_button_index)
    778     --last_visible_index_;
    779   for (int i = 0; i < view_model_->view_size(); ++i) {
    780     bool visible = i <= last_visible_index_ || i > last_hidden_index_;
    781     // To receive drag event continuously from |drag_view_| during the dragging
    782     // off from the shelf, don't make |drag_view_| invisible. It will be
    783     // eventually invisible and removed from the |view_model_| by
    784     // FinalizeRipOffDrag().
    785     if (dragged_off_shelf_ && view_model_->view_at(i) == drag_view_)
    786       continue;
    787     view_model_->view_at(i)->SetVisible(visible);
    788   }
    789 
    790   overflow_button_->SetVisible(show_overflow);
    791   if (show_overflow) {
    792     DCHECK_NE(0, view_model_->view_size());
    793     if (last_visible_index_ == -1) {
    794       x = 0;
    795       y = 0;
    796     } else {
    797       x = layout_manager_->PrimaryAxisValue(
    798           view_model_->ideal_bounds(last_visible_index_).right(),
    799           view_model_->ideal_bounds(last_visible_index_).x());
    800       y = layout_manager_->PrimaryAxisValue(
    801           view_model_->ideal_bounds(last_visible_index_).y(),
    802           view_model_->ideal_bounds(last_visible_index_).bottom());
    803     }
    804     // Set all hidden panel icon positions to be on the overflow button.
    805     for (int i = first_panel_index; i <= last_hidden_index_; ++i)
    806       view_model_->set_ideal_bounds(i, gfx::Rect(x, y, w, h));
    807 
    808     // Add more space between last visible item and overflow button.
    809     // Without this, two buttons look too close compared with other items.
    810     x = layout_manager_->PrimaryAxisValue(x + button_spacing, x);
    811     y = layout_manager_->PrimaryAxisValue(y, y + button_spacing);
    812 
    813     bounds->overflow_bounds.set_x(x);
    814     bounds->overflow_bounds.set_y(y);
    815     if (overflow_bubble_.get() && overflow_bubble_->IsShowing())
    816       UpdateOverflowRange(overflow_bubble_->shelf_view());
    817   } else {
    818     if (overflow_bubble_)
    819       overflow_bubble_->Hide();
    820   }
    821 }
    822 
    823 int ShelfView::DetermineLastVisibleIndex(int max_value) const {
    824   int index = model_->FirstPanelIndex() - 1;
    825   while (index >= 0 &&
    826          layout_manager_->PrimaryAxisValue(
    827              view_model_->ideal_bounds(index).right(),
    828              view_model_->ideal_bounds(index).bottom()) > max_value) {
    829     index--;
    830   }
    831   return index;
    832 }
    833 
    834 int ShelfView::DetermineFirstVisiblePanelIndex(int min_value) const {
    835   int index = model_->FirstPanelIndex();
    836   while (index < view_model_->view_size() &&
    837          layout_manager_->PrimaryAxisValue(
    838              view_model_->ideal_bounds(index).right(),
    839              view_model_->ideal_bounds(index).bottom()) < min_value) {
    840     ++index;
    841   }
    842   return index;
    843 }
    844 
    845 void ShelfView::AddIconObserver(ShelfIconObserver* observer) {
    846   observers_.AddObserver(observer);
    847 }
    848 
    849 void ShelfView::RemoveIconObserver(ShelfIconObserver* observer) {
    850   observers_.RemoveObserver(observer);
    851 }
    852 
    853 void ShelfView::AnimateToIdealBounds() {
    854   IdealBounds ideal_bounds;
    855   CalculateIdealBounds(&ideal_bounds);
    856   for (int i = 0; i < view_model_->view_size(); ++i) {
    857     View* view = view_model_->view_at(i);
    858     bounds_animator_->AnimateViewTo(view, view_model_->ideal_bounds(i));
    859     // Now that the item animation starts, we have to make sure that the
    860     // padding of the first gets properly transferred to the new first item.
    861     if (i && view->border())
    862       view->SetBorder(views::Border::NullBorder());
    863   }
    864   overflow_button_->SetBoundsRect(ideal_bounds.overflow_bounds);
    865 }
    866 
    867 views::View* ShelfView::CreateViewForItem(const ShelfItem& item) {
    868   views::View* view = NULL;
    869   switch (item.type) {
    870     case TYPE_BROWSER_SHORTCUT:
    871     case TYPE_APP_SHORTCUT:
    872     case TYPE_WINDOWED_APP:
    873     case TYPE_PLATFORM_APP:
    874     case TYPE_DIALOG:
    875     case TYPE_APP_PANEL: {
    876       ShelfButton* button = ShelfButton::Create(this, this, layout_manager_);
    877       button->SetImage(item.image);
    878       ReflectItemStatus(item, button);
    879       view = button;
    880       break;
    881     }
    882 
    883     case TYPE_APP_LIST: {
    884       view = new AppListButton(this, this, layout_manager_->shelf_widget());
    885       break;
    886     }
    887 
    888     default:
    889       break;
    890   }
    891   view->set_context_menu_controller(this);
    892 
    893   DCHECK(view);
    894   ConfigureChildView(view);
    895   return view;
    896 }
    897 
    898 void ShelfView::FadeIn(views::View* view) {
    899   view->SetVisible(true);
    900   view->layer()->SetOpacity(0);
    901   AnimateToIdealBounds();
    902   bounds_animator_->SetAnimationDelegate(
    903       view,
    904       scoped_ptr<gfx::AnimationDelegate>(new FadeInAnimationDelegate(view)));
    905 }
    906 
    907 void ShelfView::PrepareForDrag(Pointer pointer, const ui::LocatedEvent& event) {
    908   DCHECK(!dragging());
    909   DCHECK(drag_view_);
    910   drag_pointer_ = pointer;
    911   start_drag_index_ = view_model_->GetIndexOfView(drag_view_);
    912 
    913   if (start_drag_index_== -1) {
    914     CancelDrag(-1);
    915     return;
    916   }
    917 
    918   // If the item is no longer draggable, bail out.
    919   ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
    920       model_->items()[start_drag_index_].id);
    921   if (!item_delegate->IsDraggable()) {
    922     CancelDrag(-1);
    923     return;
    924   }
    925 
    926   // Move the view to the front so that it appears on top of other views.
    927   ReorderChildView(drag_view_, -1);
    928   bounds_animator_->StopAnimatingView(drag_view_);
    929 }
    930 
    931 void ShelfView::ContinueDrag(const ui::LocatedEvent& event) {
    932   // Due to a syncing operation the application might have been removed.
    933   // Bail if it is gone.
    934   int current_index = view_model_->GetIndexOfView(drag_view_);
    935   DCHECK_NE(-1, current_index);
    936 
    937   ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
    938       model_->items()[current_index].id);
    939   if (!item_delegate->IsDraggable()) {
    940     CancelDrag(-1);
    941     return;
    942   }
    943 
    944   // If this is not a drag and drop host operation and not the app list item,
    945   // check if the item got ripped off the shelf - if it did we are done.
    946   if (!drag_and_drop_shelf_id_ &&
    947       RemovableByRipOff(current_index) != NOT_REMOVABLE) {
    948     if (HandleRipOffDrag(event))
    949       return;
    950     // The rip off handler could have changed the location of the item.
    951     current_index = view_model_->GetIndexOfView(drag_view_);
    952   }
    953 
    954   // TODO: I don't think this works correctly with RTL.
    955   gfx::Point drag_point(event.location());
    956   ConvertPointToTarget(drag_view_, this, &drag_point);
    957 
    958   // Constrain the location to the range of valid indices for the type.
    959   std::pair<int, int> indices(GetDragRange(current_index));
    960   int first_drag_index = indices.first;
    961   int last_drag_index = indices.second;
    962   // If the last index isn't valid, we're overflowing. Constrain to the app list
    963   // (which is the last visible item).
    964   if (first_drag_index < model_->FirstPanelIndex() &&
    965       last_drag_index > last_visible_index_)
    966     last_drag_index = last_visible_index_;
    967   int x = 0, y = 0;
    968   if (layout_manager_->IsHorizontalAlignment()) {
    969     x = std::max(view_model_->ideal_bounds(indices.first).x(),
    970                      drag_point.x() - drag_origin_.x());
    971     x = std::min(view_model_->ideal_bounds(last_drag_index).right() -
    972                  view_model_->ideal_bounds(current_index).width(),
    973                  x);
    974     if (drag_view_->x() == x)
    975       return;
    976     drag_view_->SetX(x);
    977   } else {
    978     y = std::max(view_model_->ideal_bounds(indices.first).y(),
    979                      drag_point.y() - drag_origin_.y());
    980     y = std::min(view_model_->ideal_bounds(last_drag_index).bottom() -
    981                  view_model_->ideal_bounds(current_index).height(),
    982                  y);
    983     if (drag_view_->y() == y)
    984       return;
    985     drag_view_->SetY(y);
    986   }
    987 
    988   int target_index =
    989       views::ViewModelUtils::DetermineMoveIndex(
    990           *view_model_, drag_view_,
    991           layout_manager_->IsHorizontalAlignment() ?
    992               views::ViewModelUtils::HORIZONTAL :
    993               views::ViewModelUtils::VERTICAL,
    994           x, y);
    995   target_index =
    996       std::min(indices.second, std::max(target_index, indices.first));
    997   if (target_index == current_index)
    998     return;
    999 
   1000   // Change the model, the ShelfItemMoved() callback will handle the
   1001   // |view_model_| update.
   1002   model_->Move(current_index, target_index);
   1003   bounds_animator_->StopAnimatingView(drag_view_);
   1004 }
   1005 
   1006 bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) {
   1007   int current_index = view_model_->GetIndexOfView(drag_view_);
   1008   DCHECK_NE(-1, current_index);
   1009   std::string dragged_app_id =
   1010       delegate_->GetAppIDForShelfID(model_->items()[current_index].id);
   1011 
   1012   gfx::Point screen_location = event.root_location();
   1013   ash::wm::ConvertPointToScreen(GetWidget()->GetNativeWindow()->GetRootWindow(),
   1014                                 &screen_location);
   1015 
   1016   // To avoid ugly forwards and backwards flipping we use different constants
   1017   // for ripping off / re-inserting the items.
   1018   if (dragged_off_shelf_) {
   1019     // If the shelf/overflow bubble bounds contains |screen_location| we insert
   1020     // the item back into the shelf.
   1021     if (GetBoundsForDragInsertInScreen().Contains(screen_location)) {
   1022       if (dragged_off_from_overflow_to_shelf_) {
   1023         // During the dragging an item from Shelf to Overflow, it can enter here
   1024         // directly because both are located very closly.
   1025         main_shelf_->EndDrag(true);
   1026         // Stops the animation of |drag_view_| and sets its bounds explicitly
   1027         // becase ContinueDrag() stops its animation. Without this, unexpected
   1028         // bounds will be set.
   1029         bounds_animator_->StopAnimatingView(drag_view_);
   1030         int drag_view_index = view_model_->GetIndexOfView(drag_view_);
   1031         drag_view_->SetBoundsRect(view_model_->ideal_bounds(drag_view_index));
   1032         dragged_off_from_overflow_to_shelf_ = false;
   1033       }
   1034       // Destroy our proxy view item.
   1035       DestroyDragIconProxy();
   1036       // Re-insert the item and return simply false since the caller will handle
   1037       // the move as in any normal case.
   1038       dragged_off_shelf_ = false;
   1039       drag_view_->layer()->SetOpacity(1.0f);
   1040       // The size of Overflow bubble should be updated immediately when an item
   1041       // is re-inserted.
   1042       if (is_overflow_mode())
   1043         PreferredSizeChanged();
   1044       return false;
   1045     } else if (is_overflow_mode() &&
   1046                main_shelf_->GetBoundsForDragInsertInScreen().Contains(
   1047                    screen_location)) {
   1048       if (!dragged_off_from_overflow_to_shelf_) {
   1049         dragged_off_from_overflow_to_shelf_ = true;
   1050         drag_image_->SetOpacity(1.0f);
   1051         main_shelf_->StartDrag(dragged_app_id, screen_location);
   1052       } else {
   1053         main_shelf_->Drag(screen_location);
   1054       }
   1055     } else if (dragged_off_from_overflow_to_shelf_) {
   1056       // Makes the |drag_image_| partially disappear again.
   1057       dragged_off_from_overflow_to_shelf_ = false;
   1058       drag_image_->SetOpacity(kDraggedImageOpacity);
   1059       main_shelf_->EndDrag(true);
   1060       bounds_animator_->StopAnimatingView(drag_view_);
   1061       int drag_view_index = view_model_->GetIndexOfView(drag_view_);
   1062       drag_view_->SetBoundsRect(view_model_->ideal_bounds(drag_view_index));
   1063     }
   1064     // Move our proxy view item.
   1065     UpdateDragIconProxy(screen_location);
   1066     return true;
   1067   }
   1068   // Check if we are too far away from the shelf to enter the ripped off state.
   1069   // Determine the distance to the shelf.
   1070   int delta = CalculateShelfDistance(screen_location);
   1071   if (delta > kRipOffDistance) {
   1072     // Create a proxy view item which can be moved anywhere.
   1073     ShelfButton* button = static_cast<ShelfButton*>(drag_view_);
   1074     CreateDragIconProxy(event.root_location(),
   1075                         button->GetImage(),
   1076                         drag_view_,
   1077                         gfx::Vector2d(0, 0),
   1078                         kDragAndDropProxyScale);
   1079     drag_view_->layer()->SetOpacity(0.0f);
   1080     dragged_off_shelf_ = true;
   1081     if (RemovableByRipOff(current_index) == REMOVABLE) {
   1082       // Move the item to the front of the first panel item and hide it.
   1083       // ShelfItemMoved() callback will handle the |view_model_| update and
   1084       // call AnimateToIdealBounds().
   1085       if (current_index != model_->FirstPanelIndex() - 1) {
   1086         model_->Move(current_index, model_->FirstPanelIndex() - 1);
   1087         StartFadeInLastVisibleItem();
   1088       } else if (is_overflow_mode()) {
   1089         // Overflow bubble should be shrunk when an item is ripped off.
   1090         PreferredSizeChanged();
   1091       }
   1092       // Make the item partially disappear to show that it will get removed if
   1093       // dropped.
   1094       drag_image_->SetOpacity(kDraggedImageOpacity);
   1095     }
   1096     return true;
   1097   }
   1098   return false;
   1099 }
   1100 
   1101 void ShelfView::FinalizeRipOffDrag(bool cancel) {
   1102   if (!dragged_off_shelf_)
   1103     return;
   1104   // Make sure we do not come in here again.
   1105   dragged_off_shelf_ = false;
   1106 
   1107   // Coming here we should always have a |drag_view_|.
   1108   DCHECK(drag_view_);
   1109   int current_index = view_model_->GetIndexOfView(drag_view_);
   1110   // If the view isn't part of the model anymore (|current_index| == -1), a sync
   1111   // operation must have removed it. In that case we shouldn't change the model
   1112   // and only delete the proxy image.
   1113   if (current_index == -1) {
   1114     DestroyDragIconProxy();
   1115     return;
   1116   }
   1117 
   1118   // Set to true when the animation should snap back to where it was before.
   1119   bool snap_back = false;
   1120   // Items which cannot be dragged off will be handled as a cancel.
   1121   if (!cancel) {
   1122     if (dragged_off_from_overflow_to_shelf_) {
   1123       dragged_off_from_overflow_to_shelf_ = false;
   1124       main_shelf_->EndDrag(false);
   1125       drag_view_->layer()->SetOpacity(1.0f);
   1126     } else if (RemovableByRipOff(current_index) != REMOVABLE) {
   1127       // Make sure we do not try to remove un-removable items like items which
   1128       // were not pinned or have to be always there.
   1129       cancel = true;
   1130       snap_back = true;
   1131     } else {
   1132       // Make sure the item stays invisible upon removal.
   1133       drag_view_->SetVisible(false);
   1134       std::string app_id =
   1135           delegate_->GetAppIDForShelfID(model_->items()[current_index].id);
   1136       delegate_->UnpinAppWithID(app_id);
   1137     }
   1138   }
   1139   if (cancel || snap_back) {
   1140     if (dragged_off_from_overflow_to_shelf_) {
   1141       dragged_off_from_overflow_to_shelf_ = false;
   1142       // Main shelf handles revert of dragged item.
   1143       main_shelf_->EndDrag(true);
   1144       drag_view_->layer()->SetOpacity(1.0f);
   1145     } else if (!cancelling_drag_model_changed_) {
   1146       // Only do something if the change did not come through a model change.
   1147       gfx::Rect drag_bounds = drag_image_->GetBoundsInScreen();
   1148       gfx::Point relative_to = GetBoundsInScreen().origin();
   1149       gfx::Rect target(
   1150           gfx::PointAtOffsetFromOrigin(drag_bounds.origin()- relative_to),
   1151           drag_bounds.size());
   1152       drag_view_->SetBoundsRect(target);
   1153       // Hide the status from the active item since we snap it back now. Upon
   1154       // animation end the flag gets cleared if |snap_back_from_rip_off_view_|
   1155       // is set.
   1156       snap_back_from_rip_off_view_ = drag_view_;
   1157       ShelfButton* button = static_cast<ShelfButton*>(drag_view_);
   1158       button->AddState(ShelfButton::STATE_HIDDEN);
   1159       // When a canceling drag model is happening, the view model is diverged
   1160       // from the menu model and movements / animations should not be done.
   1161       model_->Move(current_index, start_drag_index_);
   1162       AnimateToIdealBounds();
   1163     }
   1164     drag_view_->layer()->SetOpacity(1.0f);
   1165   }
   1166   DestroyDragIconProxy();
   1167 }
   1168 
   1169 ShelfView::RemovableState ShelfView::RemovableByRipOff(int index) const {
   1170   DCHECK(index >= 0 && index < model_->item_count());
   1171   ShelfItemType type = model_->items()[index].type;
   1172   if (type == TYPE_APP_LIST || type == TYPE_DIALOG || !delegate_->CanPin())
   1173     return NOT_REMOVABLE;
   1174 
   1175   std::string app_id = delegate_->GetAppIDForShelfID(model_->items()[index].id);
   1176   // Note: Only pinned app shortcuts can be removed!
   1177   return (type == TYPE_APP_SHORTCUT && delegate_->IsAppPinned(app_id)) ?
   1178       REMOVABLE : DRAGGABLE;
   1179 }
   1180 
   1181 bool ShelfView::SameDragType(ShelfItemType typea, ShelfItemType typeb) const {
   1182   switch (typea) {
   1183     case TYPE_APP_SHORTCUT:
   1184     case TYPE_BROWSER_SHORTCUT:
   1185       return (typeb == TYPE_APP_SHORTCUT || typeb == TYPE_BROWSER_SHORTCUT);
   1186     case TYPE_APP_LIST:
   1187     case TYPE_PLATFORM_APP:
   1188     case TYPE_WINDOWED_APP:
   1189     case TYPE_APP_PANEL:
   1190     case TYPE_DIALOG:
   1191       return typeb == typea;
   1192     case TYPE_UNDEFINED:
   1193       NOTREACHED() << "ShelfItemType must be set.";
   1194       return false;
   1195   }
   1196   NOTREACHED();
   1197   return false;
   1198 }
   1199 
   1200 std::pair<int, int> ShelfView::GetDragRange(int index) {
   1201   int min_index = -1;
   1202   int max_index = -1;
   1203   ShelfItemType type = model_->items()[index].type;
   1204   for (int i = 0; i < model_->item_count(); ++i) {
   1205     if (SameDragType(model_->items()[i].type, type)) {
   1206       if (min_index == -1)
   1207         min_index = i;
   1208       max_index = i;
   1209     }
   1210   }
   1211   return std::pair<int, int>(min_index, max_index);
   1212 }
   1213 
   1214 void ShelfView::ConfigureChildView(views::View* view) {
   1215   view->SetPaintToLayer(true);
   1216   view->layer()->SetFillsBoundsOpaquely(false);
   1217 }
   1218 
   1219 void ShelfView::ToggleOverflowBubble() {
   1220   if (IsShowingOverflowBubble()) {
   1221     overflow_bubble_->Hide();
   1222     return;
   1223   }
   1224 
   1225   if (!overflow_bubble_)
   1226     overflow_bubble_.reset(new OverflowBubble());
   1227 
   1228   ShelfView* overflow_view =
   1229       new ShelfView(model_, delegate_, layout_manager_);
   1230   overflow_view->overflow_mode_ = true;
   1231   overflow_view->Init();
   1232   overflow_view->set_owner_overflow_bubble(overflow_bubble_.get());
   1233   overflow_view->OnShelfAlignmentChanged();
   1234   overflow_view->main_shelf_ = this;
   1235   UpdateOverflowRange(overflow_view);
   1236 
   1237   overflow_bubble_->Show(overflow_button_, overflow_view);
   1238 
   1239   Shell::GetInstance()->UpdateShelfVisibility();
   1240 }
   1241 
   1242 void ShelfView::OnFadeOutAnimationEnded() {
   1243   AnimateToIdealBounds();
   1244   StartFadeInLastVisibleItem();
   1245 }
   1246 
   1247 void ShelfView::StartFadeInLastVisibleItem() {
   1248   // If overflow button is visible and there is a valid new last item, fading
   1249   // the new last item in after sliding animation is finished.
   1250   if (overflow_button_->visible() && last_visible_index_ >= 0) {
   1251     views::View* last_visible_view = view_model_->view_at(last_visible_index_);
   1252     last_visible_view->layer()->SetOpacity(0);
   1253     bounds_animator_->SetAnimationDelegate(
   1254         last_visible_view,
   1255         scoped_ptr<gfx::AnimationDelegate>(
   1256             new StartFadeAnimationDelegate(this, last_visible_view)));
   1257   }
   1258 }
   1259 
   1260 void ShelfView::UpdateOverflowRange(ShelfView* overflow_view) const {
   1261   const int first_overflow_index = last_visible_index_ + 1;
   1262   const int last_overflow_index = last_hidden_index_;
   1263   DCHECK_LE(first_overflow_index, last_overflow_index);
   1264   DCHECK_LT(last_overflow_index, view_model_->view_size());
   1265 
   1266   overflow_view->first_visible_index_ = first_overflow_index;
   1267   overflow_view->last_visible_index_ = last_overflow_index;
   1268 }
   1269 
   1270 bool ShelfView::ShouldHideTooltip(const gfx::Point& cursor_location) {
   1271   gfx::Rect active_bounds;
   1272 
   1273   for (int i = 0; i < child_count(); ++i) {
   1274     views::View* child = child_at(i);
   1275     if (child == overflow_button_)
   1276       continue;
   1277     if (!ShouldShowTooltipForView(child))
   1278       continue;
   1279 
   1280     gfx::Rect child_bounds = child->GetMirroredBounds();
   1281     active_bounds.Union(child_bounds);
   1282   }
   1283 
   1284   return !active_bounds.Contains(cursor_location);
   1285 }
   1286 
   1287 gfx::Rect ShelfView::GetVisibleItemsBoundsInScreen() {
   1288   gfx::Size preferred_size = GetPreferredSize();
   1289   gfx::Point origin(GetMirroredXWithWidthInView(0, preferred_size.width()), 0);
   1290   ConvertPointToScreen(this, &origin);
   1291   return gfx::Rect(origin, preferred_size);
   1292 }
   1293 
   1294 gfx::Rect ShelfView::GetBoundsForDragInsertInScreen() {
   1295   gfx::Size preferred_size;
   1296   if (is_overflow_mode()) {
   1297     DCHECK(owner_overflow_bubble_);
   1298     gfx::Rect bubble_bounds =
   1299         owner_overflow_bubble_->bubble_view()->GetBubbleBounds();
   1300     preferred_size = bubble_bounds.size();
   1301   } else {
   1302     const int last_button_index = view_model_->view_size() - 1;
   1303     gfx::Rect last_button_bounds =
   1304         view_model_->view_at(last_button_index)->bounds();
   1305     if (overflow_button_->visible() &&
   1306         model_->GetItemIndexForType(TYPE_APP_PANEL) == -1) {
   1307       // When overflow button is visible and shelf has no panel items,
   1308       // last_button_bounds should be overflow button's bounds.
   1309       last_button_bounds = overflow_button_->bounds();
   1310     }
   1311 
   1312     if (layout_manager_->IsHorizontalAlignment()) {
   1313       preferred_size = gfx::Size(last_button_bounds.right() + leading_inset_,
   1314                                  kShelfSize);
   1315     } else {
   1316       preferred_size = gfx::Size(kShelfSize,
   1317                                  last_button_bounds.bottom() + leading_inset_);
   1318     }
   1319   }
   1320   gfx::Point origin(GetMirroredXWithWidthInView(0, preferred_size.width()), 0);
   1321 
   1322   // In overflow mode, we should use OverflowBubbleView as a source for
   1323   // converting |origin| to screen coordinates. When a scroll operation is
   1324   // occurred in OverflowBubble, the bounds of ShelfView in OverflowBubble can
   1325   // be changed.
   1326   if (is_overflow_mode())
   1327     ConvertPointToScreen(owner_overflow_bubble_->bubble_view(), &origin);
   1328   else
   1329     ConvertPointToScreen(this, &origin);
   1330 
   1331   return gfx::Rect(origin, preferred_size);
   1332 }
   1333 
   1334 int ShelfView::CancelDrag(int modified_index) {
   1335   FinalizeRipOffDrag(true);
   1336   if (!drag_view_)
   1337     return modified_index;
   1338   bool was_dragging = dragging();
   1339   int drag_view_index = view_model_->GetIndexOfView(drag_view_);
   1340   drag_pointer_ = NONE;
   1341   drag_view_ = NULL;
   1342   if (drag_view_index == modified_index) {
   1343     // The view that was being dragged is being modified. Don't do anything.
   1344     return modified_index;
   1345   }
   1346   if (!was_dragging)
   1347     return modified_index;
   1348 
   1349   // Restore previous position, tracking the position of the modified view.
   1350   bool at_end = modified_index == view_model_->view_size();
   1351   views::View* modified_view =
   1352       (modified_index >= 0 && !at_end) ?
   1353       view_model_->view_at(modified_index) : NULL;
   1354   model_->Move(drag_view_index, start_drag_index_);
   1355 
   1356   // If the modified view will be at the end of the list, return the new end of
   1357   // the list.
   1358   if (at_end)
   1359     return view_model_->view_size();
   1360   return modified_view ? view_model_->GetIndexOfView(modified_view) : -1;
   1361 }
   1362 
   1363 gfx::Size ShelfView::GetPreferredSize() const {
   1364   IdealBounds ideal_bounds;
   1365   CalculateIdealBounds(&ideal_bounds);
   1366 
   1367   int last_button_index = is_overflow_mode() ?
   1368       last_visible_index_ : view_model_->view_size() - 1;
   1369 
   1370   // When an item is dragged off from the overflow bubble, it is moved to last
   1371   // position and and changed to invisible. Overflow bubble size should be
   1372   // shrunk to fit only for visible items.
   1373   // If |dragged_off_from_overflow_to_shelf_| is set, there will be no invisible
   1374   // items in the shelf.
   1375   if (is_overflow_mode() &&
   1376       dragged_off_shelf_ &&
   1377       !dragged_off_from_overflow_to_shelf_ &&
   1378       RemovableByRipOff(view_model_->GetIndexOfView(drag_view_)) == REMOVABLE)
   1379     last_button_index--;
   1380 
   1381   const gfx::Rect last_button_bounds =
   1382       last_button_index  >= first_visible_index_ ?
   1383           view_model_->ideal_bounds(last_button_index) :
   1384           gfx::Rect(gfx::Size(kShelfSize, kShelfSize));
   1385 
   1386   if (layout_manager_->IsHorizontalAlignment()) {
   1387     return gfx::Size(last_button_bounds.right() + leading_inset_, kShelfSize);
   1388   }
   1389 
   1390   return gfx::Size(kShelfSize,
   1391                    last_button_bounds.bottom() + leading_inset_);
   1392 }
   1393 
   1394 void ShelfView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
   1395   // This bounds change is produced by the shelf movement and all content has
   1396   // to follow. Using an animation at that time would produce a time lag since
   1397   // the animation of the BoundsAnimator has itself a delay before it arrives
   1398   // at the required location. As such we tell the animator to go there
   1399   // immediately.
   1400   BoundsAnimatorDisabler disabler(bounds_animator_.get());
   1401   LayoutToIdealBounds();
   1402   FOR_EACH_OBSERVER(ShelfIconObserver, observers_,
   1403                     OnShelfIconPositionsChanged());
   1404 
   1405   if (IsShowingOverflowBubble())
   1406     overflow_bubble_->Hide();
   1407 }
   1408 
   1409 views::FocusTraversable* ShelfView::GetPaneFocusTraversable() {
   1410   return this;
   1411 }
   1412 
   1413 void ShelfView::GetAccessibleState(ui::AXViewState* state) {
   1414   state->role = ui::AX_ROLE_TOOLBAR;
   1415   state->name = l10n_util::GetStringUTF16(IDS_ASH_SHELF_ACCESSIBLE_NAME);
   1416 }
   1417 
   1418 void ShelfView::OnGestureEvent(ui::GestureEvent* event) {
   1419   if (gesture_handler_.ProcessGestureEvent(*event))
   1420     event->StopPropagation();
   1421 }
   1422 
   1423 void ShelfView::ShelfItemAdded(int model_index) {
   1424   {
   1425     base::AutoReset<bool> cancelling_drag(
   1426         &cancelling_drag_model_changed_, true);
   1427     model_index = CancelDrag(model_index);
   1428   }
   1429   views::View* view = CreateViewForItem(model_->items()[model_index]);
   1430   AddChildView(view);
   1431   // Hide the view, it'll be made visible when the animation is done. Using
   1432   // opacity 0 here to avoid messing with CalculateIdealBounds which touches
   1433   // the view's visibility.
   1434   view->layer()->SetOpacity(0);
   1435   view_model_->Add(view, model_index);
   1436 
   1437   // Give the button its ideal bounds. That way if we end up animating the
   1438   // button before this animation completes it doesn't appear at some random
   1439   // spot (because it was in the middle of animating from 0,0 0x0 to its
   1440   // target).
   1441   IdealBounds ideal_bounds;
   1442   CalculateIdealBounds(&ideal_bounds);
   1443   view->SetBoundsRect(view_model_->ideal_bounds(model_index));
   1444 
   1445   // The first animation moves all the views to their target position. |view|
   1446   // is hidden, so it visually appears as though we are providing space for
   1447   // it. When done we'll fade the view in.
   1448   AnimateToIdealBounds();
   1449   if (model_index <= last_visible_index_ ||
   1450       model_index >= model_->FirstPanelIndex()) {
   1451     bounds_animator_->SetAnimationDelegate(
   1452         view,
   1453         scoped_ptr<gfx::AnimationDelegate>(
   1454             new StartFadeAnimationDelegate(this, view)));
   1455   } else {
   1456     // Undo the hiding if animation does not run.
   1457     view->layer()->SetOpacity(1.0f);
   1458   }
   1459 }
   1460 
   1461 void ShelfView::ShelfItemRemoved(int model_index, ShelfID id) {
   1462   if (id == context_menu_id_)
   1463     launcher_menu_runner_.reset();
   1464   {
   1465     base::AutoReset<bool> cancelling_drag(
   1466         &cancelling_drag_model_changed_, true);
   1467     model_index = CancelDrag(model_index);
   1468   }
   1469   views::View* view = view_model_->view_at(model_index);
   1470   view_model_->Remove(model_index);
   1471 
   1472   // When the overflow bubble is visible, the overflow range needs to be set
   1473   // before CalculateIdealBounds() gets called. Otherwise CalculateIdealBounds()
   1474   // could trigger a ShelfItemChanged() by hiding the overflow bubble and
   1475   // since the overflow bubble is not yet synced with the ShelfModel this
   1476   // could cause a crash.
   1477   if (overflow_bubble_ && overflow_bubble_->IsShowing()) {
   1478     last_hidden_index_ = std::min(last_hidden_index_,
   1479                                   view_model_->view_size() - 1);
   1480     UpdateOverflowRange(overflow_bubble_->shelf_view());
   1481   }
   1482 
   1483   if (view->visible()) {
   1484     // The first animation fades out the view. When done we'll animate the rest
   1485     // of the views to their target location.
   1486     bounds_animator_->AnimateViewTo(view, view->bounds());
   1487     bounds_animator_->SetAnimationDelegate(
   1488         view,
   1489         scoped_ptr<gfx::AnimationDelegate>(
   1490             new FadeOutAnimationDelegate(this, view)));
   1491   } else {
   1492     // We don't need to show a fade out animation for invisible |view|. When an
   1493     // item is ripped out from the shelf, its |view| is already invisible.
   1494     AnimateToIdealBounds();
   1495   }
   1496 
   1497   // Close the tooltip because it isn't needed any longer and its anchor view
   1498   // will be deleted soon.
   1499   if (tooltip_->GetCurrentAnchorView() == view)
   1500     tooltip_->Close();
   1501 }
   1502 
   1503 void ShelfView::ShelfItemChanged(int model_index, const ShelfItem& old_item) {
   1504   const ShelfItem& item(model_->items()[model_index]);
   1505   if (old_item.type != item.type) {
   1506     // Type changed, swap the views.
   1507     model_index = CancelDrag(model_index);
   1508     scoped_ptr<views::View> old_view(view_model_->view_at(model_index));
   1509     bounds_animator_->StopAnimatingView(old_view.get());
   1510     // Removing and re-inserting a view in our view model will strip the ideal
   1511     // bounds from the item. To avoid recalculation of everything the bounds
   1512     // get remembered and restored after the insertion to the previous value.
   1513     gfx::Rect old_ideal_bounds = view_model_->ideal_bounds(model_index);
   1514     view_model_->Remove(model_index);
   1515     views::View* new_view = CreateViewForItem(item);
   1516     AddChildView(new_view);
   1517     view_model_->Add(new_view, model_index);
   1518     view_model_->set_ideal_bounds(model_index, old_ideal_bounds);
   1519     new_view->SetBoundsRect(old_view->bounds());
   1520     return;
   1521   }
   1522 
   1523   views::View* view = view_model_->view_at(model_index);
   1524   switch (item.type) {
   1525     case TYPE_BROWSER_SHORTCUT:
   1526       // Fallthrough for the new Shelf since it needs to show the activation
   1527       // change as well.
   1528     case TYPE_APP_SHORTCUT:
   1529     case TYPE_WINDOWED_APP:
   1530     case TYPE_PLATFORM_APP:
   1531     case TYPE_DIALOG:
   1532     case TYPE_APP_PANEL: {
   1533       ShelfButton* button = static_cast<ShelfButton*>(view);
   1534       ReflectItemStatus(item, button);
   1535       // The browser shortcut is currently not a "real" item and as such the
   1536       // the image is bogous as well. We therefore keep the image as is for it.
   1537       if (item.type != TYPE_BROWSER_SHORTCUT)
   1538         button->SetImage(item.image);
   1539       button->SchedulePaint();
   1540       break;
   1541     }
   1542 
   1543     default:
   1544       break;
   1545   }
   1546 }
   1547 
   1548 void ShelfView::ShelfItemMoved(int start_index, int target_index) {
   1549   view_model_->Move(start_index, target_index);
   1550   // When cancelling a drag due to a shelf item being added, the currently
   1551   // dragged item is moved back to its initial position. AnimateToIdealBounds
   1552   // will be called again when the new item is added to the |view_model_| but
   1553   // at this time the |view_model_| is inconsistent with the |model_|.
   1554   if (!cancelling_drag_model_changed_)
   1555     AnimateToIdealBounds();
   1556 }
   1557 
   1558 void ShelfView::ShelfStatusChanged() {
   1559   // Nothing to do here.
   1560 }
   1561 
   1562 void ShelfView::PointerPressedOnButton(views::View* view,
   1563                                        Pointer pointer,
   1564                                        const ui::LocatedEvent& event) {
   1565   if (drag_view_)
   1566     return;
   1567 
   1568   int index = view_model_->GetIndexOfView(view);
   1569   if (index == -1)
   1570     return;
   1571 
   1572   ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
   1573       model_->items()[index].id);
   1574   if (view_model_->view_size() <= 1 || !item_delegate->IsDraggable())
   1575     return;  // View is being deleted or not draggable, ignore request.
   1576 
   1577   drag_view_ = view;
   1578   drag_origin_ = gfx::Point(event.x(), event.y());
   1579   UMA_HISTOGRAM_ENUMERATION("Ash.ShelfAlignmentUsage",
   1580       layout_manager_->SelectValueForShelfAlignment(
   1581           SHELF_ALIGNMENT_UMA_ENUM_VALUE_BOTTOM,
   1582           SHELF_ALIGNMENT_UMA_ENUM_VALUE_LEFT,
   1583           SHELF_ALIGNMENT_UMA_ENUM_VALUE_RIGHT,
   1584           -1),
   1585       SHELF_ALIGNMENT_UMA_ENUM_VALUE_COUNT);
   1586 }
   1587 
   1588 void ShelfView::PointerDraggedOnButton(views::View* view,
   1589                                        Pointer pointer,
   1590                                        const ui::LocatedEvent& event) {
   1591   // To prepare all drag types (moving an item in the shelf and dragging off),
   1592   // we should check the x-axis and y-axis offset.
   1593   if (!dragging() && drag_view_ &&
   1594       ((std::abs(event.x() - drag_origin_.x()) >= kMinimumDragDistance) ||
   1595        (std::abs(event.y() - drag_origin_.y()) >= kMinimumDragDistance))) {
   1596     PrepareForDrag(pointer, event);
   1597   }
   1598   if (drag_pointer_ == pointer)
   1599     ContinueDrag(event);
   1600 }
   1601 
   1602 void ShelfView::PointerReleasedOnButton(views::View* view,
   1603                                         Pointer pointer,
   1604                                         bool canceled) {
   1605   if (canceled) {
   1606     CancelDrag(-1);
   1607   } else if (drag_pointer_ == pointer) {
   1608     FinalizeRipOffDrag(false);
   1609     drag_pointer_ = NONE;
   1610     AnimateToIdealBounds();
   1611   }
   1612   // If the drag pointer is NONE, no drag operation is going on and the
   1613   // drag_view can be released.
   1614   if (drag_pointer_ == NONE)
   1615     drag_view_ = NULL;
   1616 }
   1617 
   1618 void ShelfView::MouseMovedOverButton(views::View* view) {
   1619   if (!ShouldShowTooltipForView(view))
   1620     return;
   1621 
   1622   if (!tooltip_->IsVisible())
   1623     tooltip_->ResetTimer();
   1624 }
   1625 
   1626 void ShelfView::MouseEnteredButton(views::View* view) {
   1627   if (!ShouldShowTooltipForView(view))
   1628     return;
   1629 
   1630   if (tooltip_->IsVisible()) {
   1631     tooltip_->ShowImmediately(view, GetAccessibleName(view));
   1632   } else {
   1633     tooltip_->ShowDelayed(view, GetAccessibleName(view));
   1634   }
   1635 }
   1636 
   1637 void ShelfView::MouseExitedButton(views::View* view) {
   1638   if (!tooltip_->IsVisible())
   1639     tooltip_->StopTimer();
   1640 }
   1641 
   1642 base::string16 ShelfView::GetAccessibleName(const views::View* view) {
   1643   int view_index = view_model_->GetIndexOfView(view);
   1644   // May be -1 while in the process of animating closed.
   1645   if (view_index == -1)
   1646     return base::string16();
   1647 
   1648   ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
   1649       model_->items()[view_index].id);
   1650   return item_delegate->GetTitle();
   1651 }
   1652 
   1653 void ShelfView::ButtonPressed(views::Button* sender, const ui::Event& event) {
   1654   // Do not handle mouse release during drag.
   1655   if (dragging())
   1656     return;
   1657 
   1658   if (sender == overflow_button_) {
   1659     ToggleOverflowBubble();
   1660     return;
   1661   }
   1662 
   1663   int view_index = view_model_->GetIndexOfView(sender);
   1664   // May be -1 while in the process of animating closed.
   1665   if (view_index == -1)
   1666     return;
   1667 
   1668   // If the previous menu was closed by the same event as this one, we ignore
   1669   // the call.
   1670   if (!IsUsableEvent(event))
   1671     return;
   1672 
   1673   {
   1674     ScopedTargetRootWindow scoped_target(
   1675         sender->GetWidget()->GetNativeView()->GetRootWindow());
   1676     // Slow down activation animations if shift key is pressed.
   1677     scoped_ptr<ui::ScopedAnimationDurationScaleMode> slowing_animations;
   1678     if (event.IsShiftDown()) {
   1679       slowing_animations.reset(new ui::ScopedAnimationDurationScaleMode(
   1680             ui::ScopedAnimationDurationScaleMode::SLOW_DURATION));
   1681     }
   1682 
   1683     // Collect usage statistics before we decide what to do with the click.
   1684     switch (model_->items()[view_index].type) {
   1685       case TYPE_APP_SHORTCUT:
   1686       case TYPE_WINDOWED_APP:
   1687       case TYPE_PLATFORM_APP:
   1688       case TYPE_BROWSER_SHORTCUT:
   1689         Shell::GetInstance()->metrics()->RecordUserMetricsAction(
   1690             UMA_LAUNCHER_CLICK_ON_APP);
   1691         break;
   1692 
   1693       case TYPE_APP_LIST:
   1694         Shell::GetInstance()->metrics()->RecordUserMetricsAction(
   1695             UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON);
   1696         break;
   1697 
   1698       case TYPE_APP_PANEL:
   1699       case TYPE_DIALOG:
   1700         break;
   1701 
   1702       case TYPE_UNDEFINED:
   1703         NOTREACHED() << "ShelfItemType must be set.";
   1704         break;
   1705     }
   1706 
   1707     ShelfItemDelegate* item_delegate =
   1708         item_manager_->GetShelfItemDelegate(model_->items()[view_index].id);
   1709     if (!item_delegate->ItemSelected(event))
   1710       ShowListMenuForView(model_->items()[view_index], sender, event);
   1711   }
   1712 }
   1713 
   1714 bool ShelfView::ShowListMenuForView(const ShelfItem& item,
   1715                                     views::View* source,
   1716                                     const ui::Event& event) {
   1717   ShelfItemDelegate* item_delegate =
   1718       item_manager_->GetShelfItemDelegate(item.id);
   1719   list_menu_model_.reset(item_delegate->CreateApplicationMenu(event.flags()));
   1720 
   1721   // Make sure we have a menu and it has at least two items in addition to the
   1722   // application title and the 3 spacing separators.
   1723   if (!list_menu_model_.get() || list_menu_model_->GetItemCount() <= 5)
   1724     return false;
   1725 
   1726   ShowMenu(list_menu_model_.get(),
   1727            source,
   1728            gfx::Point(),
   1729            false,
   1730            ui::GetMenuSourceTypeForEvent(event));
   1731   return true;
   1732 }
   1733 
   1734 void ShelfView::ShowContextMenuForView(views::View* source,
   1735                                        const gfx::Point& point,
   1736                                        ui::MenuSourceType source_type) {
   1737   int view_index = view_model_->GetIndexOfView(source);
   1738   if (view_index == -1) {
   1739     Shell::GetInstance()->ShowContextMenu(point, source_type);
   1740     return;
   1741   }
   1742 
   1743   ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
   1744       model_->items()[view_index].id);
   1745   context_menu_model_.reset(item_delegate->CreateContextMenu(
   1746       source->GetWidget()->GetNativeView()->GetRootWindow()));
   1747   if (!context_menu_model_)
   1748     return;
   1749 
   1750   base::AutoReset<ShelfID> reseter(
   1751       &context_menu_id_,
   1752       view_index == -1 ? 0 : model_->items()[view_index].id);
   1753 
   1754   ShowMenu(context_menu_model_.get(),
   1755            source,
   1756            point,
   1757            true,
   1758            source_type);
   1759 }
   1760 
   1761 void ShelfView::ShowMenu(ui::MenuModel* menu_model,
   1762                          views::View* source,
   1763                          const gfx::Point& click_point,
   1764                          bool context_menu,
   1765                          ui::MenuSourceType source_type) {
   1766   closing_event_time_ = base::TimeDelta();
   1767   launcher_menu_runner_.reset(new views::MenuRunner(menu_model));
   1768 
   1769   ScopedTargetRootWindow scoped_target(
   1770       source->GetWidget()->GetNativeView()->GetRootWindow());
   1771 
   1772   // Determine the menu alignment dependent on the shelf.
   1773   views::MenuAnchorPosition menu_alignment = views::MENU_ANCHOR_TOPLEFT;
   1774   gfx::Rect anchor_point = gfx::Rect(click_point, gfx::Size());
   1775 
   1776   ShelfWidget* shelf = RootWindowController::ForShelf(
   1777       GetWidget()->GetNativeView())->shelf();
   1778   if (!context_menu) {
   1779     // Application lists use a bubble.
   1780     ShelfAlignment align = shelf->GetAlignment();
   1781     anchor_point = source->GetBoundsInScreen();
   1782 
   1783     // It is possible to invoke the menu while it is sliding into view. To cover
   1784     // that case, the screen coordinates are offsetted by the animation delta.
   1785     gfx::Vector2d offset =
   1786         source->GetWidget()->GetNativeWindow()->bounds().origin() -
   1787         source->GetWidget()->GetNativeWindow()->GetTargetBounds().origin();
   1788     anchor_point.set_x(anchor_point.x() - offset.x());
   1789     anchor_point.set_y(anchor_point.y() - offset.y());
   1790 
   1791     // Shelf items can have an asymmetrical border for spacing reasons.
   1792     // Adjust anchor location for this.
   1793     if (source->border())
   1794       anchor_point.Inset(source->border()->GetInsets());
   1795 
   1796     switch (align) {
   1797       case SHELF_ALIGNMENT_BOTTOM:
   1798         menu_alignment = views::MENU_ANCHOR_BUBBLE_ABOVE;
   1799         break;
   1800       case SHELF_ALIGNMENT_LEFT:
   1801         menu_alignment = views::MENU_ANCHOR_BUBBLE_RIGHT;
   1802         break;
   1803       case SHELF_ALIGNMENT_RIGHT:
   1804         menu_alignment = views::MENU_ANCHOR_BUBBLE_LEFT;
   1805         break;
   1806       case SHELF_ALIGNMENT_TOP:
   1807         menu_alignment = views::MENU_ANCHOR_BUBBLE_BELOW;
   1808         break;
   1809     }
   1810   }
   1811   // If this gets deleted while we are in the menu, the shelf will be gone
   1812   // as well.
   1813   bool got_deleted = false;
   1814   got_deleted_ = &got_deleted;
   1815 
   1816   shelf->ForceUndimming(true);
   1817   // NOTE: if you convert to HAS_MNEMONICS be sure and update menu building
   1818   // code.
   1819   if (launcher_menu_runner_->RunMenuAt(
   1820           source->GetWidget(),
   1821           NULL,
   1822           anchor_point,
   1823           menu_alignment,
   1824           source_type,
   1825           context_menu ? views::MenuRunner::CONTEXT_MENU : 0) ==
   1826       views::MenuRunner::MENU_DELETED) {
   1827     if (!got_deleted) {
   1828       got_deleted_ = NULL;
   1829       shelf->ForceUndimming(false);
   1830     }
   1831     return;
   1832   }
   1833   got_deleted_ = NULL;
   1834   shelf->ForceUndimming(false);
   1835 
   1836   // If it is a context menu and we are showing overflow bubble
   1837   // we want to hide overflow bubble.
   1838   if (owner_overflow_bubble_)
   1839     owner_overflow_bubble_->HideBubbleAndRefreshButton();
   1840 
   1841   // Unpinning an item will reset the |launcher_menu_runner_| before coming
   1842   // here.
   1843   if (launcher_menu_runner_)
   1844     closing_event_time_ = launcher_menu_runner_->closing_event_time();
   1845   Shell::GetInstance()->UpdateShelfVisibility();
   1846 }
   1847 
   1848 void ShelfView::OnBoundsAnimatorProgressed(views::BoundsAnimator* animator) {
   1849   FOR_EACH_OBSERVER(ShelfIconObserver, observers_,
   1850                     OnShelfIconPositionsChanged());
   1851   PreferredSizeChanged();
   1852 }
   1853 
   1854 void ShelfView::OnBoundsAnimatorDone(views::BoundsAnimator* animator) {
   1855   if (snap_back_from_rip_off_view_ && animator == bounds_animator_) {
   1856     if (!animator->IsAnimating(snap_back_from_rip_off_view_)) {
   1857       // Coming here the animation of the ShelfButton is finished and the
   1858       // previously hidden status can be shown again. Since the button itself
   1859       // might have gone away or changed locations we check that the button
   1860       // is still in the shelf and show its status again.
   1861       for (int index = 0; index < view_model_->view_size(); index++) {
   1862         views::View* view = view_model_->view_at(index);
   1863         if (view == snap_back_from_rip_off_view_) {
   1864           ShelfButton* button = static_cast<ShelfButton*>(view);
   1865           button->ClearState(ShelfButton::STATE_HIDDEN);
   1866           break;
   1867         }
   1868       }
   1869       snap_back_from_rip_off_view_ = NULL;
   1870     }
   1871   }
   1872 }
   1873 
   1874 bool ShelfView::IsUsableEvent(const ui::Event& event) {
   1875   if (closing_event_time_ == base::TimeDelta())
   1876     return true;
   1877 
   1878   base::TimeDelta delta =
   1879       base::TimeDelta(event.time_stamp() - closing_event_time_);
   1880   closing_event_time_ = base::TimeDelta();
   1881   // TODO(skuhne): This time seems excessive, but it appears that the reposting
   1882   // takes that long.  Need to come up with a better way of doing this.
   1883   return (delta.InMilliseconds() < 0 || delta.InMilliseconds() > 130);
   1884 }
   1885 
   1886 const ShelfItem* ShelfView::ShelfItemForView(const views::View* view) const {
   1887   int view_index = view_model_->GetIndexOfView(view);
   1888   if (view_index == -1)
   1889     return NULL;
   1890   return &(model_->items()[view_index]);
   1891 }
   1892 
   1893 bool ShelfView::ShouldShowTooltipForView(const views::View* view) const {
   1894   if (view == GetAppListButtonView() &&
   1895       Shell::GetInstance()->GetAppListWindow())
   1896     return false;
   1897   const ShelfItem* item = ShelfItemForView(view);
   1898   if (!item)
   1899     return true;
   1900   ShelfItemDelegate* item_delegate =
   1901       item_manager_->GetShelfItemDelegate(item->id);
   1902   return item_delegate->ShouldShowTooltip();
   1903 }
   1904 
   1905 int ShelfView::CalculateShelfDistance(const gfx::Point& coordinate) const {
   1906   ShelfWidget* shelf = RootWindowController::ForShelf(
   1907       GetWidget()->GetNativeView())->shelf();
   1908   ShelfAlignment align = shelf->GetAlignment();
   1909   const gfx::Rect bounds = GetBoundsInScreen();
   1910   int distance = 0;
   1911   switch (align) {
   1912     case SHELF_ALIGNMENT_BOTTOM:
   1913       distance = bounds.y() - coordinate.y();
   1914       break;
   1915     case SHELF_ALIGNMENT_LEFT:
   1916       distance = coordinate.x() - bounds.right();
   1917       break;
   1918     case SHELF_ALIGNMENT_RIGHT:
   1919       distance = bounds.x() - coordinate.x();
   1920       break;
   1921     case SHELF_ALIGNMENT_TOP:
   1922       distance = coordinate.y() - bounds.bottom();
   1923       break;
   1924   }
   1925   return distance > 0 ? distance : 0;
   1926 }
   1927 
   1928 }  // namespace ash
   1929