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_strings.h"
     36 #include "ui/accessibility/ax_view_state.h"
     37 #include "ui/aura/client/screen_position_client.h"
     38 #include "ui/aura/window.h"
     39 #include "ui/aura/window_event_dispatcher.h"
     40 #include "ui/base/l10n/l10n_util.h"
     41 #include "ui/base/models/simple_menu_model.h"
     42 #include "ui/base/resource/resource_bundle.h"
     43 #include "ui/compositor/layer.h"
     44 #include "ui/compositor/layer_animator.h"
     45 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
     46 #include "ui/gfx/canvas.h"
     47 #include "ui/gfx/point.h"
     48 #include "ui/views/animation/bounds_animator.h"
     49 #include "ui/views/border.h"
     50 #include "ui/views/controls/button/image_button.h"
     51 #include "ui/views/controls/menu/menu_model_adapter.h"
     52 #include "ui/views/controls/menu/menu_runner.h"
     53 #include "ui/views/focus/focus_search.h"
     54 #include "ui/views/view_model.h"
     55 #include "ui/views/view_model_utils.h"
     56 #include "ui/views/widget/widget.h"
     57 #include "ui/wm/core/coordinate_conversion.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   explicit 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   ::wm::ConvertPointFromScreen(
    621       ash::wm::GetRootWindowAt(location_in_screen_coordinates), &point_in_root);
    622   ui::MouseEvent event(ui::ET_MOUSE_PRESSED, pt, point_in_root, 0, 0);
    623   PointerPressedOnButton(drag_and_drop_view,
    624                          ShelfButtonHost::DRAG_AND_DROP,
    625                          event);
    626 
    627   // Drag the item where it really belongs.
    628   Drag(location_in_screen_coordinates);
    629   return true;
    630 }
    631 
    632 bool ShelfView::Drag(const gfx::Point& location_in_screen_coordinates) {
    633   if (!drag_and_drop_shelf_id_ ||
    634       !GetBoundsInScreen().Contains(location_in_screen_coordinates))
    635     return false;
    636 
    637   gfx::Point pt = location_in_screen_coordinates;
    638   views::View* drag_and_drop_view = view_model_->view_at(
    639       model_->ItemIndexByID(drag_and_drop_shelf_id_));
    640   ConvertPointFromScreen(drag_and_drop_view, &pt);
    641   gfx::Point point_in_root = location_in_screen_coordinates;
    642   ::wm::ConvertPointFromScreen(
    643       ash::wm::GetRootWindowAt(location_in_screen_coordinates), &point_in_root);
    644   ui::MouseEvent event(ui::ET_MOUSE_DRAGGED, pt, point_in_root, 0, 0);
    645   PointerDraggedOnButton(drag_and_drop_view,
    646                          ShelfButtonHost::DRAG_AND_DROP,
    647                          event);
    648   return true;
    649 }
    650 
    651 void ShelfView::EndDrag(bool cancel) {
    652   if (!drag_and_drop_shelf_id_)
    653     return;
    654 
    655   views::View* drag_and_drop_view = view_model_->view_at(
    656       model_->ItemIndexByID(drag_and_drop_shelf_id_));
    657   PointerReleasedOnButton(
    658       drag_and_drop_view, ShelfButtonHost::DRAG_AND_DROP, cancel);
    659 
    660   // Either destroy the temporarily created item - or - make the item visible.
    661   if (drag_and_drop_item_pinned_ && cancel) {
    662     delegate_->UnpinAppWithID(drag_and_drop_app_id_);
    663   } else if (drag_and_drop_view) {
    664     if (cancel) {
    665       // When a hosted drag gets canceled, the item can remain in the same slot
    666       // and it might have moved within the bounds. In that case the item need
    667       // to animate back to its correct location.
    668       AnimateToIdealBounds();
    669     } else {
    670       drag_and_drop_view->SetSize(pre_drag_and_drop_size_);
    671     }
    672   }
    673 
    674   drag_and_drop_shelf_id_ = 0;
    675 }
    676 
    677 void ShelfView::LayoutToIdealBounds() {
    678   if (bounds_animator_->IsAnimating()) {
    679     AnimateToIdealBounds();
    680     return;
    681   }
    682 
    683   IdealBounds ideal_bounds;
    684   CalculateIdealBounds(&ideal_bounds);
    685   views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_);
    686   overflow_button_->SetBoundsRect(ideal_bounds.overflow_bounds);
    687 }
    688 
    689 void ShelfView::UpdateAllButtonsVisibilityInOverflowMode() {
    690   // The overflow button is not shown in overflow mode.
    691   overflow_button_->SetVisible(false);
    692   DCHECK_LT(last_visible_index_, view_model_->view_size());
    693   for (int i = 0; i < view_model_->view_size(); ++i) {
    694     bool visible = i >= first_visible_index_ &&
    695         i <= last_visible_index_;
    696     // To track the dragging of |drag_view_| continuously, its visibility
    697     // should be always true regardless of its position.
    698     if (dragged_off_from_overflow_to_shelf_ &&
    699         view_model_->view_at(i) == drag_view_)
    700       view_model_->view_at(i)->SetVisible(true);
    701     else
    702       view_model_->view_at(i)->SetVisible(visible);
    703   }
    704 }
    705 
    706 void ShelfView::CalculateIdealBounds(IdealBounds* bounds) const {
    707   int available_size = layout_manager_->PrimaryAxisValue(width(), height());
    708   DCHECK(model_->item_count() == view_model_->view_size());
    709   if (!available_size)
    710     return;
    711 
    712   int first_panel_index = model_->FirstPanelIndex();
    713   int last_button_index = first_panel_index - 1;
    714 
    715   int x = 0;
    716   int y = 0;
    717   int button_size = kShelfButtonSize;
    718   int button_spacing = kShelfButtonSpacing;
    719 
    720   int w = layout_manager_->PrimaryAxisValue(button_size, width());
    721   int h = layout_manager_->PrimaryAxisValue(height(), button_size);
    722   for (int i = 0; i < view_model_->view_size(); ++i) {
    723     if (i < first_visible_index_) {
    724       view_model_->set_ideal_bounds(i, gfx::Rect(x, y, 0, 0));
    725       continue;
    726     }
    727 
    728     view_model_->set_ideal_bounds(i, gfx::Rect(x, y, w, h));
    729     if (i != last_button_index) {
    730       x = layout_manager_->PrimaryAxisValue(x + w + button_spacing, x);
    731       y = layout_manager_->PrimaryAxisValue(y, y + h + button_spacing);
    732     }
    733   }
    734 
    735   if (is_overflow_mode()) {
    736     const_cast<ShelfView*>(this)->UpdateAllButtonsVisibilityInOverflowMode();
    737     return;
    738   }
    739 
    740   // Right aligned icons.
    741   int end_position = available_size - button_spacing;
    742   x = layout_manager_->PrimaryAxisValue(end_position, 0);
    743   y = layout_manager_->PrimaryAxisValue(0, end_position);
    744   for (int i = view_model_->view_size() - 1;
    745        i >= first_panel_index; --i) {
    746     x = layout_manager_->PrimaryAxisValue(x - w - button_spacing, x);
    747     y = layout_manager_->PrimaryAxisValue(y, y - h - button_spacing);
    748     view_model_->set_ideal_bounds(i, gfx::Rect(x, y, w, h));
    749     end_position = layout_manager_->PrimaryAxisValue(x, y);
    750   }
    751 
    752   // Icons on the left / top are guaranteed up to kLeftIconProportion of
    753   // the available space.
    754   int last_icon_position = layout_manager_->PrimaryAxisValue(
    755       view_model_->ideal_bounds(last_button_index).right(),
    756       view_model_->ideal_bounds(last_button_index).bottom()) + button_size;
    757   int reserved_icon_space = available_size * kReservedNonPanelIconProportion;
    758   if (last_icon_position < reserved_icon_space)
    759     end_position = last_icon_position;
    760   else
    761     end_position = std::max(end_position, reserved_icon_space);
    762 
    763   bounds->overflow_bounds.set_size(
    764       gfx::Size(layout_manager_->PrimaryAxisValue(w, width()),
    765                 layout_manager_->PrimaryAxisValue(height(), h)));
    766 
    767   last_visible_index_ = DetermineLastVisibleIndex(
    768       end_position - button_size);
    769   last_hidden_index_ = DetermineFirstVisiblePanelIndex(end_position) - 1;
    770   bool show_overflow = last_visible_index_ < last_button_index ||
    771       last_hidden_index_ >= first_panel_index;
    772 
    773   // Create Space for the overflow button
    774   if (show_overflow &&
    775       last_visible_index_ > 0 && last_visible_index_ < last_button_index)
    776     --last_visible_index_;
    777   for (int i = 0; i < view_model_->view_size(); ++i) {
    778     bool visible = i <= last_visible_index_ || i > last_hidden_index_;
    779     // To receive drag event continuously from |drag_view_| during the dragging
    780     // off from the shelf, don't make |drag_view_| invisible. It will be
    781     // eventually invisible and removed from the |view_model_| by
    782     // FinalizeRipOffDrag().
    783     if (dragged_off_shelf_ && view_model_->view_at(i) == drag_view_)
    784       continue;
    785     view_model_->view_at(i)->SetVisible(visible);
    786   }
    787 
    788   overflow_button_->SetVisible(show_overflow);
    789   if (show_overflow) {
    790     DCHECK_NE(0, view_model_->view_size());
    791     if (last_visible_index_ == -1) {
    792       x = 0;
    793       y = 0;
    794     } else {
    795       x = layout_manager_->PrimaryAxisValue(
    796           view_model_->ideal_bounds(last_visible_index_).right(),
    797           view_model_->ideal_bounds(last_visible_index_).x());
    798       y = layout_manager_->PrimaryAxisValue(
    799           view_model_->ideal_bounds(last_visible_index_).y(),
    800           view_model_->ideal_bounds(last_visible_index_).bottom());
    801     }
    802     // Set all hidden panel icon positions to be on the overflow button.
    803     for (int i = first_panel_index; i <= last_hidden_index_; ++i)
    804       view_model_->set_ideal_bounds(i, gfx::Rect(x, y, w, h));
    805 
    806     // Add more space between last visible item and overflow button.
    807     // Without this, two buttons look too close compared with other items.
    808     x = layout_manager_->PrimaryAxisValue(x + button_spacing, x);
    809     y = layout_manager_->PrimaryAxisValue(y, y + button_spacing);
    810 
    811     bounds->overflow_bounds.set_x(x);
    812     bounds->overflow_bounds.set_y(y);
    813     if (overflow_bubble_.get() && overflow_bubble_->IsShowing())
    814       UpdateOverflowRange(overflow_bubble_->shelf_view());
    815   } else {
    816     if (overflow_bubble_)
    817       overflow_bubble_->Hide();
    818   }
    819 }
    820 
    821 int ShelfView::DetermineLastVisibleIndex(int max_value) const {
    822   int index = model_->FirstPanelIndex() - 1;
    823   while (index >= 0 &&
    824          layout_manager_->PrimaryAxisValue(
    825              view_model_->ideal_bounds(index).right(),
    826              view_model_->ideal_bounds(index).bottom()) > max_value) {
    827     index--;
    828   }
    829   return index;
    830 }
    831 
    832 int ShelfView::DetermineFirstVisiblePanelIndex(int min_value) const {
    833   int index = model_->FirstPanelIndex();
    834   while (index < view_model_->view_size() &&
    835          layout_manager_->PrimaryAxisValue(
    836              view_model_->ideal_bounds(index).right(),
    837              view_model_->ideal_bounds(index).bottom()) < min_value) {
    838     ++index;
    839   }
    840   return index;
    841 }
    842 
    843 void ShelfView::AddIconObserver(ShelfIconObserver* observer) {
    844   observers_.AddObserver(observer);
    845 }
    846 
    847 void ShelfView::RemoveIconObserver(ShelfIconObserver* observer) {
    848   observers_.RemoveObserver(observer);
    849 }
    850 
    851 void ShelfView::AnimateToIdealBounds() {
    852   IdealBounds ideal_bounds;
    853   CalculateIdealBounds(&ideal_bounds);
    854   for (int i = 0; i < view_model_->view_size(); ++i) {
    855     View* view = view_model_->view_at(i);
    856     bounds_animator_->AnimateViewTo(view, view_model_->ideal_bounds(i));
    857     // Now that the item animation starts, we have to make sure that the
    858     // padding of the first gets properly transferred to the new first item.
    859     if (i && view->border())
    860       view->SetBorder(views::Border::NullBorder());
    861   }
    862   overflow_button_->SetBoundsRect(ideal_bounds.overflow_bounds);
    863 }
    864 
    865 views::View* ShelfView::CreateViewForItem(const ShelfItem& item) {
    866   views::View* view = NULL;
    867   switch (item.type) {
    868     case TYPE_BROWSER_SHORTCUT:
    869     case TYPE_APP_SHORTCUT:
    870     case TYPE_WINDOWED_APP:
    871     case TYPE_PLATFORM_APP:
    872     case TYPE_DIALOG:
    873     case TYPE_APP_PANEL: {
    874       ShelfButton* button = ShelfButton::Create(this, this, layout_manager_);
    875       button->SetImage(item.image);
    876       ReflectItemStatus(item, button);
    877       view = button;
    878       break;
    879     }
    880 
    881     case TYPE_APP_LIST: {
    882       view = new AppListButton(this, this, layout_manager_->shelf_widget());
    883       break;
    884     }
    885 
    886     default:
    887       break;
    888   }
    889   view->set_context_menu_controller(this);
    890 
    891   DCHECK(view);
    892   ConfigureChildView(view);
    893   return view;
    894 }
    895 
    896 void ShelfView::FadeIn(views::View* view) {
    897   view->SetVisible(true);
    898   view->layer()->SetOpacity(0);
    899   AnimateToIdealBounds();
    900   bounds_animator_->SetAnimationDelegate(
    901       view,
    902       scoped_ptr<gfx::AnimationDelegate>(new FadeInAnimationDelegate(view)));
    903 }
    904 
    905 void ShelfView::PrepareForDrag(Pointer pointer, const ui::LocatedEvent& event) {
    906   DCHECK(!dragging());
    907   DCHECK(drag_view_);
    908   drag_pointer_ = pointer;
    909   start_drag_index_ = view_model_->GetIndexOfView(drag_view_);
    910 
    911   if (start_drag_index_== -1) {
    912     CancelDrag(-1);
    913     return;
    914   }
    915 
    916   // If the item is no longer draggable, bail out.
    917   ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
    918       model_->items()[start_drag_index_].id);
    919   if (!item_delegate->IsDraggable()) {
    920     CancelDrag(-1);
    921     return;
    922   }
    923 
    924   // Move the view to the front so that it appears on top of other views.
    925   ReorderChildView(drag_view_, -1);
    926   bounds_animator_->StopAnimatingView(drag_view_);
    927 }
    928 
    929 void ShelfView::ContinueDrag(const ui::LocatedEvent& event) {
    930   // Due to a syncing operation the application might have been removed.
    931   // Bail if it is gone.
    932   int current_index = view_model_->GetIndexOfView(drag_view_);
    933   DCHECK_NE(-1, current_index);
    934 
    935   ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
    936       model_->items()[current_index].id);
    937   if (!item_delegate->IsDraggable()) {
    938     CancelDrag(-1);
    939     return;
    940   }
    941 
    942   // If this is not a drag and drop host operation and not the app list item,
    943   // check if the item got ripped off the shelf - if it did we are done.
    944   if (!drag_and_drop_shelf_id_ &&
    945       RemovableByRipOff(current_index) != NOT_REMOVABLE) {
    946     if (HandleRipOffDrag(event))
    947       return;
    948     // The rip off handler could have changed the location of the item.
    949     current_index = view_model_->GetIndexOfView(drag_view_);
    950   }
    951 
    952   // TODO: I don't think this works correctly with RTL.
    953   gfx::Point drag_point(event.location());
    954   ConvertPointToTarget(drag_view_, this, &drag_point);
    955 
    956   // Constrain the location to the range of valid indices for the type.
    957   std::pair<int, int> indices(GetDragRange(current_index));
    958   int first_drag_index = indices.first;
    959   int last_drag_index = indices.second;
    960   // If the last index isn't valid, we're overflowing. Constrain to the app list
    961   // (which is the last visible item).
    962   if (first_drag_index < model_->FirstPanelIndex() &&
    963       last_drag_index > last_visible_index_)
    964     last_drag_index = last_visible_index_;
    965   int x = 0, y = 0;
    966   if (layout_manager_->IsHorizontalAlignment()) {
    967     x = std::max(view_model_->ideal_bounds(indices.first).x(),
    968                      drag_point.x() - drag_origin_.x());
    969     x = std::min(view_model_->ideal_bounds(last_drag_index).right() -
    970                  view_model_->ideal_bounds(current_index).width(),
    971                  x);
    972     if (drag_view_->x() == x)
    973       return;
    974     drag_view_->SetX(x);
    975   } else {
    976     y = std::max(view_model_->ideal_bounds(indices.first).y(),
    977                      drag_point.y() - drag_origin_.y());
    978     y = std::min(view_model_->ideal_bounds(last_drag_index).bottom() -
    979                  view_model_->ideal_bounds(current_index).height(),
    980                  y);
    981     if (drag_view_->y() == y)
    982       return;
    983     drag_view_->SetY(y);
    984   }
    985 
    986   int target_index =
    987       views::ViewModelUtils::DetermineMoveIndex(
    988           *view_model_, drag_view_,
    989           layout_manager_->IsHorizontalAlignment() ?
    990               views::ViewModelUtils::HORIZONTAL :
    991               views::ViewModelUtils::VERTICAL,
    992           x, y);
    993   target_index =
    994       std::min(indices.second, std::max(target_index, indices.first));
    995   if (target_index == current_index)
    996     return;
    997 
    998   // Change the model, the ShelfItemMoved() callback will handle the
    999   // |view_model_| update.
   1000   model_->Move(current_index, target_index);
   1001   bounds_animator_->StopAnimatingView(drag_view_);
   1002 }
   1003 
   1004 bool ShelfView::HandleRipOffDrag(const ui::LocatedEvent& event) {
   1005   int current_index = view_model_->GetIndexOfView(drag_view_);
   1006   DCHECK_NE(-1, current_index);
   1007   std::string dragged_app_id =
   1008       delegate_->GetAppIDForShelfID(model_->items()[current_index].id);
   1009 
   1010   gfx::Point screen_location = event.root_location();
   1011   ::wm::ConvertPointToScreen(GetWidget()->GetNativeWindow()->GetRootWindow(),
   1012                              &screen_location);
   1013 
   1014   // To avoid ugly forwards and backwards flipping we use different constants
   1015   // for ripping off / re-inserting the items.
   1016   if (dragged_off_shelf_) {
   1017     // If the shelf/overflow bubble bounds contains |screen_location| we insert
   1018     // the item back into the shelf.
   1019     if (GetBoundsForDragInsertInScreen().Contains(screen_location)) {
   1020       if (dragged_off_from_overflow_to_shelf_) {
   1021         // During the dragging an item from Shelf to Overflow, it can enter here
   1022         // directly because both are located very closly.
   1023         main_shelf_->EndDrag(true);
   1024         // Stops the animation of |drag_view_| and sets its bounds explicitly
   1025         // becase ContinueDrag() stops its animation. Without this, unexpected
   1026         // bounds will be set.
   1027         bounds_animator_->StopAnimatingView(drag_view_);
   1028         int drag_view_index = view_model_->GetIndexOfView(drag_view_);
   1029         drag_view_->SetBoundsRect(view_model_->ideal_bounds(drag_view_index));
   1030         dragged_off_from_overflow_to_shelf_ = false;
   1031       }
   1032       // Destroy our proxy view item.
   1033       DestroyDragIconProxy();
   1034       // Re-insert the item and return simply false since the caller will handle
   1035       // the move as in any normal case.
   1036       dragged_off_shelf_ = false;
   1037       drag_view_->layer()->SetOpacity(1.0f);
   1038       // The size of Overflow bubble should be updated immediately when an item
   1039       // is re-inserted.
   1040       if (is_overflow_mode())
   1041         PreferredSizeChanged();
   1042       return false;
   1043     } else if (is_overflow_mode() &&
   1044                main_shelf_->GetBoundsForDragInsertInScreen().Contains(
   1045                    screen_location)) {
   1046       if (!dragged_off_from_overflow_to_shelf_) {
   1047         dragged_off_from_overflow_to_shelf_ = true;
   1048         drag_image_->SetOpacity(1.0f);
   1049         main_shelf_->StartDrag(dragged_app_id, screen_location);
   1050       } else {
   1051         main_shelf_->Drag(screen_location);
   1052       }
   1053     } else if (dragged_off_from_overflow_to_shelf_) {
   1054       // Makes the |drag_image_| partially disappear again.
   1055       dragged_off_from_overflow_to_shelf_ = false;
   1056       drag_image_->SetOpacity(kDraggedImageOpacity);
   1057       main_shelf_->EndDrag(true);
   1058       bounds_animator_->StopAnimatingView(drag_view_);
   1059       int drag_view_index = view_model_->GetIndexOfView(drag_view_);
   1060       drag_view_->SetBoundsRect(view_model_->ideal_bounds(drag_view_index));
   1061     }
   1062     // Move our proxy view item.
   1063     UpdateDragIconProxy(screen_location);
   1064     return true;
   1065   }
   1066   // Check if we are too far away from the shelf to enter the ripped off state.
   1067   // Determine the distance to the shelf.
   1068   int delta = CalculateShelfDistance(screen_location);
   1069   if (delta > kRipOffDistance) {
   1070     // Create a proxy view item which can be moved anywhere.
   1071     ShelfButton* button = static_cast<ShelfButton*>(drag_view_);
   1072     CreateDragIconProxy(event.root_location(),
   1073                         button->GetImage(),
   1074                         drag_view_,
   1075                         gfx::Vector2d(0, 0),
   1076                         kDragAndDropProxyScale);
   1077     drag_view_->layer()->SetOpacity(0.0f);
   1078     dragged_off_shelf_ = true;
   1079     if (RemovableByRipOff(current_index) == REMOVABLE) {
   1080       // Move the item to the front of the first panel item and hide it.
   1081       // ShelfItemMoved() callback will handle the |view_model_| update and
   1082       // call AnimateToIdealBounds().
   1083       if (current_index != model_->FirstPanelIndex() - 1) {
   1084         model_->Move(current_index, model_->FirstPanelIndex() - 1);
   1085         StartFadeInLastVisibleItem();
   1086       } else if (is_overflow_mode()) {
   1087         // Overflow bubble should be shrunk when an item is ripped off.
   1088         PreferredSizeChanged();
   1089       }
   1090       // Make the item partially disappear to show that it will get removed if
   1091       // dropped.
   1092       drag_image_->SetOpacity(kDraggedImageOpacity);
   1093     }
   1094     return true;
   1095   }
   1096   return false;
   1097 }
   1098 
   1099 void ShelfView::FinalizeRipOffDrag(bool cancel) {
   1100   if (!dragged_off_shelf_)
   1101     return;
   1102   // Make sure we do not come in here again.
   1103   dragged_off_shelf_ = false;
   1104 
   1105   // Coming here we should always have a |drag_view_|.
   1106   DCHECK(drag_view_);
   1107   int current_index = view_model_->GetIndexOfView(drag_view_);
   1108   // If the view isn't part of the model anymore (|current_index| == -1), a sync
   1109   // operation must have removed it. In that case we shouldn't change the model
   1110   // and only delete the proxy image.
   1111   if (current_index == -1) {
   1112     DestroyDragIconProxy();
   1113     return;
   1114   }
   1115 
   1116   // Set to true when the animation should snap back to where it was before.
   1117   bool snap_back = false;
   1118   // Items which cannot be dragged off will be handled as a cancel.
   1119   if (!cancel) {
   1120     if (dragged_off_from_overflow_to_shelf_) {
   1121       dragged_off_from_overflow_to_shelf_ = false;
   1122       main_shelf_->EndDrag(false);
   1123       drag_view_->layer()->SetOpacity(1.0f);
   1124     } else if (RemovableByRipOff(current_index) != REMOVABLE) {
   1125       // Make sure we do not try to remove un-removable items like items which
   1126       // were not pinned or have to be always there.
   1127       cancel = true;
   1128       snap_back = true;
   1129     } else {
   1130       // Make sure the item stays invisible upon removal.
   1131       drag_view_->SetVisible(false);
   1132       std::string app_id =
   1133           delegate_->GetAppIDForShelfID(model_->items()[current_index].id);
   1134       delegate_->UnpinAppWithID(app_id);
   1135     }
   1136   }
   1137   if (cancel || snap_back) {
   1138     if (dragged_off_from_overflow_to_shelf_) {
   1139       dragged_off_from_overflow_to_shelf_ = false;
   1140       // Main shelf handles revert of dragged item.
   1141       main_shelf_->EndDrag(true);
   1142       drag_view_->layer()->SetOpacity(1.0f);
   1143     } else if (!cancelling_drag_model_changed_) {
   1144       // Only do something if the change did not come through a model change.
   1145       gfx::Rect drag_bounds = drag_image_->GetBoundsInScreen();
   1146       gfx::Point relative_to = GetBoundsInScreen().origin();
   1147       gfx::Rect target(
   1148           gfx::PointAtOffsetFromOrigin(drag_bounds.origin()- relative_to),
   1149           drag_bounds.size());
   1150       drag_view_->SetBoundsRect(target);
   1151       // Hide the status from the active item since we snap it back now. Upon
   1152       // animation end the flag gets cleared if |snap_back_from_rip_off_view_|
   1153       // is set.
   1154       snap_back_from_rip_off_view_ = drag_view_;
   1155       ShelfButton* button = static_cast<ShelfButton*>(drag_view_);
   1156       button->AddState(ShelfButton::STATE_HIDDEN);
   1157       // When a canceling drag model is happening, the view model is diverged
   1158       // from the menu model and movements / animations should not be done.
   1159       model_->Move(current_index, start_drag_index_);
   1160       AnimateToIdealBounds();
   1161     }
   1162     drag_view_->layer()->SetOpacity(1.0f);
   1163   }
   1164   DestroyDragIconProxy();
   1165 }
   1166 
   1167 ShelfView::RemovableState ShelfView::RemovableByRipOff(int index) const {
   1168   DCHECK(index >= 0 && index < model_->item_count());
   1169   ShelfItemType type = model_->items()[index].type;
   1170   if (type == TYPE_APP_LIST || type == TYPE_DIALOG || !delegate_->CanPin())
   1171     return NOT_REMOVABLE;
   1172 
   1173   std::string app_id = delegate_->GetAppIDForShelfID(model_->items()[index].id);
   1174   // Note: Only pinned app shortcuts can be removed!
   1175   return (type == TYPE_APP_SHORTCUT && delegate_->IsAppPinned(app_id)) ?
   1176       REMOVABLE : DRAGGABLE;
   1177 }
   1178 
   1179 bool ShelfView::SameDragType(ShelfItemType typea, ShelfItemType typeb) const {
   1180   switch (typea) {
   1181     case TYPE_APP_SHORTCUT:
   1182     case TYPE_BROWSER_SHORTCUT:
   1183       return (typeb == TYPE_APP_SHORTCUT || typeb == TYPE_BROWSER_SHORTCUT);
   1184     case TYPE_APP_LIST:
   1185     case TYPE_PLATFORM_APP:
   1186     case TYPE_WINDOWED_APP:
   1187     case TYPE_APP_PANEL:
   1188     case TYPE_DIALOG:
   1189       return typeb == typea;
   1190     case TYPE_UNDEFINED:
   1191       NOTREACHED() << "ShelfItemType must be set.";
   1192       return false;
   1193   }
   1194   NOTREACHED();
   1195   return false;
   1196 }
   1197 
   1198 std::pair<int, int> ShelfView::GetDragRange(int index) {
   1199   int min_index = -1;
   1200   int max_index = -1;
   1201   ShelfItemType type = model_->items()[index].type;
   1202   for (int i = 0; i < model_->item_count(); ++i) {
   1203     if (SameDragType(model_->items()[i].type, type)) {
   1204       if (min_index == -1)
   1205         min_index = i;
   1206       max_index = i;
   1207     }
   1208   }
   1209   return std::pair<int, int>(min_index, max_index);
   1210 }
   1211 
   1212 void ShelfView::ConfigureChildView(views::View* view) {
   1213   view->SetPaintToLayer(true);
   1214   view->layer()->SetFillsBoundsOpaquely(false);
   1215 }
   1216 
   1217 void ShelfView::ToggleOverflowBubble() {
   1218   if (IsShowingOverflowBubble()) {
   1219     overflow_bubble_->Hide();
   1220     return;
   1221   }
   1222 
   1223   if (!overflow_bubble_)
   1224     overflow_bubble_.reset(new OverflowBubble());
   1225 
   1226   ShelfView* overflow_view =
   1227       new ShelfView(model_, delegate_, layout_manager_);
   1228   overflow_view->overflow_mode_ = true;
   1229   overflow_view->Init();
   1230   overflow_view->set_owner_overflow_bubble(overflow_bubble_.get());
   1231   overflow_view->OnShelfAlignmentChanged();
   1232   overflow_view->main_shelf_ = this;
   1233   UpdateOverflowRange(overflow_view);
   1234 
   1235   overflow_bubble_->Show(overflow_button_, overflow_view);
   1236 
   1237   Shell::GetInstance()->UpdateShelfVisibility();
   1238 }
   1239 
   1240 void ShelfView::OnFadeOutAnimationEnded() {
   1241   AnimateToIdealBounds();
   1242   StartFadeInLastVisibleItem();
   1243 }
   1244 
   1245 void ShelfView::StartFadeInLastVisibleItem() {
   1246   // If overflow button is visible and there is a valid new last item, fading
   1247   // the new last item in after sliding animation is finished.
   1248   if (overflow_button_->visible() && last_visible_index_ >= 0) {
   1249     views::View* last_visible_view = view_model_->view_at(last_visible_index_);
   1250     last_visible_view->layer()->SetOpacity(0);
   1251     bounds_animator_->SetAnimationDelegate(
   1252         last_visible_view,
   1253         scoped_ptr<gfx::AnimationDelegate>(
   1254             new StartFadeAnimationDelegate(this, last_visible_view)));
   1255   }
   1256 }
   1257 
   1258 void ShelfView::UpdateOverflowRange(ShelfView* overflow_view) const {
   1259   const int first_overflow_index = last_visible_index_ + 1;
   1260   const int last_overflow_index = last_hidden_index_;
   1261   DCHECK_LE(first_overflow_index, last_overflow_index);
   1262   DCHECK_LT(last_overflow_index, view_model_->view_size());
   1263 
   1264   overflow_view->first_visible_index_ = first_overflow_index;
   1265   overflow_view->last_visible_index_ = last_overflow_index;
   1266 }
   1267 
   1268 bool ShelfView::ShouldHideTooltip(const gfx::Point& cursor_location) {
   1269   gfx::Rect active_bounds;
   1270 
   1271   for (int i = 0; i < child_count(); ++i) {
   1272     views::View* child = child_at(i);
   1273     if (child == overflow_button_)
   1274       continue;
   1275     if (!ShouldShowTooltipForView(child))
   1276       continue;
   1277 
   1278     gfx::Rect child_bounds = child->GetMirroredBounds();
   1279     active_bounds.Union(child_bounds);
   1280   }
   1281 
   1282   return !active_bounds.Contains(cursor_location);
   1283 }
   1284 
   1285 gfx::Rect ShelfView::GetVisibleItemsBoundsInScreen() {
   1286   gfx::Size preferred_size = GetPreferredSize();
   1287   gfx::Point origin(GetMirroredXWithWidthInView(0, preferred_size.width()), 0);
   1288   ConvertPointToScreen(this, &origin);
   1289   return gfx::Rect(origin, preferred_size);
   1290 }
   1291 
   1292 gfx::Rect ShelfView::GetBoundsForDragInsertInScreen() {
   1293   gfx::Size preferred_size;
   1294   if (is_overflow_mode()) {
   1295     DCHECK(owner_overflow_bubble_);
   1296     gfx::Rect bubble_bounds =
   1297         owner_overflow_bubble_->bubble_view()->GetBubbleBounds();
   1298     preferred_size = bubble_bounds.size();
   1299   } else {
   1300     const int last_button_index = view_model_->view_size() - 1;
   1301     gfx::Rect last_button_bounds =
   1302         view_model_->view_at(last_button_index)->bounds();
   1303     if (overflow_button_->visible() &&
   1304         model_->GetItemIndexForType(TYPE_APP_PANEL) == -1) {
   1305       // When overflow button is visible and shelf has no panel items,
   1306       // last_button_bounds should be overflow button's bounds.
   1307       last_button_bounds = overflow_button_->bounds();
   1308     }
   1309 
   1310     if (layout_manager_->IsHorizontalAlignment()) {
   1311       preferred_size = gfx::Size(last_button_bounds.right() + leading_inset_,
   1312                                  kShelfSize);
   1313     } else {
   1314       preferred_size = gfx::Size(kShelfSize,
   1315                                  last_button_bounds.bottom() + leading_inset_);
   1316     }
   1317   }
   1318   gfx::Point origin(GetMirroredXWithWidthInView(0, preferred_size.width()), 0);
   1319 
   1320   // In overflow mode, we should use OverflowBubbleView as a source for
   1321   // converting |origin| to screen coordinates. When a scroll operation is
   1322   // occurred in OverflowBubble, the bounds of ShelfView in OverflowBubble can
   1323   // be changed.
   1324   if (is_overflow_mode())
   1325     ConvertPointToScreen(owner_overflow_bubble_->bubble_view(), &origin);
   1326   else
   1327     ConvertPointToScreen(this, &origin);
   1328 
   1329   return gfx::Rect(origin, preferred_size);
   1330 }
   1331 
   1332 int ShelfView::CancelDrag(int modified_index) {
   1333   FinalizeRipOffDrag(true);
   1334   if (!drag_view_)
   1335     return modified_index;
   1336   bool was_dragging = dragging();
   1337   int drag_view_index = view_model_->GetIndexOfView(drag_view_);
   1338   drag_pointer_ = NONE;
   1339   drag_view_ = NULL;
   1340   if (drag_view_index == modified_index) {
   1341     // The view that was being dragged is being modified. Don't do anything.
   1342     return modified_index;
   1343   }
   1344   if (!was_dragging)
   1345     return modified_index;
   1346 
   1347   // Restore previous position, tracking the position of the modified view.
   1348   bool at_end = modified_index == view_model_->view_size();
   1349   views::View* modified_view =
   1350       (modified_index >= 0 && !at_end) ?
   1351       view_model_->view_at(modified_index) : NULL;
   1352   model_->Move(drag_view_index, start_drag_index_);
   1353 
   1354   // If the modified view will be at the end of the list, return the new end of
   1355   // the list.
   1356   if (at_end)
   1357     return view_model_->view_size();
   1358   return modified_view ? view_model_->GetIndexOfView(modified_view) : -1;
   1359 }
   1360 
   1361 gfx::Size ShelfView::GetPreferredSize() const {
   1362   IdealBounds ideal_bounds;
   1363   CalculateIdealBounds(&ideal_bounds);
   1364 
   1365   int last_button_index = is_overflow_mode() ?
   1366       last_visible_index_ : view_model_->view_size() - 1;
   1367 
   1368   // When an item is dragged off from the overflow bubble, it is moved to last
   1369   // position and and changed to invisible. Overflow bubble size should be
   1370   // shrunk to fit only for visible items.
   1371   // If |dragged_off_from_overflow_to_shelf_| is set, there will be no invisible
   1372   // items in the shelf.
   1373   if (is_overflow_mode() &&
   1374       dragged_off_shelf_ &&
   1375       !dragged_off_from_overflow_to_shelf_ &&
   1376       RemovableByRipOff(view_model_->GetIndexOfView(drag_view_)) == REMOVABLE)
   1377     last_button_index--;
   1378 
   1379   const gfx::Rect last_button_bounds =
   1380       last_button_index  >= first_visible_index_ ?
   1381           view_model_->ideal_bounds(last_button_index) :
   1382           gfx::Rect(gfx::Size(kShelfSize, kShelfSize));
   1383 
   1384   if (layout_manager_->IsHorizontalAlignment()) {
   1385     return gfx::Size(last_button_bounds.right() + leading_inset_, kShelfSize);
   1386   }
   1387 
   1388   return gfx::Size(kShelfSize,
   1389                    last_button_bounds.bottom() + leading_inset_);
   1390 }
   1391 
   1392 void ShelfView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
   1393   // This bounds change is produced by the shelf movement and all content has
   1394   // to follow. Using an animation at that time would produce a time lag since
   1395   // the animation of the BoundsAnimator has itself a delay before it arrives
   1396   // at the required location. As such we tell the animator to go there
   1397   // immediately.
   1398   BoundsAnimatorDisabler disabler(bounds_animator_.get());
   1399   LayoutToIdealBounds();
   1400   FOR_EACH_OBSERVER(ShelfIconObserver, observers_,
   1401                     OnShelfIconPositionsChanged());
   1402 
   1403   if (IsShowingOverflowBubble())
   1404     overflow_bubble_->Hide();
   1405 }
   1406 
   1407 views::FocusTraversable* ShelfView::GetPaneFocusTraversable() {
   1408   return this;
   1409 }
   1410 
   1411 void ShelfView::GetAccessibleState(ui::AXViewState* state) {
   1412   state->role = ui::AX_ROLE_TOOLBAR;
   1413   state->name = l10n_util::GetStringUTF16(IDS_ASH_SHELF_ACCESSIBLE_NAME);
   1414 }
   1415 
   1416 void ShelfView::OnGestureEvent(ui::GestureEvent* event) {
   1417   if (gesture_handler_.ProcessGestureEvent(*event))
   1418     event->StopPropagation();
   1419 }
   1420 
   1421 void ShelfView::ShelfItemAdded(int model_index) {
   1422   {
   1423     base::AutoReset<bool> cancelling_drag(
   1424         &cancelling_drag_model_changed_, true);
   1425     model_index = CancelDrag(model_index);
   1426   }
   1427   views::View* view = CreateViewForItem(model_->items()[model_index]);
   1428   AddChildView(view);
   1429   // Hide the view, it'll be made visible when the animation is done. Using
   1430   // opacity 0 here to avoid messing with CalculateIdealBounds which touches
   1431   // the view's visibility.
   1432   view->layer()->SetOpacity(0);
   1433   view_model_->Add(view, model_index);
   1434 
   1435   // Give the button its ideal bounds. That way if we end up animating the
   1436   // button before this animation completes it doesn't appear at some random
   1437   // spot (because it was in the middle of animating from 0,0 0x0 to its
   1438   // target).
   1439   IdealBounds ideal_bounds;
   1440   CalculateIdealBounds(&ideal_bounds);
   1441   view->SetBoundsRect(view_model_->ideal_bounds(model_index));
   1442 
   1443   // The first animation moves all the views to their target position. |view|
   1444   // is hidden, so it visually appears as though we are providing space for
   1445   // it. When done we'll fade the view in.
   1446   AnimateToIdealBounds();
   1447   if (model_index <= last_visible_index_ ||
   1448       model_index >= model_->FirstPanelIndex()) {
   1449     bounds_animator_->SetAnimationDelegate(
   1450         view,
   1451         scoped_ptr<gfx::AnimationDelegate>(
   1452             new StartFadeAnimationDelegate(this, view)));
   1453   } else {
   1454     // Undo the hiding if animation does not run.
   1455     view->layer()->SetOpacity(1.0f);
   1456   }
   1457 }
   1458 
   1459 void ShelfView::ShelfItemRemoved(int model_index, ShelfID id) {
   1460   if (id == context_menu_id_)
   1461     launcher_menu_runner_.reset();
   1462   {
   1463     base::AutoReset<bool> cancelling_drag(
   1464         &cancelling_drag_model_changed_, true);
   1465     model_index = CancelDrag(model_index);
   1466   }
   1467   views::View* view = view_model_->view_at(model_index);
   1468   view_model_->Remove(model_index);
   1469 
   1470   // When the overflow bubble is visible, the overflow range needs to be set
   1471   // before CalculateIdealBounds() gets called. Otherwise CalculateIdealBounds()
   1472   // could trigger a ShelfItemChanged() by hiding the overflow bubble and
   1473   // since the overflow bubble is not yet synced with the ShelfModel this
   1474   // could cause a crash.
   1475   if (overflow_bubble_ && overflow_bubble_->IsShowing()) {
   1476     last_hidden_index_ = std::min(last_hidden_index_,
   1477                                   view_model_->view_size() - 1);
   1478     UpdateOverflowRange(overflow_bubble_->shelf_view());
   1479   }
   1480 
   1481   if (view->visible()) {
   1482     // The first animation fades out the view. When done we'll animate the rest
   1483     // of the views to their target location.
   1484     bounds_animator_->AnimateViewTo(view, view->bounds());
   1485     bounds_animator_->SetAnimationDelegate(
   1486         view,
   1487         scoped_ptr<gfx::AnimationDelegate>(
   1488             new FadeOutAnimationDelegate(this, view)));
   1489   } else {
   1490     // We don't need to show a fade out animation for invisible |view|. When an
   1491     // item is ripped out from the shelf, its |view| is already invisible.
   1492     AnimateToIdealBounds();
   1493   }
   1494 
   1495   // Close the tooltip because it isn't needed any longer and its anchor view
   1496   // will be deleted soon.
   1497   if (tooltip_->GetCurrentAnchorView() == view)
   1498     tooltip_->Close();
   1499 }
   1500 
   1501 void ShelfView::ShelfItemChanged(int model_index, const ShelfItem& old_item) {
   1502   const ShelfItem& item(model_->items()[model_index]);
   1503   if (old_item.type != item.type) {
   1504     // Type changed, swap the views.
   1505     model_index = CancelDrag(model_index);
   1506     scoped_ptr<views::View> old_view(view_model_->view_at(model_index));
   1507     bounds_animator_->StopAnimatingView(old_view.get());
   1508     // Removing and re-inserting a view in our view model will strip the ideal
   1509     // bounds from the item. To avoid recalculation of everything the bounds
   1510     // get remembered and restored after the insertion to the previous value.
   1511     gfx::Rect old_ideal_bounds = view_model_->ideal_bounds(model_index);
   1512     view_model_->Remove(model_index);
   1513     views::View* new_view = CreateViewForItem(item);
   1514     AddChildView(new_view);
   1515     view_model_->Add(new_view, model_index);
   1516     view_model_->set_ideal_bounds(model_index, old_ideal_bounds);
   1517     new_view->SetBoundsRect(old_view->bounds());
   1518     return;
   1519   }
   1520 
   1521   views::View* view = view_model_->view_at(model_index);
   1522   switch (item.type) {
   1523     case TYPE_BROWSER_SHORTCUT:
   1524       // Fallthrough for the new Shelf since it needs to show the activation
   1525       // change as well.
   1526     case TYPE_APP_SHORTCUT:
   1527     case TYPE_WINDOWED_APP:
   1528     case TYPE_PLATFORM_APP:
   1529     case TYPE_DIALOG:
   1530     case TYPE_APP_PANEL: {
   1531       ShelfButton* button = static_cast<ShelfButton*>(view);
   1532       ReflectItemStatus(item, button);
   1533       // The browser shortcut is currently not a "real" item and as such the
   1534       // the image is bogous as well. We therefore keep the image as is for it.
   1535       if (item.type != TYPE_BROWSER_SHORTCUT)
   1536         button->SetImage(item.image);
   1537       button->SchedulePaint();
   1538       break;
   1539     }
   1540 
   1541     default:
   1542       break;
   1543   }
   1544 }
   1545 
   1546 void ShelfView::ShelfItemMoved(int start_index, int target_index) {
   1547   view_model_->Move(start_index, target_index);
   1548   // When cancelling a drag due to a shelf item being added, the currently
   1549   // dragged item is moved back to its initial position. AnimateToIdealBounds
   1550   // will be called again when the new item is added to the |view_model_| but
   1551   // at this time the |view_model_| is inconsistent with the |model_|.
   1552   if (!cancelling_drag_model_changed_)
   1553     AnimateToIdealBounds();
   1554 }
   1555 
   1556 void ShelfView::ShelfStatusChanged() {
   1557   // Nothing to do here.
   1558 }
   1559 
   1560 void ShelfView::PointerPressedOnButton(views::View* view,
   1561                                        Pointer pointer,
   1562                                        const ui::LocatedEvent& event) {
   1563   if (drag_view_)
   1564     return;
   1565 
   1566   int index = view_model_->GetIndexOfView(view);
   1567   if (index == -1)
   1568     return;
   1569 
   1570   ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
   1571       model_->items()[index].id);
   1572   if (view_model_->view_size() <= 1 || !item_delegate->IsDraggable())
   1573     return;  // View is being deleted or not draggable, ignore request.
   1574 
   1575   drag_view_ = view;
   1576   drag_origin_ = gfx::Point(event.x(), event.y());
   1577   UMA_HISTOGRAM_ENUMERATION("Ash.ShelfAlignmentUsage",
   1578       layout_manager_->SelectValueForShelfAlignment(
   1579           SHELF_ALIGNMENT_UMA_ENUM_VALUE_BOTTOM,
   1580           SHELF_ALIGNMENT_UMA_ENUM_VALUE_LEFT,
   1581           SHELF_ALIGNMENT_UMA_ENUM_VALUE_RIGHT,
   1582           -1),
   1583       SHELF_ALIGNMENT_UMA_ENUM_VALUE_COUNT);
   1584 }
   1585 
   1586 void ShelfView::PointerDraggedOnButton(views::View* view,
   1587                                        Pointer pointer,
   1588                                        const ui::LocatedEvent& event) {
   1589   // To prepare all drag types (moving an item in the shelf and dragging off),
   1590   // we should check the x-axis and y-axis offset.
   1591   if (!dragging() && drag_view_ &&
   1592       ((std::abs(event.x() - drag_origin_.x()) >= kMinimumDragDistance) ||
   1593        (std::abs(event.y() - drag_origin_.y()) >= kMinimumDragDistance))) {
   1594     PrepareForDrag(pointer, event);
   1595   }
   1596   if (drag_pointer_ == pointer)
   1597     ContinueDrag(event);
   1598 }
   1599 
   1600 void ShelfView::PointerReleasedOnButton(views::View* view,
   1601                                         Pointer pointer,
   1602                                         bool canceled) {
   1603   if (canceled) {
   1604     CancelDrag(-1);
   1605   } else if (drag_pointer_ == pointer) {
   1606     FinalizeRipOffDrag(false);
   1607     drag_pointer_ = NONE;
   1608     AnimateToIdealBounds();
   1609   }
   1610   // If the drag pointer is NONE, no drag operation is going on and the
   1611   // drag_view can be released.
   1612   if (drag_pointer_ == NONE)
   1613     drag_view_ = NULL;
   1614 }
   1615 
   1616 void ShelfView::MouseMovedOverButton(views::View* view) {
   1617   if (!ShouldShowTooltipForView(view))
   1618     return;
   1619 
   1620   if (!tooltip_->IsVisible())
   1621     tooltip_->ResetTimer();
   1622 }
   1623 
   1624 void ShelfView::MouseEnteredButton(views::View* view) {
   1625   if (!ShouldShowTooltipForView(view))
   1626     return;
   1627 
   1628   if (tooltip_->IsVisible()) {
   1629     tooltip_->ShowImmediately(view, GetAccessibleName(view));
   1630   } else {
   1631     tooltip_->ShowDelayed(view, GetAccessibleName(view));
   1632   }
   1633 }
   1634 
   1635 void ShelfView::MouseExitedButton(views::View* view) {
   1636   if (!tooltip_->IsVisible())
   1637     tooltip_->StopTimer();
   1638 }
   1639 
   1640 base::string16 ShelfView::GetAccessibleName(const views::View* view) {
   1641   int view_index = view_model_->GetIndexOfView(view);
   1642   // May be -1 while in the process of animating closed.
   1643   if (view_index == -1)
   1644     return base::string16();
   1645 
   1646   ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
   1647       model_->items()[view_index].id);
   1648   return item_delegate->GetTitle();
   1649 }
   1650 
   1651 void ShelfView::ButtonPressed(views::Button* sender, const ui::Event& event) {
   1652   // Do not handle mouse release during drag.
   1653   if (dragging())
   1654     return;
   1655 
   1656   if (sender == overflow_button_) {
   1657     ToggleOverflowBubble();
   1658     return;
   1659   }
   1660 
   1661   int view_index = view_model_->GetIndexOfView(sender);
   1662   // May be -1 while in the process of animating closed.
   1663   if (view_index == -1)
   1664     return;
   1665 
   1666   // If the previous menu was closed by the same event as this one, we ignore
   1667   // the call.
   1668   if (!IsUsableEvent(event))
   1669     return;
   1670 
   1671   // Don't activate the item twice on double-click. Otherwise the window starts
   1672   // animating open due to the first click, then immediately minimizes due to
   1673   // the second click. The user most likely intended to open or minimize the
   1674   // item once, not do both.
   1675   if (event.flags() & ui::EF_IS_DOUBLE_CLICK)
   1676     return;
   1677 
   1678   {
   1679     ScopedTargetRootWindow scoped_target(
   1680         sender->GetWidget()->GetNativeView()->GetRootWindow());
   1681     // Slow down activation animations if shift key is pressed.
   1682     scoped_ptr<ui::ScopedAnimationDurationScaleMode> slowing_animations;
   1683     if (event.IsShiftDown()) {
   1684       slowing_animations.reset(new ui::ScopedAnimationDurationScaleMode(
   1685             ui::ScopedAnimationDurationScaleMode::SLOW_DURATION));
   1686     }
   1687 
   1688     // Collect usage statistics before we decide what to do with the click.
   1689     switch (model_->items()[view_index].type) {
   1690       case TYPE_APP_SHORTCUT:
   1691       case TYPE_WINDOWED_APP:
   1692       case TYPE_PLATFORM_APP:
   1693       case TYPE_BROWSER_SHORTCUT:
   1694         Shell::GetInstance()->metrics()->RecordUserMetricsAction(
   1695             UMA_LAUNCHER_CLICK_ON_APP);
   1696         break;
   1697 
   1698       case TYPE_APP_LIST:
   1699         Shell::GetInstance()->metrics()->RecordUserMetricsAction(
   1700             UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON);
   1701         break;
   1702 
   1703       case TYPE_APP_PANEL:
   1704       case TYPE_DIALOG:
   1705         break;
   1706 
   1707       case TYPE_UNDEFINED:
   1708         NOTREACHED() << "ShelfItemType must be set.";
   1709         break;
   1710     }
   1711 
   1712     ShelfItemDelegate* item_delegate =
   1713         item_manager_->GetShelfItemDelegate(model_->items()[view_index].id);
   1714     if (!item_delegate->ItemSelected(event))
   1715       ShowListMenuForView(model_->items()[view_index], sender, event);
   1716   }
   1717 }
   1718 
   1719 bool ShelfView::ShowListMenuForView(const ShelfItem& item,
   1720                                     views::View* source,
   1721                                     const ui::Event& event) {
   1722   ShelfItemDelegate* item_delegate =
   1723       item_manager_->GetShelfItemDelegate(item.id);
   1724   scoped_ptr<ui::MenuModel> list_menu_model(
   1725       item_delegate->CreateApplicationMenu(event.flags()));
   1726 
   1727   // Make sure we have a menu and it has at least two items in addition to the
   1728   // application title and the 3 spacing separators.
   1729   if (!list_menu_model.get() || list_menu_model->GetItemCount() <= 5)
   1730     return false;
   1731 
   1732   ShowMenu(list_menu_model.get(),
   1733            source,
   1734            gfx::Point(),
   1735            false,
   1736            ui::GetMenuSourceTypeForEvent(event));
   1737   return true;
   1738 }
   1739 
   1740 void ShelfView::ShowContextMenuForView(views::View* source,
   1741                                        const gfx::Point& point,
   1742                                        ui::MenuSourceType source_type) {
   1743   int view_index = view_model_->GetIndexOfView(source);
   1744   if (view_index == -1) {
   1745     Shell::GetInstance()->ShowContextMenu(point, source_type);
   1746     return;
   1747   }
   1748 
   1749   ShelfItemDelegate* item_delegate = item_manager_->GetShelfItemDelegate(
   1750       model_->items()[view_index].id);
   1751   context_menu_model_.reset(item_delegate->CreateContextMenu(
   1752       source->GetWidget()->GetNativeView()->GetRootWindow()));
   1753   if (!context_menu_model_)
   1754     return;
   1755 
   1756   base::AutoReset<ShelfID> reseter(
   1757       &context_menu_id_,
   1758       view_index == -1 ? 0 : model_->items()[view_index].id);
   1759 
   1760   ShowMenu(context_menu_model_.get(),
   1761            source,
   1762            point,
   1763            true,
   1764            source_type);
   1765 }
   1766 
   1767 void ShelfView::ShowMenu(ui::MenuModel* menu_model,
   1768                          views::View* source,
   1769                          const gfx::Point& click_point,
   1770                          bool context_menu,
   1771                          ui::MenuSourceType source_type) {
   1772   closing_event_time_ = base::TimeDelta();
   1773   launcher_menu_runner_.reset(new views::MenuRunner(
   1774       menu_model, context_menu ? views::MenuRunner::CONTEXT_MENU : 0));
   1775 
   1776   ScopedTargetRootWindow scoped_target(
   1777       source->GetWidget()->GetNativeView()->GetRootWindow());
   1778 
   1779   // Determine the menu alignment dependent on the shelf.
   1780   views::MenuAnchorPosition menu_alignment = views::MENU_ANCHOR_TOPLEFT;
   1781   gfx::Rect anchor_point = gfx::Rect(click_point, gfx::Size());
   1782 
   1783   ShelfWidget* shelf = RootWindowController::ForShelf(
   1784       GetWidget()->GetNativeView())->shelf();
   1785   if (!context_menu) {
   1786     // Application lists use a bubble.
   1787     ShelfAlignment align = shelf->GetAlignment();
   1788     anchor_point = source->GetBoundsInScreen();
   1789 
   1790     // It is possible to invoke the menu while it is sliding into view. To cover
   1791     // that case, the screen coordinates are offsetted by the animation delta.
   1792     gfx::Vector2d offset =
   1793         source->GetWidget()->GetNativeWindow()->bounds().origin() -
   1794         source->GetWidget()->GetNativeWindow()->GetTargetBounds().origin();
   1795     anchor_point.set_x(anchor_point.x() - offset.x());
   1796     anchor_point.set_y(anchor_point.y() - offset.y());
   1797 
   1798     // Shelf items can have an asymmetrical border for spacing reasons.
   1799     // Adjust anchor location for this.
   1800     if (source->border())
   1801       anchor_point.Inset(source->border()->GetInsets());
   1802 
   1803     switch (align) {
   1804       case SHELF_ALIGNMENT_BOTTOM:
   1805         menu_alignment = views::MENU_ANCHOR_BUBBLE_ABOVE;
   1806         break;
   1807       case SHELF_ALIGNMENT_LEFT:
   1808         menu_alignment = views::MENU_ANCHOR_BUBBLE_RIGHT;
   1809         break;
   1810       case SHELF_ALIGNMENT_RIGHT:
   1811         menu_alignment = views::MENU_ANCHOR_BUBBLE_LEFT;
   1812         break;
   1813       case SHELF_ALIGNMENT_TOP:
   1814         menu_alignment = views::MENU_ANCHOR_BUBBLE_BELOW;
   1815         break;
   1816     }
   1817   }
   1818   // If this gets deleted while we are in the menu, the shelf will be gone
   1819   // as well.
   1820   bool got_deleted = false;
   1821   got_deleted_ = &got_deleted;
   1822 
   1823   shelf->ForceUndimming(true);
   1824   // NOTE: if you convert to HAS_MNEMONICS be sure and update menu building
   1825   // code.
   1826   if (launcher_menu_runner_->RunMenuAt(source->GetWidget(),
   1827                                        NULL,
   1828                                        anchor_point,
   1829                                        menu_alignment,
   1830                                        source_type) ==
   1831       views::MenuRunner::MENU_DELETED) {
   1832     if (!got_deleted) {
   1833       got_deleted_ = NULL;
   1834       shelf->ForceUndimming(false);
   1835     }
   1836     return;
   1837   }
   1838   got_deleted_ = NULL;
   1839   shelf->ForceUndimming(false);
   1840 
   1841   // If it is a context menu and we are showing overflow bubble
   1842   // we want to hide overflow bubble.
   1843   if (owner_overflow_bubble_)
   1844     owner_overflow_bubble_->HideBubbleAndRefreshButton();
   1845 
   1846   // Unpinning an item will reset the |launcher_menu_runner_| before coming
   1847   // here.
   1848   if (launcher_menu_runner_)
   1849     closing_event_time_ = launcher_menu_runner_->closing_event_time();
   1850   Shell::GetInstance()->UpdateShelfVisibility();
   1851 }
   1852 
   1853 void ShelfView::OnBoundsAnimatorProgressed(views::BoundsAnimator* animator) {
   1854   FOR_EACH_OBSERVER(ShelfIconObserver, observers_,
   1855                     OnShelfIconPositionsChanged());
   1856   PreferredSizeChanged();
   1857 }
   1858 
   1859 void ShelfView::OnBoundsAnimatorDone(views::BoundsAnimator* animator) {
   1860   if (snap_back_from_rip_off_view_ && animator == bounds_animator_) {
   1861     if (!animator->IsAnimating(snap_back_from_rip_off_view_)) {
   1862       // Coming here the animation of the ShelfButton is finished and the
   1863       // previously hidden status can be shown again. Since the button itself
   1864       // might have gone away or changed locations we check that the button
   1865       // is still in the shelf and show its status again.
   1866       for (int index = 0; index < view_model_->view_size(); index++) {
   1867         views::View* view = view_model_->view_at(index);
   1868         if (view == snap_back_from_rip_off_view_) {
   1869           ShelfButton* button = static_cast<ShelfButton*>(view);
   1870           button->ClearState(ShelfButton::STATE_HIDDEN);
   1871           break;
   1872         }
   1873       }
   1874       snap_back_from_rip_off_view_ = NULL;
   1875     }
   1876   }
   1877 }
   1878 
   1879 bool ShelfView::IsUsableEvent(const ui::Event& event) {
   1880   if (closing_event_time_ == base::TimeDelta())
   1881     return true;
   1882 
   1883   base::TimeDelta delta =
   1884       base::TimeDelta(event.time_stamp() - closing_event_time_);
   1885   closing_event_time_ = base::TimeDelta();
   1886   // TODO(skuhne): This time seems excessive, but it appears that the reposting
   1887   // takes that long.  Need to come up with a better way of doing this.
   1888   return (delta.InMilliseconds() < 0 || delta.InMilliseconds() > 130);
   1889 }
   1890 
   1891 const ShelfItem* ShelfView::ShelfItemForView(const views::View* view) const {
   1892   int view_index = view_model_->GetIndexOfView(view);
   1893   if (view_index == -1)
   1894     return NULL;
   1895   return &(model_->items()[view_index]);
   1896 }
   1897 
   1898 bool ShelfView::ShouldShowTooltipForView(const views::View* view) const {
   1899   if (view == GetAppListButtonView() &&
   1900       Shell::GetInstance()->GetAppListWindow())
   1901     return false;
   1902   const ShelfItem* item = ShelfItemForView(view);
   1903   if (!item)
   1904     return true;
   1905   ShelfItemDelegate* item_delegate =
   1906       item_manager_->GetShelfItemDelegate(item->id);
   1907   return item_delegate->ShouldShowTooltip();
   1908 }
   1909 
   1910 int ShelfView::CalculateShelfDistance(const gfx::Point& coordinate) const {
   1911   ShelfWidget* shelf = RootWindowController::ForShelf(
   1912       GetWidget()->GetNativeView())->shelf();
   1913   ShelfAlignment align = shelf->GetAlignment();
   1914   const gfx::Rect bounds = GetBoundsInScreen();
   1915   int distance = 0;
   1916   switch (align) {
   1917     case SHELF_ALIGNMENT_BOTTOM:
   1918       distance = bounds.y() - coordinate.y();
   1919       break;
   1920     case SHELF_ALIGNMENT_LEFT:
   1921       distance = coordinate.x() - bounds.right();
   1922       break;
   1923     case SHELF_ALIGNMENT_RIGHT:
   1924       distance = bounds.x() - coordinate.x();
   1925       break;
   1926     case SHELF_ALIGNMENT_TOP:
   1927       distance = coordinate.y() - bounds.bottom();
   1928       break;
   1929   }
   1930   return distance > 0 ? distance : 0;
   1931 }
   1932 
   1933 }  // namespace ash
   1934