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