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#ifndef UI_APP_LIST_VIEWS_APPS_GRID_VIEW_H_
6#define UI_APP_LIST_VIEWS_APPS_GRID_VIEW_H_
7
8#include <set>
9
10#include "base/basictypes.h"
11#include "base/compiler_specific.h"
12#include "base/timer/timer.h"
13#include "ui/app_list/app_list_export.h"
14#include "ui/app_list/app_list_model.h"
15#include "ui/app_list/app_list_model_observer.h"
16#include "ui/app_list/pagination_model_observer.h"
17#include "ui/base/models/list_model_observer.h"
18#include "ui/views/animation/bounds_animator.h"
19#include "ui/views/controls/button/button.h"
20#include "ui/views/view.h"
21#include "ui/views/view_model.h"
22
23#if defined(OS_WIN)
24#include "ui/base/dragdrop/drag_source_win.h"
25#endif
26
27namespace content {
28class WebContents;
29}
30
31namespace views {
32class ButtonListener;
33class DragImageView;
34class WebView;
35}
36
37namespace app_list {
38
39#if defined(OS_WIN)
40class SynchronousDrag;
41#endif
42
43namespace test {
44class AppsGridViewTestApi;
45}
46
47class ApplicationDragAndDropHost;
48class AppListItemView;
49class AppsGridViewDelegate;
50class PageSwitcher;
51class PaginationModel;
52
53// AppsGridView displays a grid for AppListItemList sub model.
54class APP_LIST_EXPORT AppsGridView : public views::View,
55                                     public views::ButtonListener,
56                                     public AppListItemListObserver,
57                                     public PaginationModelObserver,
58                                     public AppListModelObserver {
59 public:
60  enum Pointer {
61    NONE,
62    MOUSE,
63    TOUCH,
64  };
65
66  // Constructs the app icon grid view. |delegate| is the delegate of this
67  // view, which usually is the hosting AppListView. |pagination_model| is
68  // the paging info shared within the launcher UI. |start_page_contents| is
69  // the contents for the launcher start page. It could be NULL if the start
70  // page is not available.
71  AppsGridView(AppsGridViewDelegate* delegate,
72               PaginationModel* pagination_model,
73               content::WebContents* start_page_contents);
74  virtual ~AppsGridView();
75
76  // Sets fixed layout parameters. After setting this, CalculateLayout below
77  // is no longer called to dynamically choosing those layout params.
78  void SetLayout(int icon_size, int cols, int rows_per_page);
79
80  // Sets |model| to use. Note this does not take ownership of |model|.
81  void SetModel(AppListModel* model);
82
83  // Sets the |item_list| to render. Note this does not take ownership of
84  // |item_list|.
85  void SetItemList(AppListItemList* item_list);
86
87  void SetSelectedView(views::View* view);
88  void ClearSelectedView(views::View* view);
89  void ClearAnySelectedView();
90  bool IsSelectedView(const views::View* view) const;
91
92  // Ensures the view is visible. Note that if there is a running page
93  // transition, this does nothing.
94  void EnsureViewVisible(const views::View* view);
95
96  void InitiateDrag(AppListItemView* view,
97                    Pointer pointer,
98                    const ui::LocatedEvent& event);
99
100  // Called from AppListItemView when it receives a drag event.
101  void UpdateDragFromItem(Pointer pointer,
102                          const ui::LocatedEvent& event);
103
104  // Called when the user is dragging an app. |point| is in grid view
105  // coordinates.
106  void UpdateDrag(Pointer pointer, const gfx::Point& point);
107  void EndDrag(bool cancel);
108  bool IsDraggedView(const views::View* view) const;
109
110  void StartSettingUpSynchronousDrag();
111  bool RunSynchronousDrag();
112  void CleanUpSynchronousDrag();
113  void OnGotShortcutPath(const base::FilePath& path);
114
115  // Set the drag and drop host for application links.
116  void SetDragAndDropHostOfCurrentAppList(
117      ApplicationDragAndDropHost* drag_and_drop_host);
118
119  // Prerenders the icons on and around |page_index|.
120  void Prerender(int page_index);
121
122  bool has_dragged_view() const { return drag_view_ != NULL; }
123  bool dragging() const { return drag_pointer_ != NONE; }
124
125  // Overridden from views::View:
126  virtual gfx::Size GetPreferredSize() OVERRIDE;
127  virtual void Layout() OVERRIDE;
128  virtual bool OnKeyPressed(const ui::KeyEvent& event) OVERRIDE;
129  virtual bool OnKeyReleased(const ui::KeyEvent& event) OVERRIDE;
130  virtual void ViewHierarchyChanged(
131      const ViewHierarchyChangedDetails& details) OVERRIDE;
132  virtual bool GetDropFormats(
133      int* formats,
134      std::set<OSExchangeData::CustomFormat>* custom_formats) OVERRIDE;
135  virtual bool CanDrop(const OSExchangeData& data) OVERRIDE;
136  virtual int OnDragUpdated(const ui::DropTargetEvent& event) OVERRIDE;
137
138  // Stops the timer that triggers a page flip during a drag.
139  void StopPageFlipTimer();
140
141  // Return the view model for test purposes.
142  const views::ViewModel* view_model_for_test() const { return &view_model_; }
143
144  // For test: Return if the drag and drop handler was set.
145  bool has_drag_and_drop_host_for_test() { return NULL != drag_and_drop_host_; }
146
147  // For test: Return if the drag and drop operation gets dispatched.
148  bool forward_events_to_drag_and_drop_host_for_test() {
149    return forward_events_to_drag_and_drop_host_;
150  }
151
152  void set_is_root_level(bool value) { is_root_level_ = value; }
153
154 private:
155  friend class test::AppsGridViewTestApi;
156
157  enum DropAttempt {
158    DROP_FOR_NONE,
159    DROP_FOR_REORDER,
160    DROP_FOR_FOLDER,
161  };
162
163  // Represents the index to an item view in the grid.
164  struct Index {
165    Index() : page(-1), slot(-1) {}
166    Index(int page, int slot) : page(page), slot(slot) {}
167
168    bool operator==(const Index& other) const {
169      return page == other.page && slot == other.slot;
170    }
171    bool operator!=(const Index& other) const {
172      return page != other.page || slot != other.slot;
173    }
174
175    int page;  // Which page an item view is on.
176    int slot;  // Which slot in the page an item view is in.
177  };
178
179  int tiles_per_page() const { return cols_ * rows_per_page_; }
180
181  // Updates from model.
182  void Update();
183
184  // Updates page splits for item views.
185  void UpdatePaging();
186
187  // Updates the number of pulsing block views based on AppListModel status and
188  // number of apps.
189  void UpdatePulsingBlockViews();
190
191  views::View* CreateViewForItemAtIndex(size_t index);
192
193  // Convert between the model index and the visual index. The model index
194  // is the index of the item in AppListModel. The visual index is the Index
195  // struct above with page/slot info of where to display the item.
196  Index GetIndexFromModelIndex(int model_index) const;
197  int GetModelIndexFromIndex(const Index& index) const;
198
199  void SetSelectedItemByIndex(const Index& index);
200  bool IsValidIndex(const Index& index) const;
201
202  Index GetIndexOfView(const views::View* view) const;
203  views::View* GetViewAtIndex(const Index& index) const;
204
205  void MoveSelected(int page_delta, int slot_x_delta, int slot_y_delta);
206
207  void CalculateIdealBounds();
208  void AnimateToIdealBounds();
209
210  // Invoked when the given |view|'s current bounds and target bounds are on
211  // different rows. To avoid moving diagonally, |view| would be put into a
212  // slot prior |target| and fade in while moving to |target|. In the meanwhile,
213  // a layer copy of |view| would start at |current| and fade out while moving
214  // to succeeding slot of |current|. |animate_current| controls whether to run
215  // fading out animation from |current|. |animate_target| controls whether to
216  // run fading in animation to |target|.
217  void AnimationBetweenRows(views::View* view,
218                            bool animate_current,
219                            const gfx::Rect& current,
220                            bool animate_target,
221                            const gfx::Rect& target);
222
223  // Extracts drag location info from |event| into |drag_point|.
224  void ExtractDragLocation(const ui::LocatedEvent& event,
225                           gfx::Point* drag_point);
226
227  // Calculates |drop_target_| based on |drag_point|. |drag_point| is in the
228  // grid view's coordinates. When |use_page_button_hovering| is true and
229  // |drag_point| is hovering on a page button, use the last slot on that page
230  // as drop target.
231  void CalculateDropTarget(const gfx::Point& drag_point,
232                           bool use_page_button_hovering);
233
234  // Same as CalculateDropTarget, but with folder UI enabled. The |drop_target_|
235  // can be either a target for re-ordering, or a target folder to move the
236  // dragged item into if |drag_view_| enters its re-ordering or folder
237  // dropping circle.
238  void CalculateDropTargetWithFolderEnabled(const gfx::Point& drag_point,
239                                            bool use_page_button_hovering);
240
241  // Prepares |drag_and_drop_host_| for dragging. |grid_location| contains
242  // the drag point in this grid view's coordinates.
243  void StartDragAndDropHostDrag(const gfx::Point& grid_location);
244
245  // Dispatch the drag and drop update event to the dnd host (if needed).
246  void DispatchDragEventToDragAndDropHost(
247      const gfx::Point& location_in_screen_coordinates);
248
249  // Starts the page flip timer if |drag_point| is in left/right side page flip
250  // zone or is over page switcher.
251  void MaybeStartPageFlipTimer(const gfx::Point& drag_point);
252
253  // Invoked when |page_flip_timer_| fires.
254  void OnPageFlipTimer();
255
256  // Updates |model_| to move item represented by |item_view| to |target| slot.
257  void MoveItemInModel(views::View* item_view, const Index& target);
258
259  // Updates |model_| to move item represented by |item_view| into a folder
260  // containing item located at |target| slot, also update |view_model_| for
261  // the related view changes.
262  void MoveItemToFolder(views::View* item_view, const Index& target);
263
264  // Cancels any context menus showing for app items on the current page.
265  void CancelContextMenusOnCurrentPage();
266
267  // Returnes true if |point| lies within the bounds of this grid view plus a
268  // buffer area surrounding it.
269  bool IsPointWithinDragBuffer(const gfx::Point& point) const;
270
271  // Handles start page layout and transition animation.
272  void LayoutStartPage();
273
274  // Overridden from views::ButtonListener:
275  virtual void ButtonPressed(views::Button* sender,
276                             const ui::Event& event) OVERRIDE;
277
278  // Overridden from AppListItemListObserver:
279  virtual void OnListItemAdded(size_t index, AppListItemModel* item) OVERRIDE;
280  virtual void OnListItemRemoved(size_t index, AppListItemModel* item) OVERRIDE;
281  virtual void OnListItemMoved(size_t from_index,
282                               size_t to_index,
283                               AppListItemModel* item) OVERRIDE;
284
285  // Overridden from PaginationModelObserver:
286  virtual void TotalPagesChanged() OVERRIDE;
287  virtual void SelectedPageChanged(int old_selected, int new_selected) OVERRIDE;
288  virtual void TransitionStarted() OVERRIDE;
289  virtual void TransitionChanged() OVERRIDE;
290
291  // Overridden from AppListModelObserver:
292  virtual void OnAppListModelStatusChanged() OVERRIDE;
293
294  // Hide a given view temporarily without losing (mouse) events and / or
295  // changing the size of it. If |immediate| is set the change will be
296  // immediately applied - otherwise it will change gradually.
297  // If |hide| is set the view will get hidden, otherwise it gets shown.
298  void SetViewHidden(views::View* view, bool hide, bool immediate);
299
300  // Whether the folder drag-and-drop UI should be enabled.
301  bool EnableFolderDragDropUI();
302
303  // Whether target specified by |drap_target| can accept more items to be
304  // dropped into it.
305  bool CanDropIntoTarget(const Index& drop_target);
306
307  // Returns the visual index of the nearest tile in which |drag_view_| enters
308  // either its re-ordering or folder dropping circle.
309  Index GetNearestTileForDragView();
310
311  // Calculates |nearest_tile| in which |vertex| of the |drag_view| is
312  // enclosed.
313  // *|nearest_tile| and *|d_min| will be updated based on the calculation.
314  // *|d_min| is the distance between |nearest_tile| and |drag_view_|.
315  void CalculateNearestTileForVertex(
316      const gfx::Point& vertex, Index* nearest_tile, int* d_min);
317
318  // Returns the bounds of the tile in which |point| is enclosed if there
319  // is a valid item sits on the tile.
320  gfx::Rect GetTileBoundsForPoint(const gfx::Point& point, Index* tile_index);
321
322  // Gets the bounds of the tile located at |row| and |col| on current page.
323  gfx::Rect GetTileBounds(int row, int col) const;
324
325  // Gets the item view located at |slot| on the current page. If there is
326  // no item located at |slot|, returns NULL.
327  views::View* GetViewAtSlotOnCurrentPage(int slot);
328
329  // Sets state of the view with |target_index| to |is_target_folder| for
330  // dropping |drag_view_|.
331  void SetAsFolderDroppingTarget(const Index& target_index,
332                                 bool is_target_folder);
333
334  // Invoked when |reorder_timer_| fires to show re-order preview UI.
335  void OnReorderTimer();
336
337  // Invoked when |folder_dropping_timer_| fires to show folder dropping
338  // preview UI.
339  void OnFolderDroppingTimer();
340
341  AppListModel* model_;  // Owned by AppListView.
342  AppListItemList* item_list_;  // Not owned.
343  AppsGridViewDelegate* delegate_;
344  PaginationModel* pagination_model_;  // Owned by AppListController.
345  PageSwitcher* page_switcher_view_;  // Owned by views hierarchy.
346  views::WebView* start_page_view_;  // Owned by views hierarchy.
347
348  gfx::Size icon_size_;
349  int cols_;
350  int rows_per_page_;
351
352  // Tracks app item views. There is a view per item in |model_|.
353  views::ViewModel view_model_;
354
355  // Tracks pulsing block views.
356  views::ViewModel pulsing_blocks_model_;
357
358  views::View* selected_view_;
359
360  AppListItemView* drag_view_;
361
362  // The point where the drag started in AppListItemView coordinates.
363  gfx::Point drag_view_offset_;
364
365  // The point where the drag started in GridView coordinates.
366  gfx::Point drag_start_grid_view_;
367
368  // The location of |drag_view_| when the drag started.
369  gfx::Point drag_view_start_;
370
371  // Page the drag started on.
372  int drag_start_page_;
373
374#if defined(OS_WIN)
375  // Created when a drag is started (ie: drag exceeds the drag threshold), but
376  // not Run() until supplied with a shortcut path.
377  scoped_refptr<SynchronousDrag> synchronous_drag_;
378#endif
379
380  Pointer drag_pointer_;
381  Index drop_target_;
382  DropAttempt drop_attempt_;
383
384  // Timer for re-ordering the |drop_target_| and |drag_view_|.
385  base::OneShotTimer<AppsGridView> reorder_timer_;
386
387  // Timer for dropping |drag_view_| into the folder containing
388  // the |drop_target_|.
389  base::OneShotTimer<AppsGridView> folder_dropping_timer_;
390
391  // An application target drag and drop host which accepts dnd operations.
392  ApplicationDragAndDropHost* drag_and_drop_host_;
393
394  // The drag operation is currently inside the dnd host and events get
395  // forwarded.
396  bool forward_events_to_drag_and_drop_host_;
397
398  // Last mouse drag location in this view's coordinates.
399  gfx::Point last_drag_point_;
400
401  // Timer to auto flip page when dragging an item near the left/right edges.
402  base::OneShotTimer<AppsGridView> page_flip_timer_;
403
404  // Target page to switch to when |page_flip_timer_| fires.
405  int page_flip_target_;
406
407  // Delay in milliseconds of when |page_flip_timer_| should fire after user
408  // drags an item near the edges.
409  int page_flip_delay_in_ms_;
410
411  views::BoundsAnimator bounds_animator_;
412
413  // If true, AppsGridView is rending items at the root level of the app list.
414  bool is_root_level_;
415
416  DISALLOW_COPY_AND_ASSIGN(AppsGridView);
417};
418
419}  // namespace app_list
420
421#endif  // UI_APP_LIST_VIEWS_APPS_GRID_VIEW_H_
422