Home | History | Annotate | Download | only in views
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/app_list/views/apps_grid_view.h"
      6 
      7 #include <algorithm>
      8 #include <set>
      9 #include <string>
     10 
     11 #include "base/guid.h"
     12 #include "ui/app_list/app_list_constants.h"
     13 #include "ui/app_list/app_list_folder_item.h"
     14 #include "ui/app_list/app_list_item.h"
     15 #include "ui/app_list/app_list_switches.h"
     16 #include "ui/app_list/views/app_list_drag_and_drop_host.h"
     17 #include "ui/app_list/views/app_list_folder_view.h"
     18 #include "ui/app_list/views/app_list_item_view.h"
     19 #include "ui/app_list/views/apps_grid_view_delegate.h"
     20 #include "ui/app_list/views/page_switcher.h"
     21 #include "ui/app_list/views/pulsing_block_view.h"
     22 #include "ui/app_list/views/top_icon_animation_view.h"
     23 #include "ui/compositor/scoped_layer_animation_settings.h"
     24 #include "ui/events/event.h"
     25 #include "ui/gfx/animation/animation.h"
     26 #include "ui/views/border.h"
     27 #include "ui/views/view_model_utils.h"
     28 #include "ui/views/widget/widget.h"
     29 
     30 #if defined(USE_AURA)
     31 #include "ui/aura/window.h"
     32 #include "ui/aura/window_event_dispatcher.h"
     33 #if defined(OS_WIN)
     34 #include "ui/views/win/hwnd_util.h"
     35 #endif  // defined(OS_WIN)
     36 #endif  // defined(USE_AURA)
     37 
     38 #if defined(OS_WIN)
     39 #include "base/command_line.h"
     40 #include "base/files/file_path.h"
     41 #include "base/win/shortcut.h"
     42 #include "ui/base/dragdrop/drag_utils.h"
     43 #include "ui/base/dragdrop/drop_target_win.h"
     44 #include "ui/base/dragdrop/os_exchange_data.h"
     45 #include "ui/base/dragdrop/os_exchange_data_provider_win.h"
     46 #include "ui/gfx/win/dpi.h"
     47 #endif
     48 
     49 namespace app_list {
     50 
     51 namespace {
     52 
     53 // Distance a drag needs to be from the app grid to be considered 'outside', at
     54 // which point we rearrange the apps to their pre-drag configuration, as a drop
     55 // then would be canceled. We have a buffer to make it easier to drag apps to
     56 // other pages.
     57 const int kDragBufferPx = 20;
     58 
     59 // Padding space in pixels for fixed layout.
     60 const int kLeftRightPadding = 20;
     61 const int kTopPadding = 1;
     62 
     63 // Padding space in pixels between pages.
     64 const int kPagePadding = 40;
     65 
     66 // Preferred tile size when showing in fixed layout.
     67 const int kPreferredTileWidth = 88;
     68 const int kPreferredTileHeight = 98;
     69 
     70 // Width in pixels of the area on the sides that triggers a page flip.
     71 const int kPageFlipZoneSize = 40;
     72 
     73 // Delay in milliseconds to do the page flip.
     74 const int kPageFlipDelayInMs = 1000;
     75 
     76 // How many pages on either side of the selected one we prerender.
     77 const int kPrerenderPages = 1;
     78 
     79 // The drag and drop proxy should get scaled by this factor.
     80 const float kDragAndDropProxyScale = 1.5f;
     81 
     82 // Delays in milliseconds to show folder dropping preview circle.
     83 const int kFolderDroppingDelay = 150;
     84 
     85 // Delays in milliseconds to show re-order preview.
     86 const int kReorderDelay = 120;
     87 
     88 // Delays in milliseconds to show folder item reparent UI.
     89 const int kFolderItemReparentDelay = 50;
     90 
     91 // Radius of the circle, in which if entered, show folder dropping preview
     92 // UI.
     93 const int kFolderDroppingCircleRadius = 15;
     94 
     95 
     96 // RowMoveAnimationDelegate is used when moving an item into a different row.
     97 // Before running the animation, the item's layer is re-created and kept in
     98 // the original position, then the item is moved to just before its target
     99 // position and opacity set to 0. When the animation runs, this delegate moves
    100 // the layer and fades it out while fading in the item at the same time.
    101 class RowMoveAnimationDelegate : public gfx::AnimationDelegate {
    102  public:
    103   RowMoveAnimationDelegate(views::View* view,
    104                            ui::Layer* layer,
    105                            const gfx::Rect& layer_target)
    106       : view_(view),
    107         layer_(layer),
    108         layer_start_(layer ? layer->bounds() : gfx::Rect()),
    109         layer_target_(layer_target) {
    110   }
    111   virtual ~RowMoveAnimationDelegate() {}
    112 
    113   // gfx::AnimationDelegate overrides:
    114   virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE {
    115     view_->layer()->SetOpacity(animation->GetCurrentValue());
    116     view_->layer()->ScheduleDraw();
    117 
    118     if (layer_) {
    119       layer_->SetOpacity(1 - animation->GetCurrentValue());
    120       layer_->SetBounds(animation->CurrentValueBetween(layer_start_,
    121                                                        layer_target_));
    122       layer_->ScheduleDraw();
    123     }
    124   }
    125   virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE {
    126     view_->layer()->SetOpacity(1.0f);
    127     view_->SchedulePaint();
    128   }
    129   virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE {
    130     view_->layer()->SetOpacity(1.0f);
    131     view_->SchedulePaint();
    132   }
    133 
    134  private:
    135   // The view that needs to be wrapped. Owned by views hierarchy.
    136   views::View* view_;
    137 
    138   scoped_ptr<ui::Layer> layer_;
    139   const gfx::Rect layer_start_;
    140   const gfx::Rect layer_target_;
    141 
    142   DISALLOW_COPY_AND_ASSIGN(RowMoveAnimationDelegate);
    143 };
    144 
    145 // ItemRemoveAnimationDelegate is used to show animation for removing an item.
    146 // This happens when user drags an item into a folder. The dragged item will
    147 // be removed from the original list after it is dropped into the folder.
    148 class ItemRemoveAnimationDelegate : public gfx::AnimationDelegate {
    149  public:
    150   explicit ItemRemoveAnimationDelegate(views::View* view)
    151       : view_(view) {
    152   }
    153 
    154   virtual ~ItemRemoveAnimationDelegate() {
    155   }
    156 
    157   // gfx::AnimationDelegate overrides:
    158   virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE {
    159     view_->layer()->SetOpacity(1 - animation->GetCurrentValue());
    160     view_->layer()->ScheduleDraw();
    161   }
    162 
    163  private:
    164   scoped_ptr<views::View> view_;
    165 
    166   DISALLOW_COPY_AND_ASSIGN(ItemRemoveAnimationDelegate);
    167 };
    168 
    169 // ItemMoveAnimationDelegate observes when an item finishes animating when it is
    170 // not moving between rows. This is to ensure an item is repainted for the
    171 // "zoom out" case when releasing an item being dragged.
    172 class ItemMoveAnimationDelegate : public gfx::AnimationDelegate {
    173  public:
    174   ItemMoveAnimationDelegate(views::View* view) : view_(view) {}
    175 
    176   virtual void AnimationEnded(const gfx::Animation* animation) OVERRIDE {
    177     view_->SchedulePaint();
    178   }
    179   virtual void AnimationCanceled(const gfx::Animation* animation) OVERRIDE {
    180     view_->SchedulePaint();
    181   }
    182 
    183  private:
    184   views::View* view_;
    185 
    186   DISALLOW_COPY_AND_ASSIGN(ItemMoveAnimationDelegate);
    187 };
    188 
    189 // Gets the distance between the centers of the |rect_1| and |rect_2|.
    190 int GetDistanceBetweenRects(gfx::Rect rect_1,
    191                             gfx::Rect rect_2) {
    192   return (rect_1.CenterPoint() - rect_2.CenterPoint()).Length();
    193 }
    194 
    195 // Returns true if the |item| is an folder item.
    196 bool IsFolderItem(AppListItem* item) {
    197   return (item->GetItemType() == AppListFolderItem::kItemType);
    198 }
    199 
    200 bool IsOEMFolderItem(AppListItem* item) {
    201   return IsFolderItem(item) &&
    202          (static_cast<AppListFolderItem*>(item))->folder_type() ==
    203              AppListFolderItem::FOLDER_TYPE_OEM;
    204 }
    205 
    206 }  // namespace
    207 
    208 #if defined(OS_WIN)
    209 // Interprets drag events sent from Windows via the drag/drop API and forwards
    210 // them to AppsGridView.
    211 // On Windows, in order to have the OS perform the drag properly we need to
    212 // provide it with a shortcut file which may or may not exist at the time the
    213 // drag is started. Therefore while waiting for that shortcut to be located we
    214 // just do a regular "internal" drag and transition into the synchronous drag
    215 // when the shortcut is found/created. Hence a synchronous drag is an optional
    216 // phase of a regular drag and non-Windows platforms drags are equivalent to a
    217 // Windows drag that never enters the synchronous drag phase.
    218 class SynchronousDrag : public ui::DragSourceWin {
    219  public:
    220   SynchronousDrag(AppsGridView* grid_view,
    221                   AppListItemView* drag_view,
    222                   const gfx::Point& drag_view_offset)
    223       : grid_view_(grid_view),
    224         drag_view_(drag_view),
    225         drag_view_offset_(drag_view_offset),
    226         has_shortcut_path_(false),
    227         running_(false),
    228         canceled_(false) {}
    229 
    230   void set_shortcut_path(const base::FilePath& shortcut_path) {
    231     has_shortcut_path_ = true;
    232     shortcut_path_ = shortcut_path;
    233   }
    234 
    235   bool running() { return running_; }
    236 
    237   bool CanRun() {
    238     return has_shortcut_path_ && !running_;
    239   }
    240 
    241   void Run() {
    242     DCHECK(CanRun());
    243 
    244     // Prevent the synchronous dragger being destroyed while the drag is
    245     // running.
    246     scoped_refptr<SynchronousDrag> this_ref = this;
    247     running_ = true;
    248 
    249     ui::OSExchangeData data;
    250     SetupExchangeData(&data);
    251 
    252     // Hide the dragged view because the OS is going to create its own.
    253     drag_view_->SetVisible(false);
    254 
    255     // Blocks until the drag is finished. Calls into the ui::DragSourceWin
    256     // methods.
    257     DWORD effects;
    258     DoDragDrop(ui::OSExchangeDataProviderWin::GetIDataObject(data),
    259                this, DROPEFFECT_MOVE | DROPEFFECT_LINK, &effects);
    260 
    261     // If |drag_view_| is NULL the drag was ended by some reentrant code.
    262     if (drag_view_) {
    263       // Make the drag view visible again.
    264       drag_view_->SetVisible(true);
    265       drag_view_->OnSyncDragEnd();
    266 
    267       grid_view_->EndDrag(canceled_ || !IsCursorWithinGridView());
    268     }
    269   }
    270 
    271   void EndDragExternally() {
    272     CancelDrag();
    273     drag_view_ = NULL;
    274   }
    275 
    276  private:
    277   // Overridden from ui::DragSourceWin.
    278   virtual void OnDragSourceCancel() OVERRIDE {
    279     canceled_ = true;
    280   }
    281 
    282   virtual void OnDragSourceDrop() OVERRIDE {
    283   }
    284 
    285   virtual void OnDragSourceMove() OVERRIDE {
    286     grid_view_->UpdateDrag(AppsGridView::MOUSE, GetCursorInGridViewCoords());
    287   }
    288 
    289   void SetupExchangeData(ui::OSExchangeData* data) {
    290     data->SetFilename(shortcut_path_);
    291     gfx::ImageSkia image(drag_view_->GetDragImage());
    292     gfx::Size image_size(image.size());
    293     drag_utils::SetDragImageOnDataObject(
    294         image,
    295         drag_view_offset_ - drag_view_->GetDragImageOffset(),
    296         data);
    297   }
    298 
    299   HWND GetGridViewHWND() {
    300     return views::HWNDForView(grid_view_);
    301   }
    302 
    303   bool IsCursorWithinGridView() {
    304     POINT p;
    305     GetCursorPos(&p);
    306     return GetGridViewHWND() == WindowFromPoint(p);
    307   }
    308 
    309   gfx::Point GetCursorInGridViewCoords() {
    310     POINT p;
    311     GetCursorPos(&p);
    312     ScreenToClient(GetGridViewHWND(), &p);
    313     gfx::Point grid_view_pt(p.x, p.y);
    314     grid_view_pt = gfx::win::ScreenToDIPPoint(grid_view_pt);
    315     views::View::ConvertPointFromWidget(grid_view_, &grid_view_pt);
    316     return grid_view_pt;
    317   }
    318 
    319   AppsGridView* grid_view_;
    320   AppListItemView* drag_view_;
    321   gfx::Point drag_view_offset_;
    322   bool has_shortcut_path_;
    323   base::FilePath shortcut_path_;
    324   bool running_;
    325   bool canceled_;
    326 
    327   DISALLOW_COPY_AND_ASSIGN(SynchronousDrag);
    328 };
    329 #endif  // defined(OS_WIN)
    330 
    331 AppsGridView::AppsGridView(AppsGridViewDelegate* delegate)
    332     : model_(NULL),
    333       item_list_(NULL),
    334       delegate_(delegate),
    335       folder_delegate_(NULL),
    336       page_switcher_view_(NULL),
    337       cols_(0),
    338       rows_per_page_(0),
    339       selected_view_(NULL),
    340       drag_view_(NULL),
    341       drag_start_page_(-1),
    342 #if defined(OS_WIN)
    343       use_synchronous_drag_(true),
    344 #endif
    345       drag_pointer_(NONE),
    346       drop_attempt_(DROP_FOR_NONE),
    347       drag_and_drop_host_(NULL),
    348       forward_events_to_drag_and_drop_host_(false),
    349       page_flip_target_(-1),
    350       page_flip_delay_in_ms_(kPageFlipDelayInMs),
    351       bounds_animator_(this),
    352       activated_folder_item_view_(NULL),
    353       dragging_for_reparent_item_(false) {
    354   SetPaintToLayer(true);
    355   // Clip any icons that are outside the grid view's bounds. These icons would
    356   // otherwise be visible to the user when the grid view is off screen.
    357   layer()->SetMasksToBounds(true);
    358   SetFillsBoundsOpaquely(false);
    359 
    360   pagination_model_.SetTransitionDurations(kPageTransitionDurationInMs,
    361                                            kOverscrollPageTransitionDurationMs);
    362 
    363   pagination_model_.AddObserver(this);
    364   page_switcher_view_ = new PageSwitcher(&pagination_model_);
    365   AddChildView(page_switcher_view_);
    366 }
    367 
    368 AppsGridView::~AppsGridView() {
    369   // Coming here |drag_view_| should already be canceled since otherwise the
    370   // drag would disappear after the app list got animated away and closed,
    371   // which would look odd.
    372   DCHECK(!drag_view_);
    373   if (drag_view_)
    374     EndDrag(true);
    375 
    376   if (model_)
    377     model_->RemoveObserver(this);
    378   pagination_model_.RemoveObserver(this);
    379 
    380   if (item_list_)
    381     item_list_->RemoveObserver(this);
    382 
    383   // Make sure |page_switcher_view_| is deleted before |pagination_model_|.
    384   view_model_.Clear();
    385   RemoveAllChildViews(true);
    386 }
    387 
    388 void AppsGridView::SetLayout(int icon_size, int cols, int rows_per_page) {
    389   icon_size_.SetSize(icon_size, icon_size);
    390   cols_ = cols;
    391   rows_per_page_ = rows_per_page;
    392 
    393   SetBorder(views::Border::CreateEmptyBorder(
    394       kTopPadding, kLeftRightPadding, 0, kLeftRightPadding));
    395 }
    396 
    397 void AppsGridView::ResetForShowApps() {
    398   activated_folder_item_view_ = NULL;
    399   ClearDragState();
    400   layer()->SetOpacity(1.0f);
    401   SetVisible(true);
    402   // Set all views to visible in case they weren't made visible again by an
    403   // incomplete animation.
    404   for (int i = 0; i < view_model_.view_size(); ++i) {
    405     view_model_.view_at(i)->SetVisible(true);
    406   }
    407   CHECK_EQ(item_list_->item_count(),
    408            static_cast<size_t>(view_model_.view_size()));
    409 }
    410 
    411 void AppsGridView::SetModel(AppListModel* model) {
    412   if (model_)
    413     model_->RemoveObserver(this);
    414 
    415   model_ = model;
    416   if (model_)
    417     model_->AddObserver(this);
    418 
    419   Update();
    420 }
    421 
    422 void AppsGridView::SetItemList(AppListItemList* item_list) {
    423   if (item_list_)
    424     item_list_->RemoveObserver(this);
    425   item_list_ = item_list;
    426   if (item_list_)
    427     item_list_->AddObserver(this);
    428   Update();
    429 }
    430 
    431 void AppsGridView::SetSelectedView(views::View* view) {
    432   if (IsSelectedView(view) || IsDraggedView(view))
    433     return;
    434 
    435   Index index = GetIndexOfView(view);
    436   if (IsValidIndex(index))
    437     SetSelectedItemByIndex(index);
    438 }
    439 
    440 void AppsGridView::ClearSelectedView(views::View* view) {
    441   if (view && IsSelectedView(view)) {
    442     selected_view_->SchedulePaint();
    443     selected_view_ = NULL;
    444   }
    445 }
    446 
    447 void AppsGridView::ClearAnySelectedView() {
    448   if (selected_view_) {
    449     selected_view_->SchedulePaint();
    450     selected_view_ = NULL;
    451   }
    452 }
    453 
    454 bool AppsGridView::IsSelectedView(const views::View* view) const {
    455   return selected_view_ == view;
    456 }
    457 
    458 void AppsGridView::EnsureViewVisible(const views::View* view) {
    459   if (pagination_model_.has_transition())
    460     return;
    461 
    462   Index index = GetIndexOfView(view);
    463   if (IsValidIndex(index))
    464     pagination_model_.SelectPage(index.page, false);
    465 }
    466 
    467 void AppsGridView::InitiateDrag(AppListItemView* view,
    468                                 Pointer pointer,
    469                                 const ui::LocatedEvent& event) {
    470   DCHECK(view);
    471   if (drag_view_ || pulsing_blocks_model_.view_size())
    472     return;
    473 
    474   drag_view_ = view;
    475   drag_view_init_index_ = GetIndexOfView(drag_view_);
    476   drag_view_offset_ = event.location();
    477   drag_start_page_ = pagination_model_.selected_page();
    478   ExtractDragLocation(event, &drag_start_grid_view_);
    479   drag_view_start_ = gfx::Point(drag_view_->x(), drag_view_->y());
    480 }
    481 
    482 void AppsGridView::StartSettingUpSynchronousDrag() {
    483 #if defined(OS_WIN)
    484   if (!delegate_ || !use_synchronous_drag_)
    485     return;
    486 
    487   // Folders can't be integrated with the OS.
    488   if (IsFolderItem(drag_view_->item()))
    489     return;
    490 
    491   // Favor the drag and drop host over native win32 drag. For the Win8/ash
    492   // launcher we want to have ashes drag and drop over win32's.
    493   if (drag_and_drop_host_)
    494     return;
    495 
    496   // Never create a second synchronous drag if the drag started in a folder.
    497   if (IsDraggingForReparentInRootLevelGridView())
    498     return;
    499 
    500   synchronous_drag_ = new SynchronousDrag(this, drag_view_, drag_view_offset_);
    501   delegate_->GetShortcutPathForApp(drag_view_->item()->id(),
    502                                    base::Bind(&AppsGridView::OnGotShortcutPath,
    503                                               base::Unretained(this),
    504                                               synchronous_drag_));
    505 #endif
    506 }
    507 
    508 bool AppsGridView::RunSynchronousDrag() {
    509 #if defined(OS_WIN)
    510   if (!synchronous_drag_)
    511     return false;
    512 
    513   if (synchronous_drag_->CanRun()) {
    514     if (IsDraggingForReparentInHiddenGridView())
    515       folder_delegate_->SetRootLevelDragViewVisible(false);
    516     synchronous_drag_->Run();
    517     synchronous_drag_ = NULL;
    518     return true;
    519   } else if (!synchronous_drag_->running()) {
    520     // The OS drag is not ready yet. If the root grid has a drag view because
    521     // a reparent has started, ensure it is visible.
    522     if (IsDraggingForReparentInHiddenGridView())
    523       folder_delegate_->SetRootLevelDragViewVisible(true);
    524   }
    525 #endif
    526   return false;
    527 }
    528 
    529 void AppsGridView::CleanUpSynchronousDrag() {
    530 #if defined(OS_WIN)
    531   if (synchronous_drag_)
    532     synchronous_drag_->EndDragExternally();
    533 
    534   synchronous_drag_ = NULL;
    535 #endif
    536 }
    537 
    538 #if defined(OS_WIN)
    539 void AppsGridView::OnGotShortcutPath(
    540     scoped_refptr<SynchronousDrag> synchronous_drag,
    541     const base::FilePath& path) {
    542   // Drag may have ended before we get the shortcut path or a new drag may have
    543   // begun.
    544   if (synchronous_drag_ != synchronous_drag)
    545     return;
    546   // Setting the shortcut path here means the next time we hit UpdateDrag()
    547   // we'll enter the synchronous drag.
    548   // NOTE we don't Run() the drag here because that causes animations not to
    549   // update for some reason.
    550   synchronous_drag_->set_shortcut_path(path);
    551   DCHECK(synchronous_drag_->CanRun());
    552 }
    553 #endif
    554 
    555 bool AppsGridView::UpdateDragFromItem(Pointer pointer,
    556                                       const ui::LocatedEvent& event) {
    557   DCHECK(drag_view_);
    558 
    559   gfx::Point drag_point_in_grid_view;
    560   ExtractDragLocation(event, &drag_point_in_grid_view);
    561   UpdateDrag(pointer, drag_point_in_grid_view);
    562   if (!dragging())
    563     return false;
    564 
    565   // If a drag and drop host is provided, see if the drag operation needs to be
    566   // forwarded.
    567   gfx::Point location_in_screen = drag_point_in_grid_view;
    568   views::View::ConvertPointToScreen(this, &location_in_screen);
    569   DispatchDragEventToDragAndDropHost(location_in_screen);
    570   if (drag_and_drop_host_)
    571     drag_and_drop_host_->UpdateDragIconProxy(location_in_screen);
    572   return true;
    573 }
    574 
    575 void AppsGridView::UpdateDrag(Pointer pointer, const gfx::Point& point) {
    576   if (folder_delegate_)
    577     UpdateDragStateInsideFolder(pointer, point);
    578 
    579   // EndDrag was called before if |drag_view_| is NULL.
    580   if (!drag_view_)
    581     return;
    582 
    583   if (RunSynchronousDrag())
    584     return;
    585 
    586   gfx::Vector2d drag_vector(point - drag_start_grid_view_);
    587   if (!dragging() && ExceededDragThreshold(drag_vector)) {
    588     drag_pointer_ = pointer;
    589     // Move the view to the front so that it appears on top of other views.
    590     ReorderChildView(drag_view_, -1);
    591     bounds_animator_.StopAnimatingView(drag_view_);
    592     // Stopping the animation may have invalidated our drag view due to the
    593     // view hierarchy changing.
    594     if (!drag_view_)
    595       return;
    596 
    597     StartSettingUpSynchronousDrag();
    598     if (!dragging_for_reparent_item_)
    599       StartDragAndDropHostDrag(point);
    600   }
    601 
    602   if (drag_pointer_ != pointer)
    603     return;
    604 
    605   last_drag_point_ = point;
    606   const Index last_drop_target = drop_target_;
    607   DropAttempt last_drop_attempt = drop_attempt_;
    608   CalculateDropTarget(last_drag_point_, false);
    609 
    610   if (IsPointWithinDragBuffer(last_drag_point_))
    611     MaybeStartPageFlipTimer(last_drag_point_);
    612   else
    613     StopPageFlipTimer();
    614 
    615   gfx::Point page_switcher_point(last_drag_point_);
    616   views::View::ConvertPointToTarget(this, page_switcher_view_,
    617                                     &page_switcher_point);
    618   page_switcher_view_->UpdateUIForDragPoint(page_switcher_point);
    619 
    620   if (!EnableFolderDragDropUI()) {
    621     if (last_drop_target != drop_target_)
    622       AnimateToIdealBounds();
    623     drag_view_->SetPosition(drag_view_start_ + drag_vector);
    624     return;
    625   }
    626 
    627   // Update drag with folder UI enabled.
    628   if (last_drop_target != drop_target_ ||
    629       last_drop_attempt != drop_attempt_) {
    630     if (drop_attempt_ == DROP_FOR_REORDER) {
    631       folder_dropping_timer_.Stop();
    632       reorder_timer_.Start(FROM_HERE,
    633           base::TimeDelta::FromMilliseconds(kReorderDelay),
    634           this, &AppsGridView::OnReorderTimer);
    635     } else if (drop_attempt_ == DROP_FOR_FOLDER) {
    636       reorder_timer_.Stop();
    637       folder_dropping_timer_.Start(FROM_HERE,
    638           base::TimeDelta::FromMilliseconds(kFolderDroppingDelay),
    639           this, &AppsGridView::OnFolderDroppingTimer);
    640     }
    641 
    642     // Reset the previous drop target.
    643     SetAsFolderDroppingTarget(last_drop_target, false);
    644   }
    645 
    646   drag_view_->SetPosition(drag_view_start_ + drag_vector);
    647 }
    648 
    649 void AppsGridView::EndDrag(bool cancel) {
    650   // EndDrag was called before if |drag_view_| is NULL.
    651   if (!drag_view_)
    652     return;
    653 
    654   // Coming here a drag and drop was in progress.
    655   bool landed_in_drag_and_drop_host = forward_events_to_drag_and_drop_host_;
    656   if (forward_events_to_drag_and_drop_host_) {
    657     DCHECK(!IsDraggingForReparentInRootLevelGridView());
    658     forward_events_to_drag_and_drop_host_ = false;
    659     drag_and_drop_host_->EndDrag(cancel);
    660     if (IsDraggingForReparentInHiddenGridView()) {
    661       folder_delegate_->DispatchEndDragEventForReparent(
    662           true /* events_forwarded_to_drag_drop_host */,
    663           cancel /* cancel_drag */);
    664     }
    665   } else {
    666     if (IsDraggingForReparentInHiddenGridView()) {
    667       // Forward the EndDrag event to the root level grid view.
    668       folder_delegate_->DispatchEndDragEventForReparent(
    669           false /* events_forwarded_to_drag_drop_host */,
    670           cancel /* cancel_drag */);
    671       EndDragForReparentInHiddenFolderGridView();
    672       return;
    673     }
    674 
    675     if (!cancel && dragging()) {
    676       // Regular drag ending path, ie, not for reparenting.
    677       CalculateDropTarget(last_drag_point_, true);
    678       if (IsValidIndex(drop_target_)) {
    679         if (!EnableFolderDragDropUI()) {
    680             MoveItemInModel(drag_view_, drop_target_);
    681         } else {
    682           if (drop_attempt_ == DROP_FOR_REORDER)
    683             MoveItemInModel(drag_view_, drop_target_);
    684           else if (drop_attempt_ == DROP_FOR_FOLDER)
    685             MoveItemToFolder(drag_view_, drop_target_);
    686         }
    687       }
    688     }
    689   }
    690 
    691   if (drag_and_drop_host_) {
    692     // If we had a drag and drop proxy icon, we delete it and make the real
    693     // item visible again.
    694     drag_and_drop_host_->DestroyDragIconProxy();
    695     if (landed_in_drag_and_drop_host) {
    696       // Move the item directly to the target location, avoiding the "zip back"
    697       // animation if the user was pinning it to the shelf.
    698       int i = drop_target_.slot;
    699       gfx::Rect bounds = view_model_.ideal_bounds(i);
    700       drag_view_->SetBoundsRect(bounds);
    701     }
    702     // Fade in slowly if it landed in the shelf.
    703     SetViewHidden(drag_view_,
    704                   false /* show */,
    705                   !landed_in_drag_and_drop_host /* animate */);
    706   }
    707 
    708   // The drag can be ended after the synchronous drag is created but before it
    709   // is Run().
    710   CleanUpSynchronousDrag();
    711 
    712   SetAsFolderDroppingTarget(drop_target_, false);
    713   ClearDragState();
    714   AnimateToIdealBounds();
    715 
    716   StopPageFlipTimer();
    717 
    718   // If user releases mouse inside a folder's grid view, burst the folder
    719   // container ink bubble.
    720   if (folder_delegate_ && !IsDraggingForReparentInHiddenGridView())
    721     folder_delegate_->UpdateFolderViewBackground(false);
    722 }
    723 
    724 void AppsGridView::StopPageFlipTimer() {
    725   page_flip_timer_.Stop();
    726   page_flip_target_ = -1;
    727 }
    728 
    729 AppListItemView* AppsGridView::GetItemViewAt(int index) const {
    730   DCHECK(index >= 0 && index < view_model_.view_size());
    731   return static_cast<AppListItemView*>(view_model_.view_at(index));
    732 }
    733 
    734 void AppsGridView::SetTopItemViewsVisible(bool visible) {
    735   int top_item_count = std::min(static_cast<int>(kNumFolderTopItems),
    736                                 view_model_.view_size());
    737   for (int i = 0; i < top_item_count; ++i)
    738     GetItemViewAt(i)->SetVisible(visible);
    739 }
    740 
    741 void AppsGridView::ScheduleShowHideAnimation(bool show) {
    742   // Stop any previous animation.
    743   layer()->GetAnimator()->StopAnimating();
    744 
    745   // Set initial state.
    746   SetVisible(true);
    747   layer()->SetOpacity(show ? 0.0f : 1.0f);
    748 
    749   ui::ScopedLayerAnimationSettings animation(layer()->GetAnimator());
    750   animation.AddObserver(this);
    751   animation.SetTweenType(
    752       show ? kFolderFadeInTweenType : kFolderFadeOutTweenType);
    753   animation.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
    754       show ? kFolderTransitionInDurationMs : kFolderTransitionOutDurationMs));
    755 
    756   layer()->SetOpacity(show ? 1.0f : 0.0f);
    757 }
    758 
    759 void AppsGridView::InitiateDragFromReparentItemInRootLevelGridView(
    760     AppListItemView* original_drag_view,
    761     const gfx::Rect& drag_view_rect,
    762     const gfx::Point& drag_point) {
    763   DCHECK(original_drag_view && !drag_view_);
    764   DCHECK(!dragging_for_reparent_item_);
    765 
    766   // Create a new AppListItemView to duplicate the original_drag_view in the
    767   // folder's grid view.
    768   AppListItemView* view = new AppListItemView(this, original_drag_view->item());
    769   AddChildView(view);
    770   drag_view_ = view;
    771   drag_view_->SetPaintToLayer(true);
    772   // Note: For testing purpose, SetFillsBoundsOpaquely can be set to true to
    773   // show the gray background.
    774   drag_view_->SetFillsBoundsOpaquely(false);
    775   drag_view_->SetIconSize(icon_size_);
    776   drag_view_->SetBoundsRect(drag_view_rect);
    777   drag_view_->SetDragUIState();  // Hide the title of the drag_view_.
    778 
    779   // Hide the drag_view_ for drag icon proxy.
    780   SetViewHidden(drag_view_,
    781                 true /* hide */,
    782                 true /* no animate */);
    783 
    784   // Add drag_view_ to the end of the view_model_.
    785   view_model_.Add(drag_view_, view_model_.view_size());
    786 
    787   drag_start_page_ = pagination_model_.selected_page();
    788   drag_start_grid_view_ = drag_point;
    789 
    790   drag_view_start_ = gfx::Point(drag_view_->x(), drag_view_->y());
    791 
    792   // Set the flag in root level grid view.
    793   dragging_for_reparent_item_ = true;
    794 }
    795 
    796 void AppsGridView::UpdateDragFromReparentItem(Pointer pointer,
    797                                               const gfx::Point& drag_point) {
    798   DCHECK(drag_view_);
    799   DCHECK(IsDraggingForReparentInRootLevelGridView());
    800 
    801   UpdateDrag(pointer, drag_point);
    802 }
    803 
    804 bool AppsGridView::IsDraggedView(const views::View* view) const {
    805   return drag_view_ == view;
    806 }
    807 
    808 void AppsGridView::ClearDragState() {
    809   drop_attempt_ = DROP_FOR_NONE;
    810   drag_pointer_ = NONE;
    811   drop_target_ = Index();
    812   drag_start_grid_view_ = gfx::Point();
    813   drag_start_page_ = -1;
    814   drag_view_offset_ = gfx::Point();
    815 
    816   if (drag_view_) {
    817     drag_view_->OnDragEnded();
    818     if (IsDraggingForReparentInRootLevelGridView()) {
    819       const int drag_view_index = view_model_.GetIndexOfView(drag_view_);
    820       CHECK_EQ(view_model_.view_size() - 1, drag_view_index);
    821       DeleteItemViewAtIndex(drag_view_index);
    822     }
    823   }
    824   drag_view_ = NULL;
    825   dragging_for_reparent_item_ = false;
    826 }
    827 
    828 void AppsGridView::SetDragViewVisible(bool visible) {
    829   DCHECK(drag_view_);
    830   SetViewHidden(drag_view_, !visible, true);
    831 }
    832 
    833 void AppsGridView::SetDragAndDropHostOfCurrentAppList(
    834     ApplicationDragAndDropHost* drag_and_drop_host) {
    835   drag_and_drop_host_ = drag_and_drop_host;
    836 }
    837 
    838 void AppsGridView::Prerender(int page_index) {
    839   Layout();
    840   int start = std::max(0, (page_index - kPrerenderPages) * tiles_per_page());
    841   int end = std::min(view_model_.view_size(),
    842                      (page_index + 1 + kPrerenderPages) * tiles_per_page());
    843   for (int i = start; i < end; i++) {
    844     AppListItemView* v = static_cast<AppListItemView*>(view_model_.view_at(i));
    845     v->Prerender();
    846   }
    847 }
    848 
    849 bool AppsGridView::IsAnimatingView(views::View* view) {
    850   return bounds_animator_.IsAnimating(view);
    851 }
    852 
    853 gfx::Size AppsGridView::GetPreferredSize() const {
    854   const gfx::Insets insets(GetInsets());
    855   const gfx::Size tile_size = gfx::Size(kPreferredTileWidth,
    856                                         kPreferredTileHeight);
    857   const int page_switcher_height =
    858       page_switcher_view_->GetPreferredSize().height();
    859   return gfx::Size(
    860       tile_size.width() * cols_ + insets.width(),
    861       tile_size.height() * rows_per_page_ +
    862           page_switcher_height + insets.height());
    863 }
    864 
    865 bool AppsGridView::GetDropFormats(
    866     int* formats,
    867     std::set<OSExchangeData::CustomFormat>* custom_formats) {
    868   // TODO(koz): Only accept a specific drag type for app shortcuts.
    869   *formats = OSExchangeData::FILE_NAME;
    870   return true;
    871 }
    872 
    873 bool AppsGridView::CanDrop(const OSExchangeData& data) {
    874   return true;
    875 }
    876 
    877 int AppsGridView::OnDragUpdated(const ui::DropTargetEvent& event) {
    878   return ui::DragDropTypes::DRAG_MOVE;
    879 }
    880 
    881 void AppsGridView::Layout() {
    882   if (bounds_animator_.IsAnimating())
    883     bounds_animator_.Cancel();
    884 
    885   CalculateIdealBounds();
    886   for (int i = 0; i < view_model_.view_size(); ++i) {
    887     views::View* view = view_model_.view_at(i);
    888     if (view != drag_view_)
    889       view->SetBoundsRect(view_model_.ideal_bounds(i));
    890   }
    891   views::ViewModelUtils::SetViewBoundsToIdealBounds(pulsing_blocks_model_);
    892 
    893   const int page_switcher_height =
    894       page_switcher_view_->GetPreferredSize().height();
    895   gfx::Rect rect(GetContentsBounds());
    896   rect.set_y(rect.bottom() - page_switcher_height);
    897   rect.set_height(page_switcher_height);
    898   page_switcher_view_->SetBoundsRect(rect);
    899 }
    900 
    901 bool AppsGridView::OnKeyPressed(const ui::KeyEvent& event) {
    902   bool handled = false;
    903   if (selected_view_)
    904     handled = selected_view_->OnKeyPressed(event);
    905 
    906   if (!handled) {
    907     const int forward_dir = base::i18n::IsRTL() ? -1 : 1;
    908     switch (event.key_code()) {
    909       case ui::VKEY_LEFT:
    910         MoveSelected(0, -forward_dir, 0);
    911         return true;
    912       case ui::VKEY_RIGHT:
    913         MoveSelected(0, forward_dir, 0);
    914         return true;
    915       case ui::VKEY_UP:
    916         MoveSelected(0, 0, -1);
    917         return true;
    918       case ui::VKEY_DOWN:
    919         MoveSelected(0, 0, 1);
    920         return true;
    921       case ui::VKEY_PRIOR: {
    922         MoveSelected(-1, 0, 0);
    923         return true;
    924       }
    925       case ui::VKEY_NEXT: {
    926         MoveSelected(1, 0, 0);
    927         return true;
    928       }
    929       default:
    930         break;
    931     }
    932   }
    933 
    934   return handled;
    935 }
    936 
    937 bool AppsGridView::OnKeyReleased(const ui::KeyEvent& event) {
    938   bool handled = false;
    939   if (selected_view_)
    940     handled = selected_view_->OnKeyReleased(event);
    941 
    942   return handled;
    943 }
    944 
    945 void AppsGridView::ViewHierarchyChanged(
    946     const ViewHierarchyChangedDetails& details) {
    947   if (!details.is_add && details.parent == this) {
    948     // The view being delete should not have reference in |view_model_|.
    949     CHECK_EQ(-1, view_model_.GetIndexOfView(details.child));
    950 
    951     if (selected_view_ == details.child)
    952       selected_view_ = NULL;
    953     if (activated_folder_item_view_ == details.child)
    954       activated_folder_item_view_ = NULL;
    955 
    956     if (drag_view_ == details.child)
    957       EndDrag(true);
    958 
    959     bounds_animator_.StopAnimatingView(details.child);
    960   }
    961 }
    962 
    963 void AppsGridView::Update() {
    964   DCHECK(!selected_view_ && !drag_view_);
    965   view_model_.Clear();
    966   if (!item_list_ || !item_list_->item_count())
    967     return;
    968   for (size_t i = 0; i < item_list_->item_count(); ++i) {
    969     views::View* view = CreateViewForItemAtIndex(i);
    970     view_model_.Add(view, i);
    971     AddChildView(view);
    972   }
    973   UpdatePaging();
    974   UpdatePulsingBlockViews();
    975   Layout();
    976   SchedulePaint();
    977 }
    978 
    979 void AppsGridView::UpdatePaging() {
    980   int total_page = view_model_.view_size() && tiles_per_page()
    981                        ? (view_model_.view_size() - 1) / tiles_per_page() + 1
    982                        : 0;
    983 
    984   pagination_model_.SetTotalPages(total_page);
    985 }
    986 
    987 void AppsGridView::UpdatePulsingBlockViews() {
    988   const int existing_items = item_list_ ? item_list_->item_count() : 0;
    989   const int available_slots =
    990       tiles_per_page() - existing_items % tiles_per_page();
    991   const int desired = model_->status() == AppListModel::STATUS_SYNCING ?
    992       available_slots : 0;
    993 
    994   if (pulsing_blocks_model_.view_size() == desired)
    995     return;
    996 
    997   while (pulsing_blocks_model_.view_size() > desired) {
    998     views::View* view = pulsing_blocks_model_.view_at(0);
    999     pulsing_blocks_model_.Remove(0);
   1000     delete view;
   1001   }
   1002 
   1003   while (pulsing_blocks_model_.view_size() < desired) {
   1004     views::View* view = new PulsingBlockView(
   1005         gfx::Size(kPreferredTileWidth, kPreferredTileHeight), true);
   1006     pulsing_blocks_model_.Add(view, 0);
   1007     AddChildView(view);
   1008   }
   1009 }
   1010 
   1011 views::View* AppsGridView::CreateViewForItemAtIndex(size_t index) {
   1012   // The drag_view_ might be pending for deletion, therefore view_model_
   1013   // may have one more item than item_list_.
   1014   DCHECK_LE(index, item_list_->item_count());
   1015   AppListItemView* view = new AppListItemView(this,
   1016                                               item_list_->item_at(index));
   1017   view->SetIconSize(icon_size_);
   1018   view->SetPaintToLayer(true);
   1019   view->SetFillsBoundsOpaquely(false);
   1020   return view;
   1021 }
   1022 
   1023 AppsGridView::Index AppsGridView::GetIndexFromModelIndex(
   1024     int model_index) const {
   1025   return Index(model_index / tiles_per_page(), model_index % tiles_per_page());
   1026 }
   1027 
   1028 int AppsGridView::GetModelIndexFromIndex(const Index& index) const {
   1029   return index.page * tiles_per_page() + index.slot;
   1030 }
   1031 
   1032 void AppsGridView::SetSelectedItemByIndex(const Index& index) {
   1033   if (GetIndexOfView(selected_view_) == index)
   1034     return;
   1035 
   1036   views::View* new_selection = GetViewAtIndex(index);
   1037   if (!new_selection)
   1038     return;  // Keep current selection.
   1039 
   1040   if (selected_view_)
   1041     selected_view_->SchedulePaint();
   1042 
   1043   EnsureViewVisible(new_selection);
   1044   selected_view_ = new_selection;
   1045   selected_view_->SchedulePaint();
   1046   selected_view_->NotifyAccessibilityEvent(
   1047       ui::AX_EVENT_FOCUS, true);
   1048 }
   1049 
   1050 bool AppsGridView::IsValidIndex(const Index& index) const {
   1051   return index.page >= 0 && index.page < pagination_model_.total_pages() &&
   1052          index.slot >= 0 && index.slot < tiles_per_page() &&
   1053          GetModelIndexFromIndex(index) < view_model_.view_size();
   1054 }
   1055 
   1056 AppsGridView::Index AppsGridView::GetIndexOfView(
   1057     const views::View* view) const {
   1058   const int model_index = view_model_.GetIndexOfView(view);
   1059   if (model_index == -1)
   1060     return Index();
   1061 
   1062   return GetIndexFromModelIndex(model_index);
   1063 }
   1064 
   1065 views::View* AppsGridView::GetViewAtIndex(const Index& index) const {
   1066   if (!IsValidIndex(index))
   1067     return NULL;
   1068 
   1069   const int model_index = GetModelIndexFromIndex(index);
   1070   return view_model_.view_at(model_index);
   1071 }
   1072 
   1073 void AppsGridView::MoveSelected(int page_delta,
   1074                                 int slot_x_delta,
   1075                                 int slot_y_delta) {
   1076   if (!selected_view_)
   1077     return SetSelectedItemByIndex(Index(pagination_model_.selected_page(), 0));
   1078 
   1079   const Index& selected = GetIndexOfView(selected_view_);
   1080   int target_slot = selected.slot + slot_x_delta + slot_y_delta * cols_;
   1081 
   1082   if (selected.slot % cols_ == 0 && slot_x_delta == -1) {
   1083     if (selected.page > 0) {
   1084       page_delta = -1;
   1085       target_slot = selected.slot + cols_ - 1;
   1086     } else {
   1087       target_slot = selected.slot;
   1088     }
   1089   }
   1090 
   1091   if (selected.slot % cols_ == cols_ - 1 && slot_x_delta == 1) {
   1092     if (selected.page < pagination_model_.total_pages() - 1) {
   1093       page_delta = 1;
   1094       target_slot = selected.slot - cols_ + 1;
   1095     } else {
   1096       target_slot = selected.slot;
   1097     }
   1098   }
   1099 
   1100   // Clamp the target slot to the last item if we are moving to the last page
   1101   // but our target slot is past the end of the item list.
   1102   if (page_delta &&
   1103       selected.page + page_delta == pagination_model_.total_pages() - 1) {
   1104     int last_item_slot = (view_model_.view_size() - 1) % tiles_per_page();
   1105     if (last_item_slot < target_slot) {
   1106       target_slot = last_item_slot;
   1107     }
   1108   }
   1109 
   1110   int target_page = std::min(pagination_model_.total_pages() - 1,
   1111                              std::max(selected.page + page_delta, 0));
   1112   SetSelectedItemByIndex(Index(target_page, target_slot));
   1113 }
   1114 
   1115 void AppsGridView::CalculateIdealBounds() {
   1116   gfx::Rect rect(GetContentsBounds());
   1117   if (rect.IsEmpty())
   1118     return;
   1119 
   1120   gfx::Size tile_size(kPreferredTileWidth, kPreferredTileHeight);
   1121 
   1122   gfx::Rect grid_rect(gfx::Size(tile_size.width() * cols_,
   1123                                 tile_size.height() * rows_per_page_));
   1124   grid_rect.Intersect(rect);
   1125 
   1126   // Page width including padding pixels. A tile.x + page_width means the same
   1127   // tile slot in the next page.
   1128   const int page_width = grid_rect.width() + kPagePadding;
   1129 
   1130   // If there is a transition, calculates offset for current and target page.
   1131   const int current_page = pagination_model_.selected_page();
   1132   const PaginationModel::Transition& transition =
   1133       pagination_model_.transition();
   1134   const bool is_valid = pagination_model_.is_valid_page(transition.target_page);
   1135 
   1136   // Transition to right means negative offset.
   1137   const int dir = transition.target_page > current_page ? -1 : 1;
   1138   const int transition_offset = is_valid ?
   1139       transition.progress * page_width * dir : 0;
   1140 
   1141   const int total_views =
   1142       view_model_.view_size() + pulsing_blocks_model_.view_size();
   1143   int slot_index = 0;
   1144   for (int i = 0; i < total_views; ++i) {
   1145     if (i < view_model_.view_size() && view_model_.view_at(i) == drag_view_) {
   1146       if (EnableFolderDragDropUI() && drop_attempt_ == DROP_FOR_FOLDER)
   1147         ++slot_index;
   1148       continue;
   1149     }
   1150 
   1151     Index view_index = GetIndexFromModelIndex(slot_index);
   1152 
   1153     if (drop_target_ == view_index) {
   1154       if (EnableFolderDragDropUI() && drop_attempt_ == DROP_FOR_FOLDER) {
   1155         view_index = GetIndexFromModelIndex(slot_index);
   1156       } else if (!EnableFolderDragDropUI() ||
   1157                  drop_attempt_ == DROP_FOR_REORDER) {
   1158         ++slot_index;
   1159         view_index = GetIndexFromModelIndex(slot_index);
   1160       }
   1161     }
   1162 
   1163     // Decides an x_offset for current item.
   1164     int x_offset = 0;
   1165     if (view_index.page < current_page)
   1166       x_offset = -page_width;
   1167     else if (view_index.page > current_page)
   1168       x_offset = page_width;
   1169 
   1170     if (is_valid) {
   1171       if (view_index.page == current_page ||
   1172           view_index.page == transition.target_page) {
   1173         x_offset += transition_offset;
   1174       }
   1175     }
   1176 
   1177     const int row = view_index.slot / cols_;
   1178     const int col = view_index.slot % cols_;
   1179     gfx::Rect tile_slot(
   1180         gfx::Point(grid_rect.x() + col * tile_size.width() + x_offset,
   1181                    grid_rect.y() + row * tile_size.height()),
   1182         tile_size);
   1183     if (i < view_model_.view_size()) {
   1184       view_model_.set_ideal_bounds(i, tile_slot);
   1185     } else {
   1186       pulsing_blocks_model_.set_ideal_bounds(i - view_model_.view_size(),
   1187                                              tile_slot);
   1188     }
   1189 
   1190     ++slot_index;
   1191   }
   1192 }
   1193 
   1194 void AppsGridView::AnimateToIdealBounds() {
   1195   const gfx::Rect visible_bounds(GetVisibleBounds());
   1196 
   1197   CalculateIdealBounds();
   1198   for (int i = 0; i < view_model_.view_size(); ++i) {
   1199     views::View* view = view_model_.view_at(i);
   1200     if (view == drag_view_)
   1201       continue;
   1202 
   1203     const gfx::Rect& target = view_model_.ideal_bounds(i);
   1204     if (bounds_animator_.GetTargetBounds(view) == target)
   1205       continue;
   1206 
   1207     const gfx::Rect& current = view->bounds();
   1208     const bool current_visible = visible_bounds.Intersects(current);
   1209     const bool target_visible = visible_bounds.Intersects(target);
   1210     const bool visible = current_visible || target_visible;
   1211 
   1212     const int y_diff = target.y() - current.y();
   1213     if (visible && y_diff && y_diff % kPreferredTileHeight == 0) {
   1214       AnimationBetweenRows(view,
   1215                            current_visible,
   1216                            current,
   1217                            target_visible,
   1218                            target);
   1219     } else if (visible || bounds_animator_.IsAnimating(view)) {
   1220       bounds_animator_.AnimateViewTo(view, target);
   1221       bounds_animator_.SetAnimationDelegate(
   1222           view,
   1223           scoped_ptr<gfx::AnimationDelegate>(
   1224               new ItemMoveAnimationDelegate(view)));
   1225     } else {
   1226       view->SetBoundsRect(target);
   1227     }
   1228   }
   1229 }
   1230 
   1231 void AppsGridView::AnimationBetweenRows(views::View* view,
   1232                                         bool animate_current,
   1233                                         const gfx::Rect& current,
   1234                                         bool animate_target,
   1235                                         const gfx::Rect& target) {
   1236   // Determine page of |current| and |target|. -1 means in the left invisible
   1237   // page, 0 is the center visible page and 1 means in the right invisible page.
   1238   const int current_page = current.x() < 0 ? -1 :
   1239       current.x() >= width() ? 1 : 0;
   1240   const int target_page = target.x() < 0 ? -1 :
   1241       target.x() >= width() ? 1 : 0;
   1242 
   1243   const int dir = current_page < target_page ||
   1244       (current_page == target_page && current.y() < target.y()) ? 1 : -1;
   1245 
   1246   scoped_ptr<ui::Layer> layer;
   1247   if (animate_current) {
   1248     layer = view->RecreateLayer();
   1249     layer->SuppressPaint();
   1250 
   1251     view->SetFillsBoundsOpaquely(false);
   1252     view->layer()->SetOpacity(0.f);
   1253   }
   1254 
   1255   gfx::Rect current_out(current);
   1256   current_out.Offset(dir * kPreferredTileWidth, 0);
   1257 
   1258   gfx::Rect target_in(target);
   1259   if (animate_target)
   1260     target_in.Offset(-dir * kPreferredTileWidth, 0);
   1261   view->SetBoundsRect(target_in);
   1262   bounds_animator_.AnimateViewTo(view, target);
   1263 
   1264   bounds_animator_.SetAnimationDelegate(
   1265       view,
   1266       scoped_ptr<gfx::AnimationDelegate>(
   1267           new RowMoveAnimationDelegate(view, layer.release(), current_out)));
   1268 }
   1269 
   1270 void AppsGridView::ExtractDragLocation(const ui::LocatedEvent& event,
   1271                                        gfx::Point* drag_point) {
   1272 #if defined(USE_AURA) && !defined(OS_WIN)
   1273   // Use root location of |event| instead of location in |drag_view_|'s
   1274   // coordinates because |drag_view_| has a scale transform and location
   1275   // could have integer round error and causes jitter.
   1276   *drag_point = event.root_location();
   1277 
   1278   // GetWidget() could be NULL for tests.
   1279   if (GetWidget()) {
   1280     aura::Window::ConvertPointToTarget(
   1281         GetWidget()->GetNativeWindow()->GetRootWindow(),
   1282         GetWidget()->GetNativeWindow(),
   1283         drag_point);
   1284   }
   1285 
   1286   views::View::ConvertPointFromWidget(this, drag_point);
   1287 #else
   1288   // For non-aura, root location is not clearly defined but |drag_view_| does
   1289   // not have the scale transform. So no round error would be introduced and
   1290   // it's okay to use View::ConvertPointToTarget.
   1291   *drag_point = event.location();
   1292   views::View::ConvertPointToTarget(drag_view_, this, drag_point);
   1293 #endif
   1294 }
   1295 
   1296 void AppsGridView::CalculateDropTarget(const gfx::Point& drag_point,
   1297                                        bool use_page_button_hovering) {
   1298   if (EnableFolderDragDropUI()) {
   1299     CalculateDropTargetWithFolderEnabled(drag_point, use_page_button_hovering);
   1300     return;
   1301   }
   1302 
   1303   int current_page = pagination_model_.selected_page();
   1304   gfx::Point point(drag_point);
   1305   if (!IsPointWithinDragBuffer(drag_point)) {
   1306     point = drag_start_grid_view_;
   1307     current_page = drag_start_page_;
   1308   }
   1309 
   1310   if (use_page_button_hovering &&
   1311       page_switcher_view_->bounds().Contains(point)) {
   1312     gfx::Point page_switcher_point(point);
   1313     views::View::ConvertPointToTarget(this, page_switcher_view_,
   1314                                       &page_switcher_point);
   1315     int page = page_switcher_view_->GetPageForPoint(page_switcher_point);
   1316     if (pagination_model_.is_valid_page(page)) {
   1317       drop_target_.page = page;
   1318       drop_target_.slot = tiles_per_page() - 1;
   1319     }
   1320   } else {
   1321     gfx::Rect bounds(GetContentsBounds());
   1322     const int drop_row = (point.y() - bounds.y()) / kPreferredTileHeight;
   1323     const int drop_col = std::min(cols_ - 1,
   1324         (point.x() - bounds.x()) / kPreferredTileWidth);
   1325 
   1326     drop_target_.page = current_page;
   1327     drop_target_.slot = std::max(0, std::min(
   1328         tiles_per_page() - 1,
   1329         drop_row * cols_ + drop_col));
   1330   }
   1331 
   1332   // Limits to the last possible slot on last page.
   1333   if (drop_target_.page == pagination_model_.total_pages() - 1) {
   1334     drop_target_.slot = std::min(
   1335         (view_model_.view_size() - 1) % tiles_per_page(),
   1336         drop_target_.slot);
   1337   }
   1338 }
   1339 
   1340 
   1341 void AppsGridView::CalculateDropTargetWithFolderEnabled(
   1342     const gfx::Point& drag_point,
   1343     bool use_page_button_hovering) {
   1344   gfx::Point point(drag_point);
   1345   if (!IsPointWithinDragBuffer(drag_point)) {
   1346     point = drag_start_grid_view_;
   1347   }
   1348 
   1349   if (use_page_button_hovering &&
   1350       page_switcher_view_->bounds().Contains(point)) {
   1351     gfx::Point page_switcher_point(point);
   1352     views::View::ConvertPointToTarget(this, page_switcher_view_,
   1353                                       &page_switcher_point);
   1354     int page = page_switcher_view_->GetPageForPoint(page_switcher_point);
   1355     if (pagination_model_.is_valid_page(page))
   1356       drop_attempt_ = DROP_FOR_NONE;
   1357   } else {
   1358     DCHECK(drag_view_);
   1359     // Try to find the nearest target for folder dropping or re-ordering.
   1360     drop_target_ = GetNearestTileForDragView();
   1361   }
   1362 }
   1363 
   1364 void AppsGridView::OnReorderTimer() {
   1365   if (drop_attempt_ == DROP_FOR_REORDER)
   1366     AnimateToIdealBounds();
   1367 }
   1368 
   1369 void AppsGridView::OnFolderItemReparentTimer() {
   1370   DCHECK(folder_delegate_);
   1371   if (drag_out_of_folder_container_ && drag_view_) {
   1372     folder_delegate_->ReparentItem(drag_view_, last_drag_point_);
   1373 
   1374     // Set the flag in the folder's grid view.
   1375     dragging_for_reparent_item_ = true;
   1376 
   1377     // Do not observe any data change since it is going to be hidden.
   1378     item_list_->RemoveObserver(this);
   1379     item_list_ = NULL;
   1380   }
   1381 }
   1382 
   1383 void AppsGridView::OnFolderDroppingTimer() {
   1384   if (drop_attempt_ == DROP_FOR_FOLDER)
   1385     SetAsFolderDroppingTarget(drop_target_, true);
   1386 }
   1387 
   1388 void AppsGridView::UpdateDragStateInsideFolder(Pointer pointer,
   1389                                                const gfx::Point& drag_point) {
   1390   if (IsUnderOEMFolder())
   1391     return;
   1392 
   1393   if (IsDraggingForReparentInHiddenGridView()) {
   1394     // Dispatch drag event to root level grid view for re-parenting folder
   1395     // folder item purpose.
   1396     DispatchDragEventForReparent(pointer, drag_point);
   1397     return;
   1398   }
   1399 
   1400   // Regular drag and drop in a folder's grid view.
   1401   folder_delegate_->UpdateFolderViewBackground(true);
   1402 
   1403   // Calculate if the drag_view_ is dragged out of the folder's container
   1404   // ink bubble.
   1405   gfx::Rect bounds_to_folder_view = ConvertRectToParent(drag_view_->bounds());
   1406   gfx::Point pt = bounds_to_folder_view.CenterPoint();
   1407   bool is_item_dragged_out_of_folder =
   1408       folder_delegate_->IsPointOutsideOfFolderBoundary(pt);
   1409   if (is_item_dragged_out_of_folder) {
   1410     if (!drag_out_of_folder_container_) {
   1411       folder_item_reparent_timer_.Start(
   1412           FROM_HERE,
   1413           base::TimeDelta::FromMilliseconds(kFolderItemReparentDelay),
   1414           this,
   1415           &AppsGridView::OnFolderItemReparentTimer);
   1416       drag_out_of_folder_container_ = true;
   1417     }
   1418   } else {
   1419     folder_item_reparent_timer_.Stop();
   1420     drag_out_of_folder_container_ = false;
   1421   }
   1422 }
   1423 
   1424 bool AppsGridView::IsDraggingForReparentInRootLevelGridView() const {
   1425   return (!folder_delegate_ && dragging_for_reparent_item_);
   1426 }
   1427 
   1428 bool AppsGridView::IsDraggingForReparentInHiddenGridView() const {
   1429   return (folder_delegate_ && dragging_for_reparent_item_);
   1430 }
   1431 
   1432 gfx::Rect AppsGridView::GetTargetIconRectInFolder(
   1433     AppListItemView* drag_item_view,
   1434     AppListItemView* folder_item_view) {
   1435   gfx::Rect view_ideal_bounds = view_model_.ideal_bounds(
   1436       view_model_.GetIndexOfView(folder_item_view));
   1437   gfx::Rect icon_ideal_bounds =
   1438       folder_item_view->GetIconBoundsForTargetViewBounds(view_ideal_bounds);
   1439   AppListFolderItem* folder_item =
   1440       static_cast<AppListFolderItem*>(folder_item_view->item());
   1441   return folder_item->GetTargetIconRectInFolderForItem(
   1442       drag_item_view->item(), icon_ideal_bounds);
   1443 }
   1444 
   1445 bool AppsGridView::IsUnderOEMFolder() {
   1446   if (!folder_delegate_)
   1447     return false;
   1448 
   1449   return folder_delegate_->IsOEMFolder();
   1450 }
   1451 
   1452 void AppsGridView::DispatchDragEventForReparent(Pointer pointer,
   1453                                                 const gfx::Point& drag_point) {
   1454   folder_delegate_->DispatchDragEventForReparent(pointer, drag_point);
   1455 }
   1456 
   1457 void AppsGridView::EndDragFromReparentItemInRootLevel(
   1458     bool events_forwarded_to_drag_drop_host,
   1459     bool cancel_drag) {
   1460   // EndDrag was called before if |drag_view_| is NULL.
   1461   if (!drag_view_)
   1462     return;
   1463 
   1464   DCHECK(IsDraggingForReparentInRootLevelGridView());
   1465   bool cancel_reparent = cancel_drag || drop_attempt_ == DROP_FOR_NONE;
   1466   if (!events_forwarded_to_drag_drop_host && !cancel_reparent) {
   1467     CalculateDropTarget(last_drag_point_, true);
   1468     if (IsValidIndex(drop_target_)) {
   1469       if (drop_attempt_ == DROP_FOR_REORDER) {
   1470         ReparentItemForReorder(drag_view_, drop_target_);
   1471       } else if (drop_attempt_ == DROP_FOR_FOLDER) {
   1472         ReparentItemToAnotherFolder(drag_view_, drop_target_);
   1473       }
   1474     }
   1475     SetViewHidden(drag_view_, false /* show */, true /* no animate */);
   1476   }
   1477 
   1478   // The drag can be ended after the synchronous drag is created but before it
   1479   // is Run().
   1480   CleanUpSynchronousDrag();
   1481 
   1482   SetAsFolderDroppingTarget(drop_target_, false);
   1483   if (cancel_reparent) {
   1484     CancelFolderItemReparent(drag_view_);
   1485   } else {
   1486     // By setting |drag_view_| to NULL here, we prevent ClearDragState() from
   1487     // cleaning up the newly created AppListItemView, effectively claiming
   1488     // ownership of the newly created drag view.
   1489     drag_view_->OnDragEnded();
   1490     drag_view_ = NULL;
   1491   }
   1492   ClearDragState();
   1493   AnimateToIdealBounds();
   1494 
   1495   StopPageFlipTimer();
   1496 }
   1497 
   1498 void AppsGridView::EndDragForReparentInHiddenFolderGridView() {
   1499   if (drag_and_drop_host_) {
   1500     // If we had a drag and drop proxy icon, we delete it and make the real
   1501     // item visible again.
   1502     drag_and_drop_host_->DestroyDragIconProxy();
   1503   }
   1504 
   1505   // The drag can be ended after the synchronous drag is created but before it
   1506   // is Run().
   1507   CleanUpSynchronousDrag();
   1508 
   1509   SetAsFolderDroppingTarget(drop_target_, false);
   1510   ClearDragState();
   1511 }
   1512 
   1513 void AppsGridView::OnFolderItemRemoved() {
   1514   DCHECK(folder_delegate_);
   1515   item_list_ = NULL;
   1516 }
   1517 
   1518 void AppsGridView::StartDragAndDropHostDrag(const gfx::Point& grid_location) {
   1519   // When a drag and drop host is given, the item can be dragged out of the app
   1520   // list window. In that case a proxy widget needs to be used.
   1521   // Note: This code has very likely to be changed for Windows (non metro mode)
   1522   // when a |drag_and_drop_host_| gets implemented.
   1523   if (!drag_view_ || !drag_and_drop_host_)
   1524     return;
   1525 
   1526   gfx::Point screen_location = grid_location;
   1527   views::View::ConvertPointToScreen(this, &screen_location);
   1528 
   1529   // Determine the mouse offset to the center of the icon so that the drag and
   1530   // drop host follows this layer.
   1531   gfx::Vector2d delta = drag_view_offset_ -
   1532                         drag_view_->GetLocalBounds().CenterPoint();
   1533   delta.set_y(delta.y() + drag_view_->title()->size().height() / 2);
   1534 
   1535   // We have to hide the original item since the drag and drop host will do
   1536   // the OS dependent code to "lift off the dragged item".
   1537   DCHECK(!IsDraggingForReparentInRootLevelGridView());
   1538   drag_and_drop_host_->CreateDragIconProxy(screen_location,
   1539                                            drag_view_->item()->icon(),
   1540                                            drag_view_,
   1541                                            delta,
   1542                                            kDragAndDropProxyScale);
   1543   SetViewHidden(drag_view_,
   1544            true /* hide */,
   1545            true /* no animation */);
   1546 }
   1547 
   1548 void AppsGridView::DispatchDragEventToDragAndDropHost(
   1549     const gfx::Point& location_in_screen_coordinates) {
   1550   if (!drag_view_ || !drag_and_drop_host_)
   1551     return;
   1552 
   1553   if (GetLocalBounds().Contains(last_drag_point_)) {
   1554     // The event was issued inside the app menu and we should get all events.
   1555     if (forward_events_to_drag_and_drop_host_) {
   1556       // The DnD host was previously called and needs to be informed that the
   1557       // session returns to the owner.
   1558       forward_events_to_drag_and_drop_host_ = false;
   1559       drag_and_drop_host_->EndDrag(true);
   1560     }
   1561   } else {
   1562     if (IsFolderItem(drag_view_->item()))
   1563       return;
   1564 
   1565     // The event happened outside our app menu and we might need to dispatch.
   1566     if (forward_events_to_drag_and_drop_host_) {
   1567       // Dispatch since we have already started.
   1568       if (!drag_and_drop_host_->Drag(location_in_screen_coordinates)) {
   1569         // The host is not active any longer and we cancel the operation.
   1570         forward_events_to_drag_and_drop_host_ = false;
   1571         drag_and_drop_host_->EndDrag(true);
   1572       }
   1573     } else {
   1574       if (drag_and_drop_host_->StartDrag(drag_view_->item()->id(),
   1575                                          location_in_screen_coordinates)) {
   1576         // From now on we forward the drag events.
   1577         forward_events_to_drag_and_drop_host_ = true;
   1578         // Any flip operations are stopped.
   1579         StopPageFlipTimer();
   1580       }
   1581     }
   1582   }
   1583 }
   1584 
   1585 void AppsGridView::MaybeStartPageFlipTimer(const gfx::Point& drag_point) {
   1586   if (!IsPointWithinDragBuffer(drag_point))
   1587     StopPageFlipTimer();
   1588   int new_page_flip_target = -1;
   1589 
   1590   if (page_switcher_view_->bounds().Contains(drag_point)) {
   1591     gfx::Point page_switcher_point(drag_point);
   1592     views::View::ConvertPointToTarget(this, page_switcher_view_,
   1593                                       &page_switcher_point);
   1594     new_page_flip_target =
   1595         page_switcher_view_->GetPageForPoint(page_switcher_point);
   1596   }
   1597 
   1598   // TODO(xiyuan): Fix this for RTL.
   1599   if (new_page_flip_target == -1 && drag_point.x() < kPageFlipZoneSize)
   1600     new_page_flip_target = pagination_model_.selected_page() - 1;
   1601 
   1602   if (new_page_flip_target == -1 &&
   1603       drag_point.x() > width() - kPageFlipZoneSize) {
   1604     new_page_flip_target = pagination_model_.selected_page() + 1;
   1605   }
   1606 
   1607   if (new_page_flip_target == page_flip_target_)
   1608     return;
   1609 
   1610   StopPageFlipTimer();
   1611   if (pagination_model_.is_valid_page(new_page_flip_target)) {
   1612     page_flip_target_ = new_page_flip_target;
   1613 
   1614     if (page_flip_target_ != pagination_model_.selected_page()) {
   1615       page_flip_timer_.Start(FROM_HERE,
   1616           base::TimeDelta::FromMilliseconds(page_flip_delay_in_ms_),
   1617           this, &AppsGridView::OnPageFlipTimer);
   1618     }
   1619   }
   1620 }
   1621 
   1622 void AppsGridView::OnPageFlipTimer() {
   1623   DCHECK(pagination_model_.is_valid_page(page_flip_target_));
   1624   pagination_model_.SelectPage(page_flip_target_, true);
   1625 }
   1626 
   1627 void AppsGridView::MoveItemInModel(views::View* item_view,
   1628                                    const Index& target) {
   1629   int current_model_index = view_model_.GetIndexOfView(item_view);
   1630   DCHECK_GE(current_model_index, 0);
   1631 
   1632   int target_model_index = GetModelIndexFromIndex(target);
   1633   if (target_model_index == current_model_index)
   1634     return;
   1635 
   1636   item_list_->RemoveObserver(this);
   1637   item_list_->MoveItem(current_model_index, target_model_index);
   1638   view_model_.Move(current_model_index, target_model_index);
   1639   item_list_->AddObserver(this);
   1640 
   1641   if (pagination_model_.selected_page() != target.page)
   1642     pagination_model_.SelectPage(target.page, false);
   1643 }
   1644 
   1645 void AppsGridView::MoveItemToFolder(views::View* item_view,
   1646                                     const Index& target) {
   1647   const std::string& source_item_id =
   1648       static_cast<AppListItemView*>(item_view)->item()->id();
   1649   AppListItemView* target_view =
   1650       static_cast<AppListItemView*>(GetViewAtSlotOnCurrentPage(target.slot));
   1651   const std::string&  target_view_item_id = target_view->item()->id();
   1652 
   1653   // Make change to data model.
   1654   item_list_->RemoveObserver(this);
   1655   std::string folder_item_id =
   1656       model_->MergeItems(target_view_item_id, source_item_id);
   1657   item_list_->AddObserver(this);
   1658   if (folder_item_id.empty()) {
   1659     LOG(ERROR) << "Unable to merge into item id: " << target_view_item_id;
   1660     return;
   1661   }
   1662   if (folder_item_id != target_view_item_id) {
   1663     // New folder was created, change the view model to replace the old target
   1664     // view with the new folder item view.
   1665     size_t folder_item_index;
   1666     if (item_list_->FindItemIndex(folder_item_id, &folder_item_index)) {
   1667       int target_view_index = view_model_.GetIndexOfView(target_view);
   1668       gfx::Rect target_view_bounds = target_view->bounds();
   1669       DeleteItemViewAtIndex(target_view_index);
   1670       views::View* target_folder_view =
   1671           CreateViewForItemAtIndex(folder_item_index);
   1672       target_folder_view->SetBoundsRect(target_view_bounds);
   1673       view_model_.Add(target_folder_view, target_view_index);
   1674       AddChildView(target_folder_view);
   1675     } else {
   1676       LOG(ERROR) << "Folder no longer in item_list: " << folder_item_id;
   1677     }
   1678   }
   1679 
   1680   // Fade out the drag_view_ and delete it when animation ends.
   1681   int drag_view_index = view_model_.GetIndexOfView(drag_view_);
   1682   view_model_.Remove(drag_view_index);
   1683   bounds_animator_.AnimateViewTo(drag_view_, drag_view_->bounds());
   1684   bounds_animator_.SetAnimationDelegate(
   1685       drag_view_,
   1686       scoped_ptr<gfx::AnimationDelegate>(
   1687           new ItemRemoveAnimationDelegate(drag_view_)));
   1688   UpdatePaging();
   1689 }
   1690 
   1691 void AppsGridView::ReparentItemForReorder(views::View* item_view,
   1692                                           const Index& target) {
   1693   item_list_->RemoveObserver(this);
   1694   model_->RemoveObserver(this);
   1695 
   1696   AppListItem* reparent_item = static_cast<AppListItemView*>(item_view)->item();
   1697   DCHECK(reparent_item->IsInFolder());
   1698   const std::string source_folder_id = reparent_item->folder_id();
   1699   AppListFolderItem* source_folder =
   1700       static_cast<AppListFolderItem*>(item_list_->FindItem(source_folder_id));
   1701 
   1702   int target_model_index = GetModelIndexFromIndex(target);
   1703 
   1704   // Remove the source folder view if there is only 1 item in it, since the
   1705   // source folder will be deleted after its only child item removed from it.
   1706   if (source_folder->ChildItemCount() == 1u) {
   1707     const int deleted_folder_index =
   1708         view_model_.GetIndexOfView(activated_folder_item_view());
   1709     DeleteItemViewAtIndex(deleted_folder_index);
   1710 
   1711     // Adjust |target_model_index| if it is beyond the deleted folder index.
   1712     if (target_model_index > deleted_folder_index)
   1713       --target_model_index;
   1714   }
   1715 
   1716   // Move the item from its parent folder to top level item list.
   1717   // Must move to target_model_index, the location we expect the target item
   1718   // to be, not the item location we want to insert before.
   1719   int current_model_index = view_model_.GetIndexOfView(item_view);
   1720   syncer::StringOrdinal target_position;
   1721   if (target_model_index < static_cast<int>(item_list_->item_count()))
   1722     target_position = item_list_->item_at(target_model_index)->position();
   1723   model_->MoveItemToFolderAt(reparent_item, "", target_position);
   1724   view_model_.Move(current_model_index, target_model_index);
   1725 
   1726   RemoveLastItemFromReparentItemFolderIfNecessary(source_folder_id);
   1727 
   1728   item_list_->AddObserver(this);
   1729   model_->AddObserver(this);
   1730   UpdatePaging();
   1731 }
   1732 
   1733 void AppsGridView::ReparentItemToAnotherFolder(views::View* item_view,
   1734                                                const Index& target) {
   1735   DCHECK(IsDraggingForReparentInRootLevelGridView());
   1736 
   1737   AppListItemView* target_view =
   1738       static_cast<AppListItemView*>(GetViewAtSlotOnCurrentPage(target.slot));
   1739   if (!target_view)
   1740     return;
   1741 
   1742   // Make change to data model.
   1743   item_list_->RemoveObserver(this);
   1744 
   1745   AppListItem* reparent_item = static_cast<AppListItemView*>(item_view)->item();
   1746   DCHECK(reparent_item->IsInFolder());
   1747   const std::string source_folder_id = reparent_item->folder_id();
   1748   AppListFolderItem* source_folder =
   1749       static_cast<AppListFolderItem*>(item_list_->FindItem(source_folder_id));
   1750 
   1751   // Remove the source folder view if there is only 1 item in it, since the
   1752   // source folder will be deleted after its only child item merged into the
   1753   // target item.
   1754   if (source_folder->ChildItemCount() == 1u)
   1755     DeleteItemViewAtIndex(
   1756         view_model_.GetIndexOfView(activated_folder_item_view()));
   1757 
   1758   AppListItem* target_item = target_view->item();
   1759 
   1760   // Move item to the target folder.
   1761   std::string target_id_after_merge =
   1762       model_->MergeItems(target_item->id(), reparent_item->id());
   1763   if (target_id_after_merge.empty()) {
   1764     LOG(ERROR) << "Unable to reparent to item id: " << target_item->id();
   1765     item_list_->AddObserver(this);
   1766     return;
   1767   }
   1768 
   1769   if (target_id_after_merge != target_item->id()) {
   1770     // New folder was created, change the view model to replace the old target
   1771     // view with the new folder item view.
   1772     const std::string& new_folder_id = reparent_item->folder_id();
   1773     size_t new_folder_index;
   1774     if (item_list_->FindItemIndex(new_folder_id, &new_folder_index)) {
   1775       int target_view_index = view_model_.GetIndexOfView(target_view);
   1776       DeleteItemViewAtIndex(target_view_index);
   1777       views::View* new_folder_view =
   1778           CreateViewForItemAtIndex(new_folder_index);
   1779       view_model_.Add(new_folder_view, target_view_index);
   1780       AddChildView(new_folder_view);
   1781     } else {
   1782       LOG(ERROR) << "Folder no longer in item_list: " << new_folder_id;
   1783     }
   1784   }
   1785 
   1786   RemoveLastItemFromReparentItemFolderIfNecessary(source_folder_id);
   1787 
   1788   item_list_->AddObserver(this);
   1789 
   1790   // Fade out the drag_view_ and delete it when animation ends.
   1791   int drag_view_index = view_model_.GetIndexOfView(drag_view_);
   1792   view_model_.Remove(drag_view_index);
   1793   bounds_animator_.AnimateViewTo(drag_view_, drag_view_->bounds());
   1794   bounds_animator_.SetAnimationDelegate(
   1795       drag_view_,
   1796       scoped_ptr<gfx::AnimationDelegate>(
   1797           new ItemRemoveAnimationDelegate(drag_view_)));
   1798   UpdatePaging();
   1799 }
   1800 
   1801 // After moving the re-parenting item out of the folder, if there is only 1 item
   1802 // left, remove the last item out of the folder, delete the folder and insert it
   1803 // to the data model at the same position. Make the same change to view_model_
   1804 // accordingly.
   1805 void AppsGridView::RemoveLastItemFromReparentItemFolderIfNecessary(
   1806     const std::string& source_folder_id) {
   1807   AppListFolderItem* source_folder =
   1808       static_cast<AppListFolderItem*>(item_list_->FindItem(source_folder_id));
   1809   if (!source_folder || source_folder->ChildItemCount() != 1u)
   1810     return;
   1811 
   1812   // Delete view associated with the folder item to be removed.
   1813   DeleteItemViewAtIndex(
   1814       view_model_.GetIndexOfView(activated_folder_item_view()));
   1815 
   1816   // Now make the data change to remove the folder item in model.
   1817   AppListItem* last_item = source_folder->item_list()->item_at(0);
   1818   model_->MoveItemToFolderAt(last_item, "", source_folder->position());
   1819 
   1820   // Create a new item view for the last item in folder.
   1821   size_t last_item_index;
   1822   if (!item_list_->FindItemIndex(last_item->id(), &last_item_index) ||
   1823       last_item_index > static_cast<size_t>(view_model_.view_size())) {
   1824     NOTREACHED();
   1825     return;
   1826   }
   1827   views::View* last_item_view = CreateViewForItemAtIndex(last_item_index);
   1828   view_model_.Add(last_item_view, last_item_index);
   1829   AddChildView(last_item_view);
   1830 }
   1831 
   1832 void AppsGridView::CancelFolderItemReparent(AppListItemView* drag_item_view) {
   1833   // The icon of the dragged item must target to its final ideal bounds after
   1834   // the animation completes.
   1835   CalculateIdealBounds();
   1836 
   1837   gfx::Rect target_icon_rect =
   1838       GetTargetIconRectInFolder(drag_item_view, activated_folder_item_view_);
   1839 
   1840   gfx::Rect drag_view_icon_to_grid =
   1841       drag_item_view->ConvertRectToParent(drag_item_view->GetIconBounds());
   1842   drag_view_icon_to_grid.ClampToCenteredSize(
   1843         gfx::Size(kPreferredIconDimension, kPreferredIconDimension));
   1844   TopIconAnimationView* icon_view = new TopIconAnimationView(
   1845       drag_item_view->item()->icon(),
   1846       target_icon_rect,
   1847       false);    /* animate like closing folder */
   1848   AddChildView(icon_view);
   1849   icon_view->SetBoundsRect(drag_view_icon_to_grid);
   1850   icon_view->TransformView();
   1851 }
   1852 
   1853 void AppsGridView::CancelContextMenusOnCurrentPage() {
   1854   int start = pagination_model_.selected_page() * tiles_per_page();
   1855   int end = std::min(view_model_.view_size(), start + tiles_per_page());
   1856   for (int i = start; i < end; ++i) {
   1857     AppListItemView* view =
   1858         static_cast<AppListItemView*>(view_model_.view_at(i));
   1859     view->CancelContextMenu();
   1860   }
   1861 }
   1862 
   1863 void AppsGridView::DeleteItemViewAtIndex(int index) {
   1864   views::View* item_view = view_model_.view_at(index);
   1865   view_model_.Remove(index);
   1866   if (item_view == drag_view_)
   1867     drag_view_ = NULL;
   1868   delete item_view;
   1869 }
   1870 
   1871 bool AppsGridView::IsPointWithinDragBuffer(const gfx::Point& point) const {
   1872   gfx::Rect rect(GetLocalBounds());
   1873   rect.Inset(-kDragBufferPx, -kDragBufferPx, -kDragBufferPx, -kDragBufferPx);
   1874   return rect.Contains(point);
   1875 }
   1876 
   1877 void AppsGridView::ButtonPressed(views::Button* sender,
   1878                                  const ui::Event& event) {
   1879   if (dragging())
   1880     return;
   1881 
   1882   if (strcmp(sender->GetClassName(), AppListItemView::kViewClassName))
   1883     return;
   1884 
   1885   if (delegate_) {
   1886     // Always set the previous activated_folder_item_view_ to be visible. This
   1887     // prevents a case where the item would remain hidden due the
   1888     // |activated_folder_item_view_| changing during the animation. We only
   1889     // need to track |activated_folder_item_view_| in the root level grid view.
   1890     if (!folder_delegate_) {
   1891       if (activated_folder_item_view_)
   1892         activated_folder_item_view_->SetVisible(true);
   1893       AppListItemView* pressed_item_view =
   1894           static_cast<AppListItemView*>(sender);
   1895       if (IsFolderItem(pressed_item_view->item()))
   1896         activated_folder_item_view_ = pressed_item_view;
   1897       else
   1898         activated_folder_item_view_ = NULL;
   1899     }
   1900     delegate_->ActivateApp(static_cast<AppListItemView*>(sender)->item(),
   1901                            event.flags());
   1902   }
   1903 }
   1904 
   1905 void AppsGridView::OnListItemAdded(size_t index, AppListItem* item) {
   1906   EndDrag(true);
   1907 
   1908   views::View* view = CreateViewForItemAtIndex(index);
   1909   view_model_.Add(view, index);
   1910   AddChildView(view);
   1911 
   1912   UpdatePaging();
   1913   UpdatePulsingBlockViews();
   1914   Layout();
   1915   SchedulePaint();
   1916 }
   1917 
   1918 void AppsGridView::OnListItemRemoved(size_t index, AppListItem* item) {
   1919   EndDrag(true);
   1920 
   1921   DeleteItemViewAtIndex(index);
   1922 
   1923   UpdatePaging();
   1924   UpdatePulsingBlockViews();
   1925   Layout();
   1926   SchedulePaint();
   1927 }
   1928 
   1929 void AppsGridView::OnListItemMoved(size_t from_index,
   1930                                    size_t to_index,
   1931                                    AppListItem* item) {
   1932   EndDrag(true);
   1933   view_model_.Move(from_index, to_index);
   1934 
   1935   UpdatePaging();
   1936   AnimateToIdealBounds();
   1937 }
   1938 
   1939 void AppsGridView::TotalPagesChanged() {
   1940 }
   1941 
   1942 void AppsGridView::SelectedPageChanged(int old_selected, int new_selected) {
   1943   if (dragging()) {
   1944     CalculateDropTarget(last_drag_point_, true);
   1945     Layout();
   1946     MaybeStartPageFlipTimer(last_drag_point_);
   1947   } else {
   1948     ClearSelectedView(selected_view_);
   1949     Layout();
   1950   }
   1951 }
   1952 
   1953 void AppsGridView::TransitionStarted() {
   1954   CancelContextMenusOnCurrentPage();
   1955 }
   1956 
   1957 void AppsGridView::TransitionChanged() {
   1958   // Update layout for valid page transition only since over-scroll no longer
   1959   // animates app icons.
   1960   const PaginationModel::Transition& transition =
   1961       pagination_model_.transition();
   1962   if (pagination_model_.is_valid_page(transition.target_page))
   1963     Layout();
   1964 }
   1965 
   1966 void AppsGridView::OnAppListModelStatusChanged() {
   1967   UpdatePulsingBlockViews();
   1968   Layout();
   1969   SchedulePaint();
   1970 }
   1971 
   1972 void AppsGridView::SetViewHidden(views::View* view, bool hide, bool immediate) {
   1973   ui::ScopedLayerAnimationSettings animator(view->layer()->GetAnimator());
   1974   animator.SetPreemptionStrategy(
   1975       immediate ? ui::LayerAnimator::IMMEDIATELY_SET_NEW_TARGET :
   1976                   ui::LayerAnimator::BLEND_WITH_CURRENT_ANIMATION);
   1977   view->layer()->SetOpacity(hide ? 0 : 1);
   1978 }
   1979 
   1980 void AppsGridView::OnImplicitAnimationsCompleted() {
   1981   if (layer()->opacity() == 0.0f)
   1982     SetVisible(false);
   1983 }
   1984 
   1985 bool AppsGridView::EnableFolderDragDropUI() {
   1986   // Enable drag and drop folder UI only if it is at the app list root level
   1987   // and the switch is on and the target folder can still accept new items.
   1988   return model_->folders_enabled() && !folder_delegate_ &&
   1989       CanDropIntoTarget(drop_target_);
   1990 }
   1991 
   1992 bool AppsGridView::CanDropIntoTarget(const Index& drop_target) {
   1993   views::View* target_view = GetViewAtSlotOnCurrentPage(drop_target.slot);
   1994   if (!target_view)
   1995     return true;
   1996 
   1997   AppListItem* target_item =
   1998       static_cast<AppListItemView*>(target_view)->item();
   1999   // Items can be dropped into non-folders (which have no children) or folders
   2000   // that have fewer than the max allowed items.
   2001   // OEM folder does not allow to drag/drop other items in it.
   2002   return target_item->ChildItemCount() < kMaxFolderItems &&
   2003          !IsOEMFolderItem(target_item);
   2004 }
   2005 
   2006 // TODO(jennyz): Optimize the calculation for finding nearest tile.
   2007 AppsGridView::Index AppsGridView::GetNearestTileForDragView() {
   2008   Index nearest_tile;
   2009   nearest_tile.page = -1;
   2010   nearest_tile.slot = -1;
   2011   int d_min = -1;
   2012 
   2013   // Calculate the top left tile |drag_view| intersects.
   2014   gfx::Point pt = drag_view_->bounds().origin();
   2015   CalculateNearestTileForVertex(pt, &nearest_tile, &d_min);
   2016 
   2017   // Calculate the top right tile |drag_view| intersects.
   2018   pt = drag_view_->bounds().top_right();
   2019   CalculateNearestTileForVertex(pt, &nearest_tile, &d_min);
   2020 
   2021   // Calculate the bottom left tile |drag_view| intersects.
   2022   pt = drag_view_->bounds().bottom_left();
   2023   CalculateNearestTileForVertex(pt, &nearest_tile, &d_min);
   2024 
   2025   // Calculate the bottom right tile |drag_view| intersects.
   2026   pt = drag_view_->bounds().bottom_right();
   2027   CalculateNearestTileForVertex(pt, &nearest_tile, &d_min);
   2028 
   2029   const int d_folder_dropping =
   2030       kFolderDroppingCircleRadius + kPreferredIconDimension / 2;
   2031   const int d_reorder =
   2032       kReorderDroppingCircleRadius + kPreferredIconDimension / 2;
   2033 
   2034   // If user drags an item across pages to the last page, and targets it
   2035   // to the last empty slot on it, push the last item for re-ordering.
   2036   if (IsLastPossibleDropTarget(nearest_tile) && d_min < d_reorder) {
   2037     drop_attempt_ = DROP_FOR_REORDER;
   2038     nearest_tile.slot = nearest_tile.slot - 1;
   2039     return nearest_tile;
   2040   }
   2041 
   2042   if (IsValidIndex(nearest_tile)) {
   2043     if (d_min < d_folder_dropping) {
   2044       views::View* target_view = GetViewAtSlotOnCurrentPage(nearest_tile.slot);
   2045       if (target_view &&
   2046           !IsFolderItem(static_cast<AppListItemView*>(drag_view_)->item())) {
   2047         // If a non-folder item is dragged to the target slot with an item
   2048         // sitting on it, attempt to drop the dragged item into the folder
   2049         // containing the item on nearest_tile.
   2050         drop_attempt_ = DROP_FOR_FOLDER;
   2051         return nearest_tile;
   2052       } else {
   2053         // If the target slot is blank, or the dragged item is a folder, attempt
   2054         // to re-order.
   2055         drop_attempt_ = DROP_FOR_REORDER;
   2056         return nearest_tile;
   2057       }
   2058     } else if (d_min < d_reorder) {
   2059       // Entering the re-order circle of the slot.
   2060       drop_attempt_ = DROP_FOR_REORDER;
   2061       return nearest_tile;
   2062     }
   2063   }
   2064 
   2065   // If |drag_view| is not entering the re-order or fold dropping region of
   2066   // any items, cancel any previous re-order or folder dropping timer, and
   2067   // return itself.
   2068   drop_attempt_ = DROP_FOR_NONE;
   2069   reorder_timer_.Stop();
   2070   folder_dropping_timer_.Stop();
   2071 
   2072   // When dragging for reparent a folder item, it should go back to its parent
   2073   // folder item if there is no drop target.
   2074   if (IsDraggingForReparentInRootLevelGridView()) {
   2075     DCHECK(activated_folder_item_view_);
   2076     return GetIndexOfView(activated_folder_item_view_);
   2077   }
   2078 
   2079   return GetIndexOfView(drag_view_);
   2080 }
   2081 
   2082 void AppsGridView::CalculateNearestTileForVertex(const gfx::Point& vertex,
   2083                                                  Index* nearest_tile,
   2084                                                  int* d_min) {
   2085   Index target_index;
   2086   gfx::Rect target_bounds = GetTileBoundsForPoint(vertex, &target_index);
   2087 
   2088   if (target_bounds.IsEmpty() || target_index == *nearest_tile)
   2089     return;
   2090 
   2091   // Do not count the tile, where drag_view_ used to sit on and is still moving
   2092   // on top of it, in calculating nearest tile for drag_view_.
   2093   views::View* target_view = GetViewAtSlotOnCurrentPage(target_index.slot);
   2094   if (target_index == drag_view_init_index_ && !target_view &&
   2095       !IsDraggingForReparentInRootLevelGridView()) {
   2096     return;
   2097   }
   2098 
   2099   int d_center = GetDistanceBetweenRects(drag_view_->bounds(), target_bounds);
   2100   if (*d_min < 0 || d_center < *d_min) {
   2101     *d_min = d_center;
   2102     *nearest_tile = target_index;
   2103   }
   2104 }
   2105 
   2106 gfx::Rect AppsGridView::GetTileBoundsForPoint(const gfx::Point& point,
   2107                                               Index *tile_index) {
   2108   // Check if |point| is outside of contents bounds.
   2109   gfx::Rect bounds(GetContentsBounds());
   2110   if (!bounds.Contains(point))
   2111     return gfx::Rect();
   2112 
   2113   // Calculate which tile |point| is enclosed in.
   2114   int x = point.x();
   2115   int y = point.y();
   2116   int col = (x - bounds.x()) / kPreferredTileWidth;
   2117   int row = (y - bounds.y()) / kPreferredTileHeight;
   2118   gfx::Rect tile_rect = GetTileBounds(row, col);
   2119 
   2120   // Check if |point| is outside a valid item's tile.
   2121   Index index(pagination_model_.selected_page(), row * cols_ + col);
   2122   *tile_index = index;
   2123   return tile_rect;
   2124 }
   2125 
   2126 gfx::Rect AppsGridView::GetTileBounds(int row, int col) const {
   2127   gfx::Rect bounds(GetContentsBounds());
   2128   gfx::Size tile_size(kPreferredTileWidth, kPreferredTileHeight);
   2129   gfx::Rect grid_rect(gfx::Size(tile_size.width() * cols_,
   2130                                 tile_size.height() * rows_per_page_));
   2131   grid_rect.Intersect(bounds);
   2132   gfx::Rect tile_rect(
   2133       gfx::Point(grid_rect.x() + col * tile_size.width(),
   2134                  grid_rect.y() + row * tile_size.height()),
   2135       tile_size);
   2136   return tile_rect;
   2137 }
   2138 
   2139 bool AppsGridView::IsLastPossibleDropTarget(const Index& index) const {
   2140   int last_possible_slot = view_model_.view_size() % tiles_per_page();
   2141   return (index.page == pagination_model_.total_pages() - 1 &&
   2142           index.slot == last_possible_slot + 1);
   2143 }
   2144 
   2145 views::View* AppsGridView::GetViewAtSlotOnCurrentPage(int slot) {
   2146   if (slot < 0)
   2147     return NULL;
   2148 
   2149   // Calculate the original bound of the tile at |index|.
   2150   int row = slot / cols_;
   2151   int col = slot % cols_;
   2152   gfx::Rect tile_rect = GetTileBounds(row, col);
   2153 
   2154   for (int i = 0; i < view_model_.view_size(); ++i) {
   2155     views::View* view = view_model_.view_at(i);
   2156     if (view->bounds() == tile_rect && view != drag_view_)
   2157       return view;
   2158   }
   2159   return NULL;
   2160 }
   2161 
   2162 void AppsGridView::SetAsFolderDroppingTarget(const Index& target_index,
   2163                                              bool is_target_folder) {
   2164   AppListItemView* target_view =
   2165       static_cast<AppListItemView*>(
   2166           GetViewAtSlotOnCurrentPage(target_index.slot));
   2167   if (target_view)
   2168     target_view->SetAsAttemptedFolderTarget(is_target_folder);
   2169 }
   2170 
   2171 }  // namespace app_list
   2172