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