Home | History | Annotate | Download | only in launcher
      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/launcher/launcher_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/launcher/alternate_app_list_button.h"
     13 #include "ash/launcher/app_list_button.h"
     14 #include "ash/launcher/launcher_button.h"
     15 #include "ash/launcher/launcher_delegate.h"
     16 #include "ash/launcher/launcher_icon_observer.h"
     17 #include "ash/launcher/launcher_model.h"
     18 #include "ash/launcher/launcher_tooltip_manager.h"
     19 #include "ash/launcher/overflow_bubble.h"
     20 #include "ash/launcher/overflow_button.h"
     21 #include "ash/launcher/tabbed_launcher_button.h"
     22 #include "ash/root_window_controller.h"
     23 #include "ash/scoped_target_root_window.h"
     24 #include "ash/shelf/shelf_layout_manager.h"
     25 #include "ash/shelf/shelf_widget.h"
     26 #include "ash/shell_delegate.h"
     27 #include "base/auto_reset.h"
     28 #include "base/memory/scoped_ptr.h"
     29 #include "grit/ash_resources.h"
     30 #include "grit/ash_strings.h"
     31 #include "ui/aura/client/screen_position_client.h"
     32 #include "ui/aura/root_window.h"
     33 #include "ui/aura/window.h"
     34 #include "ui/base/accessibility/accessible_view_state.h"
     35 #include "ui/base/l10n/l10n_util.h"
     36 #include "ui/base/models/simple_menu_model.h"
     37 #include "ui/base/resource/resource_bundle.h"
     38 #include "ui/compositor/layer.h"
     39 #include "ui/compositor/layer_animator.h"
     40 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
     41 #include "ui/gfx/canvas.h"
     42 #include "ui/gfx/point.h"
     43 #include "ui/views/animation/bounds_animator.h"
     44 #include "ui/views/border.h"
     45 #include "ui/views/controls/button/image_button.h"
     46 #include "ui/views/controls/menu/menu_model_adapter.h"
     47 #include "ui/views/controls/menu/menu_runner.h"
     48 #include "ui/views/focus/focus_search.h"
     49 #include "ui/views/focus_border.h"
     50 #include "ui/views/view_model.h"
     51 #include "ui/views/view_model_utils.h"
     52 #include "ui/views/widget/widget.h"
     53 
     54 using ui::Animation;
     55 using views::View;
     56 
     57 namespace ash {
     58 namespace internal {
     59 
     60 // Default amount content is inset on the left edge.
     61 const int kDefaultLeadingInset = 8;
     62 
     63 // Minimum distance before drag starts.
     64 const int kMinimumDragDistance = 8;
     65 
     66 // Size between the buttons.
     67 const int kButtonSpacing = 4;
     68 const int kAlternateButtonSpacing = 10;
     69 
     70 // Size allocated to for each button.
     71 const int kButtonSize = 44;
     72 
     73 // Additional spacing for the left and right side of icons.
     74 const int kHorizontalIconSpacing = 2;
     75 
     76 // Inset for items which do not have an icon.
     77 const int kHorizontalNoIconInsetSpacing =
     78     kHorizontalIconSpacing + kDefaultLeadingInset;
     79 
     80 // The proportion of the launcher space reserved for non-panel icons. Panels
     81 // may flow into this space but will be put into the overflow bubble if there
     82 // is contention for the space.
     83 const float kReservedNonPanelIconProportion = 0.67f;
     84 
     85 // This is the command id of the menu item which contains the name of the menu.
     86 const int kCommandIdOfMenuName = 0;
     87 
     88 // The background color of the active item in the list.
     89 const SkColor kActiveListItemBackgroundColor = SkColorSetRGB(203 , 219, 241);
     90 
     91 // The background color of the active & hovered item in the list.
     92 const SkColor kFocusedActiveListItemBackgroundColor =
     93     SkColorSetRGB(193, 211, 236);
     94 
     95 // The text color of the caption item in a list.
     96 const SkColor kCaptionItemForegroundColor = SK_ColorBLACK;
     97 
     98 // The maximum allowable length of a menu line of an application menu in pixels.
     99 const int kMaximumAppMenuItemLength = 350;
    100 
    101 namespace {
    102 
    103 // The MenuModelAdapter gets slightly changed to adapt the menu appearance to
    104 // our requirements.
    105 class LauncherMenuModelAdapter
    106     : public views::MenuModelAdapter {
    107  public:
    108   explicit LauncherMenuModelAdapter(ash::LauncherMenuModel* menu_model);
    109 
    110   // Overriding MenuModelAdapter's MenuDelegate implementation.
    111   virtual const gfx::Font* GetLabelFont(int command_id) const OVERRIDE;
    112   virtual bool IsCommandEnabled(int id) const OVERRIDE;
    113   virtual void GetHorizontalIconMargins(int id,
    114                                         int icon_size,
    115                                         int* left_margin,
    116                                         int* right_margin) const OVERRIDE;
    117   virtual bool GetForegroundColor(int command_id,
    118                                   bool is_hovered,
    119                                   SkColor* override_color) const OVERRIDE;
    120   virtual bool GetBackgroundColor(int command_id,
    121                                   bool is_hovered,
    122                                   SkColor* override_color) const OVERRIDE;
    123   virtual int GetMaxWidthForMenu(views::MenuItemView* menu) OVERRIDE;
    124   virtual bool ShouldReserveSpaceForSubmenuIndicator() const OVERRIDE;
    125 
    126  private:
    127   ash::LauncherMenuModel* launcher_menu_model_;
    128 
    129   DISALLOW_COPY_AND_ASSIGN(LauncherMenuModelAdapter);
    130 };
    131 
    132 
    133 LauncherMenuModelAdapter::LauncherMenuModelAdapter(
    134     ash::LauncherMenuModel* menu_model)
    135     : MenuModelAdapter(menu_model),
    136       launcher_menu_model_(menu_model) {}
    137 
    138 const gfx::Font* LauncherMenuModelAdapter::GetLabelFont(
    139     int command_id) const {
    140   if (command_id != kCommandIdOfMenuName)
    141     return MenuModelAdapter::GetLabelFont(command_id);
    142 
    143   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    144   return &rb.GetFont(ui::ResourceBundle::BoldFont);
    145 }
    146 
    147 bool LauncherMenuModelAdapter::IsCommandEnabled(int id) const {
    148   return id != kCommandIdOfMenuName;
    149 }
    150 
    151 bool LauncherMenuModelAdapter::GetForegroundColor(
    152     int command_id,
    153     bool is_hovered,
    154     SkColor* override_color) const {
    155   if (command_id != kCommandIdOfMenuName)
    156     return false;
    157 
    158   *override_color = kCaptionItemForegroundColor;
    159   return true;
    160 }
    161 
    162 bool LauncherMenuModelAdapter::GetBackgroundColor(
    163     int command_id,
    164     bool is_hovered,
    165     SkColor* override_color) const {
    166   if (!launcher_menu_model_->IsCommandActive(command_id))
    167     return false;
    168 
    169   *override_color = is_hovered ? kFocusedActiveListItemBackgroundColor :
    170                                  kActiveListItemBackgroundColor;
    171   return true;
    172 }
    173 
    174 void LauncherMenuModelAdapter::GetHorizontalIconMargins(
    175     int command_id,
    176     int icon_size,
    177     int* left_margin,
    178     int* right_margin) const {
    179   *left_margin = kHorizontalIconSpacing;
    180   *right_margin = (command_id != kCommandIdOfMenuName) ?
    181       kHorizontalIconSpacing : -(icon_size + kHorizontalNoIconInsetSpacing);
    182 }
    183 
    184 int LauncherMenuModelAdapter::GetMaxWidthForMenu(views::MenuItemView* menu) {
    185   return kMaximumAppMenuItemLength;
    186 }
    187 
    188 bool LauncherMenuModelAdapter::ShouldReserveSpaceForSubmenuIndicator() const {
    189   return false;
    190 }
    191 
    192 // Custom FocusSearch used to navigate the launcher in the order items are in
    193 // the ViewModel.
    194 class LauncherFocusSearch : public views::FocusSearch {
    195  public:
    196   explicit LauncherFocusSearch(views::ViewModel* view_model)
    197       : FocusSearch(NULL, true, true),
    198         view_model_(view_model) {}
    199   virtual ~LauncherFocusSearch() {}
    200 
    201   // views::FocusSearch overrides:
    202   virtual View* FindNextFocusableView(
    203       View* starting_view,
    204       bool reverse,
    205       Direction direction,
    206       bool check_starting_view,
    207       views::FocusTraversable** focus_traversable,
    208       View** focus_traversable_view) OVERRIDE {
    209     int index = view_model_->GetIndexOfView(starting_view);
    210     if (index == -1)
    211       return view_model_->view_at(0);
    212 
    213     if (reverse) {
    214       --index;
    215       if (index < 0)
    216         index = view_model_->view_size() - 1;
    217     } else {
    218       ++index;
    219       if (index >= view_model_->view_size())
    220         index = 0;
    221     }
    222     return view_model_->view_at(index);
    223   }
    224 
    225  private:
    226   views::ViewModel* view_model_;
    227 
    228   DISALLOW_COPY_AND_ASSIGN(LauncherFocusSearch);
    229 };
    230 
    231 class LauncherButtonFocusBorder : public views::FocusBorder {
    232  public:
    233   LauncherButtonFocusBorder() {}
    234   virtual ~LauncherButtonFocusBorder() {}
    235 
    236  private:
    237   // views::FocusBorder overrides:
    238   virtual void Paint(const View& view, gfx::Canvas* canvas) const OVERRIDE {
    239     gfx::Rect rect(view.GetLocalBounds());
    240     rect.Inset(1, 1);
    241     canvas->DrawRect(rect, kFocusBorderColor);
    242   }
    243 
    244   DISALLOW_COPY_AND_ASSIGN(LauncherButtonFocusBorder);
    245 };
    246 
    247 // AnimationDelegate that deletes a view when done. This is used when a launcher
    248 // item is removed, which triggers a remove animation. When the animation is
    249 // done we delete the view.
    250 class DeleteViewAnimationDelegate
    251     : public views::BoundsAnimator::OwnedAnimationDelegate {
    252  public:
    253   explicit DeleteViewAnimationDelegate(views::View* view) : view_(view) {}
    254   virtual ~DeleteViewAnimationDelegate() {}
    255 
    256  private:
    257   scoped_ptr<views::View> view_;
    258 
    259   DISALLOW_COPY_AND_ASSIGN(DeleteViewAnimationDelegate);
    260 };
    261 
    262 // AnimationDelegate used when inserting a new item. This steadily increases the
    263 // opacity of the layer as the animation progress.
    264 class FadeInAnimationDelegate
    265     : public views::BoundsAnimator::OwnedAnimationDelegate {
    266  public:
    267   explicit FadeInAnimationDelegate(views::View* view) : view_(view) {}
    268   virtual ~FadeInAnimationDelegate() {}
    269 
    270   // AnimationDelegate overrides:
    271   virtual void AnimationProgressed(const Animation* animation) OVERRIDE {
    272     view_->layer()->SetOpacity(animation->GetCurrentValue());
    273     view_->layer()->ScheduleDraw();
    274   }
    275   virtual void AnimationEnded(const Animation* animation) OVERRIDE {
    276     view_->layer()->SetOpacity(1.0f);
    277     view_->layer()->ScheduleDraw();
    278   }
    279   virtual void AnimationCanceled(const Animation* animation) OVERRIDE {
    280     view_->layer()->SetOpacity(1.0f);
    281     view_->layer()->ScheduleDraw();
    282   }
    283 
    284  private:
    285   views::View* view_;
    286 
    287   DISALLOW_COPY_AND_ASSIGN(FadeInAnimationDelegate);
    288 };
    289 
    290 void ReflectItemStatus(const ash::LauncherItem& item,
    291                        LauncherButton* button) {
    292   switch (item.status) {
    293     case STATUS_CLOSED:
    294       button->ClearState(LauncherButton::STATE_ACTIVE);
    295       button->ClearState(LauncherButton::STATE_RUNNING);
    296       button->ClearState(LauncherButton::STATE_ATTENTION);
    297       break;
    298     case STATUS_RUNNING:
    299       button->ClearState(LauncherButton::STATE_ACTIVE);
    300       button->AddState(LauncherButton::STATE_RUNNING);
    301       button->ClearState(LauncherButton::STATE_ATTENTION);
    302       break;
    303     case STATUS_ACTIVE:
    304       button->AddState(LauncherButton::STATE_ACTIVE);
    305       button->ClearState(LauncherButton::STATE_RUNNING);
    306       button->ClearState(LauncherButton::STATE_ATTENTION);
    307       break;
    308     case STATUS_ATTENTION:
    309       button->ClearState(LauncherButton::STATE_ACTIVE);
    310       button->ClearState(LauncherButton::STATE_RUNNING);
    311       button->AddState(LauncherButton::STATE_ATTENTION);
    312       break;
    313   }
    314 }
    315 
    316 // Get the event location in screen coordinates.
    317 gfx::Point GetPositionInScreen(const gfx::Point& root_location,
    318                                views::View* view) {
    319   gfx::Point root_location_in_screen = root_location;
    320   aura::RootWindow* root_window =
    321       view->GetWidget()->GetNativeWindow()->GetRootWindow();
    322   aura::client::GetScreenPositionClient(root_window->GetRootWindow())->
    323         ConvertPointToScreen(root_window, &root_location_in_screen);
    324   return root_location_in_screen;
    325 }
    326 
    327 }  // namespace
    328 
    329 // AnimationDelegate used when deleting an item. This steadily decreased the
    330 // opacity of the layer as the animation progress.
    331 class LauncherView::FadeOutAnimationDelegate
    332     : public views::BoundsAnimator::OwnedAnimationDelegate {
    333  public:
    334   FadeOutAnimationDelegate(LauncherView* host, views::View* view)
    335       : launcher_view_(host),
    336         view_(view) {}
    337   virtual ~FadeOutAnimationDelegate() {}
    338 
    339   // AnimationDelegate overrides:
    340   virtual void AnimationProgressed(const Animation* animation) OVERRIDE {
    341     view_->layer()->SetOpacity(1 - animation->GetCurrentValue());
    342     view_->layer()->ScheduleDraw();
    343   }
    344   virtual void AnimationEnded(const Animation* animation) OVERRIDE {
    345     launcher_view_->OnFadeOutAnimationEnded();
    346   }
    347   virtual void AnimationCanceled(const Animation* animation) OVERRIDE {
    348   }
    349 
    350  private:
    351   LauncherView* launcher_view_;
    352   scoped_ptr<views::View> view_;
    353 
    354   DISALLOW_COPY_AND_ASSIGN(FadeOutAnimationDelegate);
    355 };
    356 
    357 // AnimationDelegate used to trigger fading an element in. When an item is
    358 // inserted this delegate is attached to the animation that expands the size of
    359 // the item.  When done it kicks off another animation to fade the item in.
    360 class LauncherView::StartFadeAnimationDelegate
    361     : public views::BoundsAnimator::OwnedAnimationDelegate {
    362  public:
    363   StartFadeAnimationDelegate(LauncherView* host,
    364                              views::View* view)
    365       : launcher_view_(host),
    366         view_(view) {}
    367   virtual ~StartFadeAnimationDelegate() {}
    368 
    369   // AnimationDelegate overrides:
    370   virtual void AnimationEnded(const Animation* animation) OVERRIDE {
    371     launcher_view_->FadeIn(view_);
    372   }
    373   virtual void AnimationCanceled(const Animation* animation) OVERRIDE {
    374     view_->layer()->SetOpacity(1.0f);
    375   }
    376 
    377  private:
    378   LauncherView* launcher_view_;
    379   views::View* view_;
    380 
    381   DISALLOW_COPY_AND_ASSIGN(StartFadeAnimationDelegate);
    382 };
    383 
    384 LauncherView::LauncherView(LauncherModel* model,
    385                            LauncherDelegate* delegate,
    386                            ShelfLayoutManager* shelf_layout_manager)
    387     : model_(model),
    388       delegate_(delegate),
    389       view_model_(new views::ViewModel),
    390       first_visible_index_(0),
    391       last_visible_index_(-1),
    392       overflow_button_(NULL),
    393       drag_pointer_(NONE),
    394       drag_view_(NULL),
    395       drag_offset_(0),
    396       start_drag_index_(-1),
    397       context_menu_id_(0),
    398       leading_inset_(kDefaultLeadingInset),
    399       cancelling_drag_model_changed_(false),
    400       last_hidden_index_(0),
    401       closing_event_time_(base::TimeDelta()),
    402       got_deleted_(NULL),
    403       drag_and_drop_item_pinned_(false),
    404       drag_and_drop_launcher_id_(0) {
    405   DCHECK(model_);
    406   bounds_animator_.reset(new views::BoundsAnimator(this));
    407   bounds_animator_->AddObserver(this);
    408   set_context_menu_controller(this);
    409   focus_search_.reset(new LauncherFocusSearch(view_model_.get()));
    410   tooltip_.reset(new LauncherTooltipManager(
    411       shelf_layout_manager, this));
    412 }
    413 
    414 LauncherView::~LauncherView() {
    415   bounds_animator_->RemoveObserver(this);
    416   model_->RemoveObserver(this);
    417   // If we are inside the MenuRunner, we need to know if we were getting
    418   // deleted while it was running.
    419   if (got_deleted_)
    420     *got_deleted_ = true;
    421 }
    422 
    423 void LauncherView::Init() {
    424   model_->AddObserver(this);
    425 
    426   const LauncherItems& items(model_->items());
    427   for (LauncherItems::const_iterator i = items.begin(); i != items.end(); ++i) {
    428     views::View* child = CreateViewForItem(*i);
    429     child->SetPaintToLayer(true);
    430     view_model_->Add(child, static_cast<int>(i - items.begin()));
    431     AddChildView(child);
    432   }
    433   LauncherStatusChanged();
    434   overflow_button_ = new OverflowButton(this);
    435   overflow_button_->set_context_menu_controller(this);
    436   ConfigureChildView(overflow_button_);
    437   AddChildView(overflow_button_);
    438   UpdateFirstButtonPadding();
    439 
    440   // We'll layout when our bounds change.
    441 }
    442 
    443 void LauncherView::OnShelfAlignmentChanged() {
    444   UpdateFirstButtonPadding();
    445   overflow_button_->OnShelfAlignmentChanged();
    446   LayoutToIdealBounds();
    447   for (int i=0; i < view_model_->view_size(); ++i) {
    448     // TODO: remove when AppIcon is a Launcher Button.
    449     if (TYPE_APP_LIST == model_->items()[i].type &&
    450         !ash::switches::UseAlternateShelfLayout()) {
    451       ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
    452       static_cast<AppListButton*>(view_model_->view_at(i))->SetImageAlignment(
    453           shelf->SelectValueForShelfAlignment(
    454               views::ImageButton::ALIGN_CENTER,
    455               views::ImageButton::ALIGN_LEFT,
    456               views::ImageButton::ALIGN_RIGHT,
    457               views::ImageButton::ALIGN_CENTER),
    458           shelf->SelectValueForShelfAlignment(
    459               views::ImageButton::ALIGN_TOP,
    460               views::ImageButton::ALIGN_MIDDLE,
    461               views::ImageButton::ALIGN_MIDDLE,
    462               views::ImageButton::ALIGN_BOTTOM));
    463     }
    464     if (i >= first_visible_index_ && i <= last_visible_index_)
    465       view_model_->view_at(i)->Layout();
    466   }
    467   tooltip_->UpdateArrow();
    468   if (overflow_bubble_)
    469     overflow_bubble_->Hide();
    470 }
    471 
    472 void LauncherView::SchedulePaintForAllButtons() {
    473   for (int i = 0; i < view_model_->view_size(); ++i) {
    474     if (i >= first_visible_index_ && i <= last_visible_index_)
    475       view_model_->view_at(i)->SchedulePaint();
    476   }
    477   if (overflow_button_ && overflow_button_->visible())
    478     overflow_button_->SchedulePaint();
    479 }
    480 
    481 gfx::Rect LauncherView::GetIdealBoundsOfItemIcon(LauncherID id) {
    482   int index = model_->ItemIndexByID(id);
    483   if (index == -1 || (index > last_visible_index_ &&
    484                       index < model_->FirstPanelIndex()))
    485     return gfx::Rect();
    486   const gfx::Rect& ideal_bounds(view_model_->ideal_bounds(index));
    487   DCHECK_NE(TYPE_APP_LIST, model_->items()[index].type);
    488   LauncherButton* button =
    489       static_cast<LauncherButton*>(view_model_->view_at(index));
    490   gfx::Rect icon_bounds = button->GetIconBounds();
    491   return gfx::Rect(GetMirroredXWithWidthInView(
    492                        ideal_bounds.x() + icon_bounds.x(), icon_bounds.width()),
    493                    ideal_bounds.y() + icon_bounds.y(),
    494                    icon_bounds.width(),
    495                    icon_bounds.height());
    496 }
    497 
    498 void LauncherView::UpdatePanelIconPosition(LauncherID id,
    499                                            const gfx::Point& midpoint) {
    500   int current_index = model_->ItemIndexByID(id);
    501   int first_panel_index = model_->FirstPanelIndex();
    502   if (current_index < first_panel_index)
    503     return;
    504 
    505   gfx::Point midpoint_in_view(GetMirroredXInView(midpoint.x()),
    506                               midpoint.y());
    507   ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
    508   int target_index = current_index;
    509   while (target_index > first_panel_index &&
    510          shelf->PrimaryAxisValue(view_model_->ideal_bounds(target_index).x(),
    511                                  view_model_->ideal_bounds(target_index).y()) >
    512          shelf->PrimaryAxisValue(midpoint_in_view.x(), midpoint_in_view.y())) {
    513     --target_index;
    514   }
    515   while (target_index < view_model_->view_size() - 1 &&
    516          shelf->PrimaryAxisValue(
    517              view_model_->ideal_bounds(target_index).right(),
    518              view_model_->ideal_bounds(target_index).bottom()) <
    519          shelf->PrimaryAxisValue(midpoint_in_view.x(), midpoint_in_view.y())) {
    520     ++target_index;
    521   }
    522   if (current_index != target_index)
    523     model_->Move(current_index, target_index);
    524 }
    525 
    526 bool LauncherView::IsShowingMenu() const {
    527 #if !defined(OS_MACOSX)
    528   return (launcher_menu_runner_.get() &&
    529        launcher_menu_runner_->IsRunning());
    530 #endif
    531   return false;
    532 }
    533 
    534 bool LauncherView::IsShowingOverflowBubble() const {
    535   return overflow_bubble_.get() && overflow_bubble_->IsShowing();
    536 }
    537 
    538 views::View* LauncherView::GetAppListButtonView() const {
    539   for (int i = 0; i < model_->item_count(); ++i) {
    540     if (model_->items()[i].type == TYPE_APP_LIST)
    541       return view_model_->view_at(i);
    542   }
    543 
    544   NOTREACHED() << "Applist button not found";
    545   return NULL;
    546 }
    547 
    548 ////////////////////////////////////////////////////////////////////////////////
    549 // LauncherView, FocusTraversable implementation:
    550 
    551 views::FocusSearch* LauncherView::GetFocusSearch() {
    552   return focus_search_.get();
    553 }
    554 
    555 views::FocusTraversable* LauncherView::GetFocusTraversableParent() {
    556   return parent()->GetFocusTraversable();
    557 }
    558 
    559 View* LauncherView::GetFocusTraversableParentView() {
    560   return this;
    561 }
    562 
    563 void LauncherView::CreateDragIconProxy(
    564     const gfx::Point& location_in_screen_coordinates,
    565     const gfx::ImageSkia& icon,
    566     views::View* replaced_view,
    567     const gfx::Vector2d& cursor_offset_from_center,
    568     float scale_factor) {
    569   drag_replaced_view_ = replaced_view;
    570   drag_image_.reset(new ash::internal::DragImageView(
    571       drag_replaced_view_->GetWidget()->GetNativeWindow()->GetRootWindow()));
    572   drag_image_->SetImage(icon);
    573   gfx::Size size = drag_image_->GetPreferredSize();
    574   size.set_width(size.width() * scale_factor);
    575   size.set_height(size.height() * scale_factor);
    576   drag_image_offset_ = gfx::Vector2d(size.width() / 2, size.height() / 2) +
    577                        cursor_offset_from_center;
    578   gfx::Rect drag_image_bounds(
    579       GetPositionInScreen(location_in_screen_coordinates,
    580                           drag_replaced_view_) - drag_image_offset_, size);
    581   drag_image_->SetBoundsInScreen(drag_image_bounds);
    582   drag_image_->SetWidgetVisible(true);
    583 }
    584 
    585 void LauncherView::UpdateDragIconProxy(
    586     const gfx::Point& location_in_screen_coordinates) {
    587   drag_image_->SetScreenPosition(
    588       GetPositionInScreen(location_in_screen_coordinates,
    589                           drag_replaced_view_) - drag_image_offset_);
    590 }
    591 
    592 void LauncherView::DestroyDragIconProxy() {
    593   drag_image_.reset();
    594   drag_image_offset_ = gfx::Vector2d(0, 0);
    595 }
    596 
    597 bool LauncherView::StartDrag(const std::string& app_id,
    598                              const gfx::Point& location_in_screen_coordinates) {
    599   // Bail if an operation is already going on - or the cursor is not inside.
    600   // This could happen if mouse / touch operations overlap.
    601   if (drag_and_drop_launcher_id_ ||
    602       !GetBoundsInScreen().Contains(location_in_screen_coordinates))
    603     return false;
    604 
    605   // If the AppsGridView (which was dispatching this event) was opened by our
    606   // button, LauncherView dragging operations are locked and we have to unlock.
    607   CancelDrag(-1);
    608   drag_and_drop_item_pinned_ = false;
    609   drag_and_drop_app_id_ = app_id;
    610   drag_and_drop_launcher_id_ =
    611       delegate_->GetLauncherIDForAppID(drag_and_drop_app_id_);
    612   // Check if the application is known and pinned - if not, we have to pin it so
    613   // that we can re-arrange the launcher order accordingly. Note that items have
    614   // to be pinned to give them the same (order) possibilities as a shortcut.
    615   if (!drag_and_drop_launcher_id_ || !delegate_->IsAppPinned(app_id)) {
    616     delegate_->PinAppWithID(app_id);
    617     drag_and_drop_launcher_id_ =
    618         delegate_->GetLauncherIDForAppID(drag_and_drop_app_id_);
    619     if (!drag_and_drop_launcher_id_)
    620       return false;
    621     drag_and_drop_item_pinned_ = true;
    622   }
    623   views::View* drag_and_drop_view = view_model_->view_at(
    624       model_->ItemIndexByID(drag_and_drop_launcher_id_));
    625   DCHECK(drag_and_drop_view);
    626 
    627   // Since there is already an icon presented by the caller, we hide this item
    628   // for now. That has to be done by reducing the size since the visibility will
    629   // change once a regrouping animation is performed.
    630   pre_drag_and_drop_size_ = drag_and_drop_view->size();
    631   drag_and_drop_view->SetSize(gfx::Size());
    632 
    633   // First we have to center the mouse cursor over the item.
    634   gfx::Point pt = drag_and_drop_view->GetBoundsInScreen().CenterPoint();
    635   views::View::ConvertPointFromScreen(drag_and_drop_view, &pt);
    636   ui::MouseEvent event(ui::ET_MOUSE_PRESSED,
    637                        pt, location_in_screen_coordinates, 0);
    638   PointerPressedOnButton(
    639       drag_and_drop_view, LauncherButtonHost::DRAG_AND_DROP, event);
    640 
    641   // Drag the item where it really belongs.
    642   Drag(location_in_screen_coordinates);
    643   return true;
    644 }
    645 
    646 bool LauncherView::Drag(const gfx::Point& location_in_screen_coordinates) {
    647   if (!drag_and_drop_launcher_id_ ||
    648       !GetBoundsInScreen().Contains(location_in_screen_coordinates))
    649     return false;
    650 
    651   gfx::Point pt = location_in_screen_coordinates;
    652   views::View* drag_and_drop_view = view_model_->view_at(
    653       model_->ItemIndexByID(drag_and_drop_launcher_id_));
    654   views::View::ConvertPointFromScreen(drag_and_drop_view, &pt);
    655 
    656   ui::MouseEvent event(ui::ET_MOUSE_DRAGGED, pt, gfx::Point(), 0);
    657   PointerDraggedOnButton(
    658       drag_and_drop_view, LauncherButtonHost::DRAG_AND_DROP, event);
    659   return true;
    660 }
    661 
    662 void LauncherView::EndDrag(bool cancel) {
    663   if (!drag_and_drop_launcher_id_)
    664     return;
    665 
    666   views::View* drag_and_drop_view = view_model_->view_at(
    667       model_->ItemIndexByID(drag_and_drop_launcher_id_));
    668   PointerReleasedOnButton(
    669       drag_and_drop_view, LauncherButtonHost::DRAG_AND_DROP, cancel);
    670 
    671   // Either destroy the temporarily created item - or - make the item visible.
    672   if (drag_and_drop_item_pinned_ && cancel)
    673     delegate_->UnpinAppsWithID(drag_and_drop_app_id_);
    674   else if (drag_and_drop_view)
    675     drag_and_drop_view->SetSize(pre_drag_and_drop_size_);
    676 
    677   drag_and_drop_launcher_id_ = 0;
    678 }
    679 
    680 void LauncherView::LayoutToIdealBounds() {
    681   IdealBounds ideal_bounds;
    682   CalculateIdealBounds(&ideal_bounds);
    683 
    684   if (bounds_animator_->IsAnimating())
    685     AnimateToIdealBounds();
    686   else
    687     views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_);
    688 
    689   overflow_button_->SetBoundsRect(ideal_bounds.overflow_bounds);
    690 }
    691 
    692 void LauncherView::CalculateIdealBounds(IdealBounds* bounds) {
    693   ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
    694 
    695   int available_size = shelf->PrimaryAxisValue(width(), height());
    696   DCHECK(model_->item_count() == view_model_->view_size());
    697   if (!available_size)
    698     return;
    699 
    700   int first_panel_index = model_->FirstPanelIndex();
    701   int last_button_index = first_panel_index - 1;
    702 
    703   // Initial x,y values account both leading_inset in primary
    704   // coordinate and secondary coordinate based on the dynamic edge of the
    705   // launcher (eg top edge on bottom-aligned launcher).
    706   int inset = ash::switches::UseAlternateShelfLayout() ? 0 : leading_inset();
    707   int x = shelf->SelectValueForShelfAlignment(inset, 0, 0, inset);
    708   int y = shelf->SelectValueForShelfAlignment(0, inset, inset, 0);
    709 
    710   int button_size = ash::switches::UseAlternateShelfLayout() ?
    711       kButtonSize : kLauncherPreferredSize;
    712   int button_spacing = ash::switches::UseAlternateShelfLayout() ?
    713       kAlternateButtonSpacing : kButtonSpacing;
    714 
    715   int w = shelf->PrimaryAxisValue(button_size, width());
    716   int h = shelf->PrimaryAxisValue(height(), button_size);
    717   for (int i = 0; i < view_model_->view_size(); ++i) {
    718     if (i < first_visible_index_) {
    719       view_model_->set_ideal_bounds(i, gfx::Rect(x, y, 0, 0));
    720       continue;
    721     }
    722 
    723     view_model_->set_ideal_bounds(i, gfx::Rect(x, y, w, h));
    724     if (i != last_button_index) {
    725       x = shelf->PrimaryAxisValue(x + w + button_spacing, x);
    726       y = shelf->PrimaryAxisValue(y, y + h + button_spacing);
    727     }
    728   }
    729 
    730   if (is_overflow_mode()) {
    731     DCHECK_LT(last_visible_index_, view_model_->view_size());
    732     for (int i = 0; i < view_model_->view_size(); ++i) {
    733       bool visible = i >= first_visible_index_ &&
    734           i <= last_visible_index_;
    735       if (!ash::switches::UseAlternateShelfLayout())
    736         visible &= i != last_button_index;
    737       view_model_->view_at(i)->SetVisible(visible);
    738     }
    739     return;
    740   }
    741 
    742   // To address Fitt's law, we make the first launcher button include the
    743   // leading inset (if there is one).
    744   if (!ash::switches::UseAlternateShelfLayout()) {
    745     if (view_model_->view_size() > 0) {
    746       view_model_->set_ideal_bounds(0, gfx::Rect(gfx::Size(
    747           shelf->PrimaryAxisValue(inset + w, w),
    748           shelf->PrimaryAxisValue(h, inset + h))));
    749     }
    750   }
    751 
    752   // Right aligned icons.
    753   int end_position = available_size - button_spacing;
    754   x = shelf->PrimaryAxisValue(end_position, 0);
    755   y = shelf->PrimaryAxisValue(0, end_position);
    756   for (int i = view_model_->view_size() - 1;
    757        i >= first_panel_index; --i) {
    758     x = shelf->PrimaryAxisValue(x - w - button_spacing, x);
    759     y = shelf->PrimaryAxisValue(y, y - h - button_spacing);
    760     view_model_->set_ideal_bounds(i, gfx::Rect(x, y, w, h));
    761     end_position = shelf->PrimaryAxisValue(x, y);
    762   }
    763 
    764   // Icons on the left / top are guaranteed up to kLeftIconProportion of
    765   // the available space.
    766   int last_icon_position = shelf->PrimaryAxisValue(
    767       view_model_->ideal_bounds(last_button_index).right(),
    768       view_model_->ideal_bounds(last_button_index).bottom())
    769       + button_size + inset;
    770   if (!ash::switches::UseAlternateShelfLayout())
    771       last_icon_position += button_size;
    772   int reserved_icon_space = available_size * kReservedNonPanelIconProportion;
    773   if (last_icon_position < reserved_icon_space)
    774     end_position = last_icon_position;
    775   else
    776     end_position = std::max(end_position, reserved_icon_space);
    777 
    778   bounds->overflow_bounds.set_size(gfx::Size(
    779       shelf->PrimaryAxisValue(w, width()),
    780       shelf->PrimaryAxisValue(height(), h)));
    781   if (ash::switches::UseAlternateShelfLayout())
    782     last_visible_index_ = DetermineLastVisibleIndex(
    783         end_position - button_size);
    784   else
    785     last_visible_index_ = DetermineLastVisibleIndex(
    786         end_position - inset - 2 * button_size);
    787   last_hidden_index_ = DetermineFirstVisiblePanelIndex(end_position) - 1;
    788   bool show_overflow =
    789       ((ash::switches::UseAlternateShelfLayout() ? 0 : 1) +
    790       last_visible_index_ < last_button_index ||
    791       last_hidden_index_ >= first_panel_index);
    792 
    793   // Create Space for the overflow button
    794   if (show_overflow && ash::switches::UseAlternateShelfLayout() &&
    795       last_visible_index_ > 0)
    796     --last_visible_index_;
    797   for (int i = 0; i < view_model_->view_size(); ++i) {
    798     bool visible = i <= last_visible_index_ || i > last_hidden_index_;
    799     // Always show the app list.
    800     if (!ash::switches::UseAlternateShelfLayout())
    801       visible |= (i == last_button_index);
    802     view_model_->view_at(i)->SetVisible(visible);
    803   }
    804 
    805   overflow_button_->SetVisible(show_overflow);
    806   if (show_overflow) {
    807     DCHECK_NE(0, view_model_->view_size());
    808     if (last_visible_index_ == -1) {
    809       x = shelf->SelectValueForShelfAlignment(inset, 0, 0, inset);
    810       y = shelf->SelectValueForShelfAlignment(0, inset, inset, 0);
    811     } else if (last_visible_index_ == last_button_index) {
    812       x = view_model_->ideal_bounds(last_visible_index_).x();
    813       y = view_model_->ideal_bounds(last_visible_index_).y();
    814     } else {
    815       x = shelf->PrimaryAxisValue(
    816           view_model_->ideal_bounds(last_visible_index_).right(),
    817           view_model_->ideal_bounds(last_visible_index_).x());
    818       y = shelf->PrimaryAxisValue(
    819           view_model_->ideal_bounds(last_visible_index_).y(),
    820           view_model_->ideal_bounds(last_visible_index_).bottom());
    821     }
    822     // Set all hidden panel icon positions to be on the overflow button.
    823     for (int i = first_panel_index; i <= last_hidden_index_; ++i)
    824       view_model_->set_ideal_bounds(i, gfx::Rect(x, y, w, h));
    825 
    826     bounds->overflow_bounds.set_x(x);
    827     bounds->overflow_bounds.set_y(y);
    828     if (!ash::switches::UseAlternateShelfLayout()) {
    829       // Position app list after overflow button.
    830       gfx::Rect app_list_bounds = view_model_->ideal_bounds(last_button_index);
    831 
    832       x = shelf->PrimaryAxisValue(x + w + button_spacing, x);
    833       y = shelf->PrimaryAxisValue(y, y + h + button_spacing);
    834       app_list_bounds.set_x(x);
    835       app_list_bounds.set_y(y);
    836       view_model_->set_ideal_bounds(last_button_index, app_list_bounds);
    837     }
    838     if (overflow_bubble_.get() && overflow_bubble_->IsShowing())
    839       UpdateOverflowRange(overflow_bubble_->launcher_view());
    840   } else {
    841     if (overflow_bubble_)
    842       overflow_bubble_->Hide();
    843   }
    844 }
    845 
    846 int LauncherView::DetermineLastVisibleIndex(int max_value) const {
    847   ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
    848 
    849   int index = model_->FirstPanelIndex() - 1;
    850   while (index >= 0 &&
    851          shelf->PrimaryAxisValue(
    852              view_model_->ideal_bounds(index).right(),
    853              view_model_->ideal_bounds(index).bottom()) > max_value) {
    854     index--;
    855   }
    856   return index;
    857 }
    858 
    859 int LauncherView::DetermineFirstVisiblePanelIndex(int min_value) const {
    860   ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
    861 
    862   int index = model_->FirstPanelIndex();
    863   while (index < view_model_->view_size() &&
    864          shelf->PrimaryAxisValue(
    865              view_model_->ideal_bounds(index).right(),
    866              view_model_->ideal_bounds(index).bottom()) < min_value) {
    867     ++index;
    868   }
    869   return index;
    870 }
    871 
    872 void LauncherView::AddIconObserver(LauncherIconObserver* observer) {
    873   observers_.AddObserver(observer);
    874 }
    875 
    876 void LauncherView::RemoveIconObserver(LauncherIconObserver* observer) {
    877   observers_.RemoveObserver(observer);
    878 }
    879 
    880 void LauncherView::AnimateToIdealBounds() {
    881   IdealBounds ideal_bounds;
    882   CalculateIdealBounds(&ideal_bounds);
    883   for (int i = 0; i < view_model_->view_size(); ++i) {
    884     View* view = view_model_->view_at(i);
    885     bounds_animator_->AnimateViewTo(view, view_model_->ideal_bounds(i));
    886     // Now that the item animation starts, we have to make sure that the
    887     // padding of the first gets properly transferred to the new first item.
    888     if (i && view->border())
    889       view->set_border(NULL);
    890     else if (!i && !view->border())
    891       UpdateFirstButtonPadding();
    892   }
    893   overflow_button_->SetBoundsRect(ideal_bounds.overflow_bounds);
    894 }
    895 
    896 views::View* LauncherView::CreateViewForItem(const LauncherItem& item) {
    897   views::View* view = NULL;
    898   switch (item.type) {
    899     case TYPE_TABBED: {
    900       TabbedLauncherButton* button =
    901           TabbedLauncherButton::Create(
    902               this,
    903               this,
    904               tooltip_->shelf_layout_manager(),
    905               item.is_incognito ?
    906                   TabbedLauncherButton::STATE_INCOGNITO :
    907                   TabbedLauncherButton::STATE_NOT_INCOGNITO);
    908       button->SetTabImage(item.image);
    909       ReflectItemStatus(item, button);
    910       view = button;
    911       break;
    912     }
    913 
    914     case TYPE_BROWSER_SHORTCUT:
    915     case TYPE_APP_SHORTCUT:
    916     case TYPE_WINDOWED_APP:
    917     case TYPE_PLATFORM_APP:
    918     case TYPE_APP_PANEL: {
    919       LauncherButton* button = LauncherButton::Create(
    920           this, this, tooltip_->shelf_layout_manager());
    921       button->SetImage(item.image);
    922       ReflectItemStatus(item, button);
    923       view = button;
    924       break;
    925     }
    926 
    927     case TYPE_APP_LIST: {
    928       if (ash::switches::UseAlternateShelfLayout()) {
    929         view = new AlternateAppListButton(this, this,
    930             tooltip_->shelf_layout_manager()->shelf_widget());
    931       } else {
    932         // TODO(dave): turn this into a LauncherButton too.
    933         AppListButton* button = new AppListButton(this, this);
    934         ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
    935         button->SetImageAlignment(
    936             shelf->SelectValueForShelfAlignment(
    937                 views::ImageButton::ALIGN_CENTER,
    938                 views::ImageButton::ALIGN_LEFT,
    939                 views::ImageButton::ALIGN_RIGHT,
    940                 views::ImageButton::ALIGN_CENTER),
    941             shelf->SelectValueForShelfAlignment(
    942                 views::ImageButton::ALIGN_TOP,
    943                 views::ImageButton::ALIGN_MIDDLE,
    944                 views::ImageButton::ALIGN_MIDDLE,
    945                 views::ImageButton::ALIGN_BOTTOM));
    946         view = button;
    947       }
    948       break;
    949     }
    950 
    951     default:
    952       break;
    953   }
    954   view->set_context_menu_controller(this);
    955   view->set_focus_border(new LauncherButtonFocusBorder);
    956 
    957   DCHECK(view);
    958   ConfigureChildView(view);
    959   return view;
    960 }
    961 
    962 void LauncherView::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 LauncherView::PrepareForDrag(Pointer pointer,
    971                                   const ui::LocatedEvent& event) {
    972   DCHECK(!dragging());
    973   DCHECK(drag_view_);
    974   drag_pointer_ = pointer;
    975   start_drag_index_ = view_model_->GetIndexOfView(drag_view_);
    976 
    977   // If the item is no longer draggable, bail out.
    978   if (start_drag_index_ == -1 ||
    979       !delegate_->IsDraggable(model_->items()[start_drag_index_])) {
    980     CancelDrag(-1);
    981     return;
    982   }
    983 
    984   // Move the view to the front so that it appears on top of other views.
    985   ReorderChildView(drag_view_, -1);
    986   bounds_animator_->StopAnimatingView(drag_view_);
    987 }
    988 
    989 void LauncherView::ContinueDrag(const ui::LocatedEvent& event) {
    990   ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
    991 
    992   // TODO: I don't think this works correctly with RTL.
    993   gfx::Point drag_point(event.location());
    994   views::View::ConvertPointToTarget(drag_view_, this, &drag_point);
    995   int current_index = view_model_->GetIndexOfView(drag_view_);
    996   DCHECK_NE(-1, current_index);
    997 
    998   // If the item is no longer draggable, bail out.
    999   if (current_index == -1 ||
   1000       !delegate_->IsDraggable(model_->items()[current_index])) {
   1001     CancelDrag(-1);
   1002     return;
   1003   }
   1004 
   1005   // Constrain the location to the range of valid indices for the type.
   1006   std::pair<int, int> indices(GetDragRange(current_index));
   1007   int first_drag_index = indices.first;
   1008   int last_drag_index = indices.second;
   1009   // If the last index isn't valid, we're overflowing. Constrain to the app list
   1010   // (which is the last visible item).
   1011   if (first_drag_index < model_->FirstPanelIndex() &&
   1012       last_drag_index > last_visible_index_)
   1013     last_drag_index = last_visible_index_;
   1014   int x = 0, y = 0;
   1015   if (shelf->IsHorizontalAlignment()) {
   1016     x = std::max(view_model_->ideal_bounds(indices.first).x(),
   1017                      drag_point.x() - drag_offset_);
   1018     x = std::min(view_model_->ideal_bounds(last_drag_index).right() -
   1019                  view_model_->ideal_bounds(current_index).width(),
   1020                  x);
   1021     if (drag_view_->x() == x)
   1022       return;
   1023     drag_view_->SetX(x);
   1024   } else {
   1025     y = std::max(view_model_->ideal_bounds(indices.first).y(),
   1026                      drag_point.y() - drag_offset_);
   1027     y = std::min(view_model_->ideal_bounds(last_drag_index).bottom() -
   1028                  view_model_->ideal_bounds(current_index).height(),
   1029                  y);
   1030     if (drag_view_->y() == y)
   1031       return;
   1032     drag_view_->SetY(y);
   1033   }
   1034 
   1035   int target_index =
   1036       views::ViewModelUtils::DetermineMoveIndex(
   1037           *view_model_, drag_view_,
   1038           shelf->IsHorizontalAlignment() ?
   1039               views::ViewModelUtils::HORIZONTAL :
   1040               views::ViewModelUtils::VERTICAL,
   1041           x, y);
   1042   target_index =
   1043       std::min(indices.second, std::max(target_index, indices.first));
   1044   if (target_index == current_index)
   1045     return;
   1046 
   1047   // Change the model, the LauncherItemMoved() callback will handle the
   1048   // |view_model_| update.
   1049   model_->Move(current_index, target_index);
   1050   bounds_animator_->StopAnimatingView(drag_view_);
   1051 }
   1052 
   1053 bool LauncherView::SameDragType(LauncherItemType typea,
   1054                                 LauncherItemType typeb) const {
   1055   switch (typea) {
   1056     case TYPE_TABBED:
   1057     case TYPE_PLATFORM_APP:
   1058       return (typeb == TYPE_TABBED || typeb == TYPE_PLATFORM_APP);
   1059     case TYPE_APP_SHORTCUT:
   1060     case TYPE_BROWSER_SHORTCUT:
   1061       return (typeb == TYPE_APP_SHORTCUT || typeb == TYPE_BROWSER_SHORTCUT);
   1062     case TYPE_WINDOWED_APP:
   1063     case TYPE_APP_LIST:
   1064     case TYPE_APP_PANEL:
   1065       return typeb == typea;
   1066   }
   1067   NOTREACHED();
   1068   return false;
   1069 }
   1070 
   1071 std::pair<int, int> LauncherView::GetDragRange(int index) {
   1072   int min_index = -1;
   1073   int max_index = -1;
   1074   LauncherItemType type = model_->items()[index].type;
   1075   for (int i = 0; i < model_->item_count(); ++i) {
   1076     if (SameDragType(model_->items()[i].type, type)) {
   1077       if (min_index == -1)
   1078         min_index = i;
   1079       max_index = i;
   1080     }
   1081   }
   1082   return std::pair<int, int>(min_index, max_index);
   1083 }
   1084 
   1085 void LauncherView::ConfigureChildView(views::View* view) {
   1086   view->SetPaintToLayer(true);
   1087   view->layer()->SetFillsBoundsOpaquely(false);
   1088 }
   1089 
   1090 void LauncherView::ToggleOverflowBubble() {
   1091   if (IsShowingOverflowBubble()) {
   1092     overflow_bubble_->Hide();
   1093     return;
   1094   }
   1095 
   1096   if (!overflow_bubble_)
   1097     overflow_bubble_.reset(new OverflowBubble());
   1098 
   1099   LauncherView* overflow_view = new LauncherView(
   1100       model_, delegate_, tooltip_->shelf_layout_manager());
   1101   overflow_view->Init();
   1102   overflow_view->OnShelfAlignmentChanged();
   1103   UpdateOverflowRange(overflow_view);
   1104 
   1105   overflow_bubble_->Show(overflow_button_, overflow_view);
   1106 
   1107   Shell::GetInstance()->UpdateShelfVisibility();
   1108 }
   1109 
   1110 void LauncherView::UpdateFirstButtonPadding() {
   1111   if (ash::switches::UseAlternateShelfLayout())
   1112     return;
   1113 
   1114   ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
   1115 
   1116   // Creates an empty border for first launcher button to make included leading
   1117   // inset act as the button's padding. This is only needed on button creation
   1118   // and when shelf alignment changes.
   1119   if (view_model_->view_size() > 0) {
   1120     view_model_->view_at(0)->set_border(views::Border::CreateEmptyBorder(
   1121         shelf->PrimaryAxisValue(0, leading_inset()),
   1122         shelf->PrimaryAxisValue(leading_inset(), 0),
   1123         0,
   1124         0));
   1125   }
   1126 }
   1127 
   1128 void LauncherView::OnFadeOutAnimationEnded() {
   1129   AnimateToIdealBounds();
   1130 
   1131   // If overflow button is visible and there is a valid new last item, fading
   1132   // the new last item in after sliding animation is finished.
   1133   if (overflow_button_->visible() && last_visible_index_ >= 0) {
   1134     views::View* last_visible_view = view_model_->view_at(last_visible_index_);
   1135     last_visible_view->layer()->SetOpacity(0);
   1136     bounds_animator_->SetAnimationDelegate(
   1137         last_visible_view,
   1138         new LauncherView::StartFadeAnimationDelegate(this, last_visible_view),
   1139         true);
   1140   }
   1141 }
   1142 
   1143 void LauncherView::UpdateOverflowRange(LauncherView* overflow_view) {
   1144   const int first_overflow_index = last_visible_index_ + 1;
   1145   const int last_overflow_index = last_hidden_index_;
   1146   DCHECK_LE(first_overflow_index, last_overflow_index);
   1147   DCHECK_LT(last_overflow_index, view_model_->view_size());
   1148 
   1149   overflow_view->first_visible_index_ = first_overflow_index;
   1150   overflow_view->last_visible_index_ = last_overflow_index;
   1151 }
   1152 
   1153 bool LauncherView::ShouldHideTooltip(const gfx::Point& cursor_location) {
   1154   gfx::Rect active_bounds;
   1155 
   1156   for (int i = 0; i < child_count(); ++i) {
   1157     views::View* child = child_at(i);
   1158     if (child == overflow_button_)
   1159       continue;
   1160     if (!ShouldShowTooltipForView(child))
   1161       continue;
   1162 
   1163     gfx::Rect child_bounds = child->GetMirroredBounds();
   1164     active_bounds.Union(child_bounds);
   1165   }
   1166 
   1167   return !active_bounds.Contains(cursor_location);
   1168 }
   1169 
   1170 int LauncherView::CancelDrag(int modified_index) {
   1171   if (!drag_view_)
   1172     return modified_index;
   1173   bool was_dragging = dragging();
   1174   int drag_view_index = view_model_->GetIndexOfView(drag_view_);
   1175   drag_pointer_ = NONE;
   1176   drag_view_ = NULL;
   1177   if (drag_view_index == modified_index) {
   1178     // The view that was being dragged is being modified. Don't do anything.
   1179     return modified_index;
   1180   }
   1181   if (!was_dragging)
   1182     return modified_index;
   1183 
   1184   // Restore previous position, tracking the position of the modified view.
   1185   bool at_end = modified_index == view_model_->view_size();
   1186   views::View* modified_view =
   1187       (modified_index >= 0 && !at_end) ?
   1188       view_model_->view_at(modified_index) : NULL;
   1189   model_->Move(drag_view_index, start_drag_index_);
   1190 
   1191   // If the modified view will be at the end of the list, return the new end of
   1192   // the list.
   1193   if (at_end)
   1194     return view_model_->view_size();
   1195   return modified_view ? view_model_->GetIndexOfView(modified_view) : -1;
   1196 }
   1197 
   1198 gfx::Size LauncherView::GetPreferredSize() {
   1199   IdealBounds ideal_bounds;
   1200   CalculateIdealBounds(&ideal_bounds);
   1201 
   1202   const int app_list_index = view_model_->view_size() - 1;
   1203   const int last_button_index = is_overflow_mode() ?
   1204       last_visible_index_ : app_list_index;
   1205   const gfx::Rect last_button_bounds =
   1206       last_button_index  >= first_visible_index_ ?
   1207           view_model_->view_at(last_button_index)->bounds() :
   1208           gfx::Rect(gfx::Size(kLauncherPreferredSize,
   1209                               kLauncherPreferredSize));
   1210 
   1211   ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
   1212 
   1213   if (shelf->IsHorizontalAlignment()) {
   1214     return gfx::Size(last_button_bounds.right() + leading_inset(),
   1215                      kLauncherPreferredSize);
   1216   }
   1217 
   1218   return gfx::Size(kLauncherPreferredSize,
   1219                    last_button_bounds.bottom() + leading_inset());
   1220 }
   1221 
   1222 void LauncherView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
   1223   LayoutToIdealBounds();
   1224   FOR_EACH_OBSERVER(LauncherIconObserver, observers_,
   1225                     OnLauncherIconPositionsChanged());
   1226 
   1227   if (IsShowingOverflowBubble())
   1228     overflow_bubble_->Hide();
   1229 }
   1230 
   1231 views::FocusTraversable* LauncherView::GetPaneFocusTraversable() {
   1232   return this;
   1233 }
   1234 
   1235 void LauncherView::GetAccessibleState(ui::AccessibleViewState* state) {
   1236   state->role = ui::AccessibilityTypes::ROLE_TOOLBAR;
   1237   state->name = l10n_util::GetStringUTF16(IDS_ASH_SHELF_ACCESSIBLE_NAME);
   1238 }
   1239 
   1240 void LauncherView::OnGestureEvent(ui::GestureEvent* event) {
   1241   if (gesture_handler_.ProcessGestureEvent(*event))
   1242     event->StopPropagation();
   1243 }
   1244 
   1245 void LauncherView::LauncherItemAdded(int model_index) {
   1246   {
   1247     base::AutoReset<bool> cancelling_drag(
   1248         &cancelling_drag_model_changed_, true);
   1249     model_index = CancelDrag(model_index);
   1250   }
   1251   views::View* view = CreateViewForItem(model_->items()[model_index]);
   1252   AddChildView(view);
   1253   // Hide the view, it'll be made visible when the animation is done. Using
   1254   // opacity 0 here to avoid messing with CalculateIdealBounds which touches
   1255   // the view's visibility.
   1256   view->layer()->SetOpacity(0);
   1257   view_model_->Add(view, model_index);
   1258 
   1259   // Give the button its ideal bounds. That way if we end up animating the
   1260   // button before this animation completes it doesn't appear at some random
   1261   // spot (because it was in the middle of animating from 0,0 0x0 to its
   1262   // target).
   1263   IdealBounds ideal_bounds;
   1264   CalculateIdealBounds(&ideal_bounds);
   1265   view->SetBoundsRect(view_model_->ideal_bounds(model_index));
   1266 
   1267   // The first animation moves all the views to their target position. |view|
   1268   // is hidden, so it visually appears as though we are providing space for
   1269   // it. When done we'll fade the view in.
   1270   AnimateToIdealBounds();
   1271   if (model_index <= last_visible_index_ ||
   1272       model_index >= model_->FirstPanelIndex()) {
   1273     bounds_animator_->SetAnimationDelegate(
   1274         view, new StartFadeAnimationDelegate(this, view), true);
   1275   } else {
   1276     // Undo the hiding if animation does not run.
   1277     view->layer()->SetOpacity(1.0f);
   1278   }
   1279 }
   1280 
   1281 void LauncherView::LauncherItemRemoved(int model_index, LauncherID id) {
   1282 #if !defined(OS_MACOSX)
   1283   if (id == context_menu_id_)
   1284     launcher_menu_runner_.reset();
   1285 #endif
   1286   {
   1287     base::AutoReset<bool> cancelling_drag(
   1288         &cancelling_drag_model_changed_, true);
   1289     model_index = CancelDrag(model_index);
   1290   }
   1291   views::View* view = view_model_->view_at(model_index);
   1292   view_model_->Remove(model_index);
   1293   // The first animation fades out the view. When done we'll animate the rest of
   1294   // the views to their target location.
   1295   bounds_animator_->AnimateViewTo(view, view->bounds());
   1296   bounds_animator_->SetAnimationDelegate(
   1297       view, new FadeOutAnimationDelegate(this, view), true);
   1298 
   1299   // If overflow bubble is visible, sanitize overflow range first and when the
   1300   // above animation finishes, CalculateIdealBounds will be called to get
   1301   // correct overflow range. CalculateIdealBounds could hide overflow bubble
   1302   // and triggers LauncherItemChanged. And since we are still in the middle
   1303   // of LauncherItemRemoved, LauncherView in overflow bubble is not synced
   1304   // with LauncherModel and will crash.
   1305   if (overflow_bubble_ && overflow_bubble_->IsShowing()) {
   1306     last_hidden_index_ = std::min(last_hidden_index_,
   1307                                   view_model_->view_size() - 1);
   1308     UpdateOverflowRange(overflow_bubble_->launcher_view());
   1309   }
   1310 }
   1311 
   1312 void LauncherView::LauncherItemChanged(int model_index,
   1313                                        const ash::LauncherItem& old_item) {
   1314   const LauncherItem& item(model_->items()[model_index]);
   1315   if (old_item.type != item.type) {
   1316     // Type changed, swap the views.
   1317     model_index = CancelDrag(model_index);
   1318     scoped_ptr<views::View> old_view(view_model_->view_at(model_index));
   1319     bounds_animator_->StopAnimatingView(old_view.get());
   1320     view_model_->Remove(model_index);
   1321     views::View* new_view = CreateViewForItem(item);
   1322     AddChildView(new_view);
   1323     view_model_->Add(new_view, model_index);
   1324     new_view->SetBoundsRect(old_view->bounds());
   1325     return;
   1326   }
   1327 
   1328   views::View* view = view_model_->view_at(model_index);
   1329   switch (item.type) {
   1330     case TYPE_TABBED: {
   1331       TabbedLauncherButton* button = static_cast<TabbedLauncherButton*>(view);
   1332       gfx::Size pref = button->GetPreferredSize();
   1333       button->SetTabImage(item.image);
   1334       if (pref != button->GetPreferredSize())
   1335         AnimateToIdealBounds();
   1336       else
   1337         button->SchedulePaint();
   1338       ReflectItemStatus(item, button);
   1339       break;
   1340     }
   1341     case TYPE_BROWSER_SHORTCUT:
   1342       // Fallthrough for the new Launcher since it needs to show the activation
   1343       // change as well.
   1344     case TYPE_APP_SHORTCUT:
   1345     case TYPE_WINDOWED_APP:
   1346     case TYPE_PLATFORM_APP:
   1347     case TYPE_APP_PANEL: {
   1348       LauncherButton* button = static_cast<LauncherButton*>(view);
   1349       ReflectItemStatus(item, button);
   1350       // The browser shortcut is currently not a "real" item and as such the
   1351       // the image is bogous as well. We therefore keep the image as is for it.
   1352       if (item.type != TYPE_BROWSER_SHORTCUT)
   1353         button->SetImage(item.image);
   1354       button->SchedulePaint();
   1355       break;
   1356     }
   1357 
   1358     default:
   1359       break;
   1360   }
   1361 }
   1362 
   1363 void LauncherView::LauncherItemMoved(int start_index, int target_index) {
   1364   view_model_->Move(start_index, target_index);
   1365   // When cancelling a drag due to a launcher item being added, the currently
   1366   // dragged item is moved back to its initial position. AnimateToIdealBounds
   1367   // will be called again when the new item is added to the |view_model_| but
   1368   // at this time the |view_model_| is inconsistent with the |model_|.
   1369   if (!cancelling_drag_model_changed_)
   1370     AnimateToIdealBounds();
   1371 }
   1372 
   1373 void LauncherView::LauncherStatusChanged() {
   1374   if (ash::switches::UseAlternateShelfLayout())
   1375     return;
   1376   AppListButton* app_list_button =
   1377       static_cast<AppListButton*>(GetAppListButtonView());
   1378   if (model_->status() == LauncherModel::STATUS_LOADING)
   1379     app_list_button->StartLoadingAnimation();
   1380   else
   1381     app_list_button->StopLoadingAnimation();
   1382 }
   1383 
   1384 void LauncherView::PointerPressedOnButton(views::View* view,
   1385                                           Pointer pointer,
   1386                                           const ui::LocatedEvent& event) {
   1387   if (drag_view_)
   1388     return;
   1389 
   1390   tooltip_->Close();
   1391   int index = view_model_->GetIndexOfView(view);
   1392   if (index == -1 ||
   1393       view_model_->view_size() <= 1 ||
   1394       !delegate_->IsDraggable(model_->items()[index]))
   1395     return;  // View is being deleted or not draggable, ignore request.
   1396 
   1397   ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
   1398 
   1399   drag_view_ = view;
   1400   drag_offset_ = shelf->PrimaryAxisValue(event.x(), event.y());
   1401 }
   1402 
   1403 void LauncherView::PointerDraggedOnButton(views::View* view,
   1404                                           Pointer pointer,
   1405                                           const ui::LocatedEvent& event) {
   1406   ShelfLayoutManager* shelf = tooltip_->shelf_layout_manager();
   1407   if (!dragging() && drag_view_ &&
   1408       shelf->PrimaryAxisValue(abs(event.x() - drag_offset_),
   1409                               abs(event.y() - drag_offset_)) >=
   1410       kMinimumDragDistance) {
   1411     PrepareForDrag(pointer, event);
   1412   }
   1413   if (drag_pointer_ == pointer)
   1414     ContinueDrag(event);
   1415 }
   1416 
   1417 void LauncherView::PointerReleasedOnButton(views::View* view,
   1418                                            Pointer pointer,
   1419                                            bool canceled) {
   1420   if (canceled) {
   1421     CancelDrag(-1);
   1422   } else if (drag_pointer_ == pointer) {
   1423     drag_pointer_ = NONE;
   1424     AnimateToIdealBounds();
   1425   }
   1426   // If the drag pointer is NONE, no drag operation is going on and the
   1427   // drag_view can be released.
   1428   if (drag_pointer_ == NONE)
   1429     drag_view_ = NULL;
   1430 }
   1431 
   1432 void LauncherView::MouseMovedOverButton(views::View* view) {
   1433   if (!ShouldShowTooltipForView(view))
   1434     return;
   1435 
   1436   if (!tooltip_->IsVisible())
   1437     tooltip_->ResetTimer();
   1438 }
   1439 
   1440 void LauncherView::MouseEnteredButton(views::View* view) {
   1441   if (!ShouldShowTooltipForView(view))
   1442     return;
   1443 
   1444   if (tooltip_->IsVisible()) {
   1445     tooltip_->ShowImmediately(view, GetAccessibleName(view));
   1446   } else {
   1447     tooltip_->ShowDelayed(view, GetAccessibleName(view));
   1448   }
   1449 }
   1450 
   1451 void LauncherView::MouseExitedButton(views::View* view) {
   1452   if (!tooltip_->IsVisible())
   1453     tooltip_->StopTimer();
   1454 }
   1455 
   1456 base::string16 LauncherView::GetAccessibleName(const views::View* view) {
   1457   int view_index = view_model_->GetIndexOfView(view);
   1458   // May be -1 while in the process of animating closed.
   1459   if (view_index == -1)
   1460     return base::string16();
   1461 
   1462   switch (model_->items()[view_index].type) {
   1463     case TYPE_TABBED:
   1464     case TYPE_APP_PANEL:
   1465     case TYPE_APP_SHORTCUT:
   1466     case TYPE_WINDOWED_APP:
   1467     case TYPE_PLATFORM_APP:
   1468     case TYPE_BROWSER_SHORTCUT:
   1469       return delegate_->GetTitle(model_->items()[view_index]);
   1470 
   1471     case TYPE_APP_LIST:
   1472       return model_->status() == LauncherModel::STATUS_LOADING ?
   1473           l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_SYNCING_TITLE) :
   1474           l10n_util::GetStringUTF16(IDS_AURA_APP_LIST_TITLE);
   1475   }
   1476   return base::string16();
   1477 }
   1478 
   1479 void LauncherView::ButtonPressed(views::Button* sender,
   1480                                  const ui::Event& event) {
   1481   // Do not handle mouse release during drag.
   1482   if (dragging())
   1483     return;
   1484 
   1485   tooltip_->Close();
   1486 
   1487   if (sender == overflow_button_) {
   1488     ToggleOverflowBubble();
   1489     return;
   1490   }
   1491 
   1492   int view_index = view_model_->GetIndexOfView(sender);
   1493   // May be -1 while in the process of animating closed.
   1494   if (view_index == -1)
   1495     return;
   1496 
   1497   // If the previous menu was closed by the same event as this one, we ignore
   1498   // the call.
   1499   if (!IsUsableEvent(event))
   1500     return;
   1501 
   1502   {
   1503     ScopedTargetRootWindow scoped_target(
   1504         sender->GetWidget()->GetNativeView()->GetRootWindow());
   1505     // Slow down activation animations if shift key is pressed.
   1506     scoped_ptr<ui::ScopedAnimationDurationScaleMode> slowing_animations;
   1507     if (event.IsShiftDown()) {
   1508       slowing_animations.reset(new ui::ScopedAnimationDurationScaleMode(
   1509             ui::ScopedAnimationDurationScaleMode::SLOW_DURATION));
   1510     }
   1511 
   1512     // Collect usage statistics before we decide what to do with the click.
   1513     switch (model_->items()[view_index].type) {
   1514       case TYPE_APP_SHORTCUT:
   1515       case TYPE_WINDOWED_APP:
   1516       case TYPE_PLATFORM_APP:
   1517       case TYPE_BROWSER_SHORTCUT:
   1518         Shell::GetInstance()->delegate()->RecordUserMetricsAction(
   1519             UMA_LAUNCHER_CLICK_ON_APP);
   1520         // Fallthrough
   1521       case TYPE_TABBED:
   1522       case TYPE_APP_PANEL:
   1523         delegate_->ItemSelected(model_->items()[view_index], event);
   1524         break;
   1525 
   1526       case TYPE_APP_LIST:
   1527         Shell::GetInstance()->delegate()->RecordUserMetricsAction(
   1528             UMA_LAUNCHER_CLICK_ON_APPLIST_BUTTON);
   1529         Shell::GetInstance()->ToggleAppList(GetWidget()->GetNativeView());
   1530         break;
   1531     }
   1532   }
   1533 
   1534   if (model_->items()[view_index].type != TYPE_APP_LIST)
   1535     ShowListMenuForView(model_->items()[view_index], sender, event);
   1536 }
   1537 
   1538 bool LauncherView::ShowListMenuForView(const LauncherItem& item,
   1539                                        views::View* source,
   1540                                        const ui::Event& event) {
   1541   scoped_ptr<ash::LauncherMenuModel> menu_model;
   1542   menu_model.reset(delegate_->CreateApplicationMenu(item, event.flags()));
   1543 
   1544   // Make sure we have a menu and it has at least two items in addition to the
   1545   // application title and the 3 spacing separators.
   1546   if (!menu_model.get() || menu_model->GetItemCount() <= 5)
   1547     return false;
   1548 
   1549   ShowMenu(scoped_ptr<views::MenuModelAdapter>(
   1550                new LauncherMenuModelAdapter(menu_model.get())),
   1551            source,
   1552            gfx::Point(),
   1553            false,
   1554            ui::GetMenuSourceTypeForEvent(event));
   1555   return true;
   1556 }
   1557 
   1558 void LauncherView::ShowContextMenuForView(views::View* source,
   1559                                           const gfx::Point& point,
   1560                                           ui:: MenuSourceType source_type) {
   1561   int view_index = view_model_->GetIndexOfView(source);
   1562   if (view_index != -1 &&
   1563       model_->items()[view_index].type == TYPE_APP_LIST) {
   1564     view_index = -1;
   1565   }
   1566 
   1567   tooltip_->Close();
   1568 
   1569   if (view_index == -1) {
   1570     Shell::GetInstance()->ShowContextMenu(point, source_type);
   1571     return;
   1572   }
   1573   scoped_ptr<ui::MenuModel> menu_model(delegate_->CreateContextMenu(
   1574       model_->items()[view_index],
   1575       source->GetWidget()->GetNativeView()->GetRootWindow()));
   1576   if (!menu_model)
   1577     return;
   1578   base::AutoReset<LauncherID> reseter(
   1579       &context_menu_id_,
   1580       view_index == -1 ? 0 : model_->items()[view_index].id);
   1581 
   1582   ShowMenu(scoped_ptr<views::MenuModelAdapter>(
   1583                new views::MenuModelAdapter(menu_model.get())),
   1584            source,
   1585            point,
   1586            true,
   1587            source_type);
   1588 }
   1589 
   1590 void LauncherView::ShowMenu(
   1591     scoped_ptr<views::MenuModelAdapter> menu_model_adapter,
   1592     views::View* source,
   1593     const gfx::Point& click_point,
   1594     bool context_menu,
   1595     ui::MenuSourceType source_type) {
   1596   closing_event_time_ = base::TimeDelta();
   1597   launcher_menu_runner_.reset(
   1598       new views::MenuRunner(menu_model_adapter->CreateMenu()));
   1599 
   1600   ScopedTargetRootWindow scoped_target(
   1601       source->GetWidget()->GetNativeView()->GetRootWindow());
   1602 
   1603   // Determine the menu alignment dependent on the shelf.
   1604   views::MenuItemView::AnchorPosition menu_alignment =
   1605       views::MenuItemView::TOPLEFT;
   1606   gfx::Rect anchor_point = gfx::Rect(click_point, gfx::Size());
   1607 
   1608   ShelfWidget* shelf = RootWindowController::ForLauncher(
   1609       GetWidget()->GetNativeView())->shelf();
   1610   if (!context_menu) {
   1611     // Application lists use a bubble.
   1612     ash::ShelfAlignment align = shelf->GetAlignment();
   1613     anchor_point = source->GetBoundsInScreen();
   1614 
   1615     // It is possible to invoke the menu while it is sliding into view. To cover
   1616     // that case, the screen coordinates are offsetted by the animation delta.
   1617     gfx::Vector2d offset =
   1618         source->GetWidget()->GetNativeWindow()->bounds().origin() -
   1619         source->GetWidget()->GetNativeWindow()->GetTargetBounds().origin();
   1620     anchor_point.set_x(anchor_point.x() - offset.x());
   1621     anchor_point.set_y(anchor_point.y() - offset.y());
   1622 
   1623     // Launcher items can have an asymmetrical border for spacing reasons.
   1624     // Adjust anchor location for this.
   1625     if (source->border())
   1626       anchor_point.Inset(source->border()->GetInsets());
   1627 
   1628     switch (align) {
   1629       case ash::SHELF_ALIGNMENT_BOTTOM:
   1630         menu_alignment = views::MenuItemView::BUBBLE_ABOVE;
   1631         break;
   1632       case ash::SHELF_ALIGNMENT_LEFT:
   1633         menu_alignment = views::MenuItemView::BUBBLE_RIGHT;
   1634         break;
   1635       case ash::SHELF_ALIGNMENT_RIGHT:
   1636         menu_alignment = views::MenuItemView::BUBBLE_LEFT;
   1637         break;
   1638       case ash::SHELF_ALIGNMENT_TOP:
   1639         menu_alignment = views::MenuItemView::BUBBLE_BELOW;
   1640         break;
   1641     }
   1642   }
   1643   // If this gets deleted while we are in the menu, the launcher will be gone
   1644   // as well.
   1645   bool got_deleted = false;
   1646   got_deleted_ = &got_deleted;
   1647 
   1648   shelf->ForceUndimming(true);
   1649   // NOTE: if you convert to HAS_MNEMONICS be sure and update menu building
   1650   // code.
   1651   if (launcher_menu_runner_->RunMenuAt(
   1652           source->GetWidget(),
   1653           NULL,
   1654           anchor_point,
   1655           menu_alignment,
   1656           source_type,
   1657           context_menu ? views::MenuRunner::CONTEXT_MENU : 0) ==
   1658       views::MenuRunner::MENU_DELETED) {
   1659     if (!got_deleted) {
   1660       got_deleted_ = NULL;
   1661       shelf->ForceUndimming(false);
   1662     }
   1663     return;
   1664   }
   1665   got_deleted_ = NULL;
   1666   shelf->ForceUndimming(false);
   1667 
   1668   // Unpinning an item will reset the |launcher_menu_runner_| before coming
   1669   // here.
   1670   if (launcher_menu_runner_)
   1671     closing_event_time_ = launcher_menu_runner_->closing_event_time();
   1672   Shell::GetInstance()->UpdateShelfVisibility();
   1673 }
   1674 
   1675 void LauncherView::OnBoundsAnimatorProgressed(views::BoundsAnimator* animator) {
   1676   FOR_EACH_OBSERVER(LauncherIconObserver, observers_,
   1677                     OnLauncherIconPositionsChanged());
   1678   PreferredSizeChanged();
   1679 }
   1680 
   1681 void LauncherView::OnBoundsAnimatorDone(views::BoundsAnimator* animator) {
   1682 }
   1683 
   1684 bool LauncherView::IsUsableEvent(const ui::Event& event) {
   1685   if (closing_event_time_ == base::TimeDelta())
   1686     return true;
   1687 
   1688   base::TimeDelta delta =
   1689       base::TimeDelta(event.time_stamp() - closing_event_time_);
   1690   closing_event_time_ = base::TimeDelta();
   1691   // TODO(skuhne): This time seems excessive, but it appears that the reposting
   1692   // takes that long.  Need to come up with a better way of doing this.
   1693   return (delta.InMilliseconds() < 0 || delta.InMilliseconds() > 130);
   1694 }
   1695 
   1696 const LauncherItem* LauncherView::LauncherItemForView(
   1697     const views::View* view) const {
   1698   int view_index = view_model_->GetIndexOfView(view);
   1699   if (view_index == -1)
   1700     return NULL;
   1701   return &(model_->items()[view_index]);
   1702 }
   1703 
   1704 bool LauncherView::ShouldShowTooltipForView(const views::View* view) const {
   1705   if (view == GetAppListButtonView() &&
   1706       Shell::GetInstance()->GetAppListWindow())
   1707     return false;
   1708   const LauncherItem* item = LauncherItemForView(view);
   1709   return (!item || delegate_->ShouldShowTooltip(*item));
   1710 }
   1711 
   1712 }  // namespace internal
   1713 }  // namespace ash
   1714