Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / tabs / tab_drag_controller.h
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 CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_H_
6 #define CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_H_
7
8 #include <vector>
9
10 #include "base/memory/weak_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/timer/timer.h"
13 #include "chrome/browser/ui/host_desktop.h"
14 #include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
15 #include "chrome/browser/ui/views/tabs/tab_strip_types.h"
16 #include "content/public/browser/notification_observer.h"
17 #include "content/public/browser/notification_registrar.h"
18 #include "ui/base/models/list_selection_model.h"
19 #include "ui/gfx/rect.h"
20 #include "ui/views/widget/widget_observer.h"
21
22 namespace gfx {
23 class Screen;
24 }
25 namespace ui {
26 class EventHandler;
27 class ListSelectionModel;
28 }
29 namespace views {
30 class View;
31 }
32 class Browser;
33 class Tab;
34 struct TabRendererData;
35 class TabStrip;
36 class TabStripModel;
37
38 // TabDragController is responsible for managing the tab dragging session. When
39 // the user presses the mouse on a tab a new TabDragController is created and
40 // Drag() is invoked as the mouse is dragged. If the mouse is dragged far enough
41 // TabDragController starts a drag session. The drag session is completed when
42 // EndDrag() is invoked (or the TabDragController is destroyed).
43 //
44 // While dragging within a tab strip TabDragController sets the bounds of the
45 // tabs (this is referred to as attached). When the user drags far enough such
46 // that the tabs should be moved out of the tab strip a new Browser is created
47 // and RunMoveLoop() is invoked on the Widget to drag the browser around. This
48 // is the default on aura.
49 class TabDragController : public content::NotificationObserver,
50                           public views::WidgetObserver,
51                           public TabStripModelObserver {
52  public:
53   // What should happen as the mouse is dragged within the tabstrip.
54   enum MoveBehavior {
55     // Only the set of visible tabs should change. This is only applicable when
56     // using touch layout.
57     MOVE_VISIBILE_TABS,
58
59     // Typical behavior where tabs are dragged around.
60     REORDER
61   };
62
63   // Indicates the event source that initiated the drag.
64   enum EventSource {
65     EVENT_SOURCE_MOUSE,
66     EVENT_SOURCE_TOUCH,
67   };
68
69   // Amount above or below the tabstrip the user has to drag before detaching.
70   static const int kTouchVerticalDetachMagnetism;
71   static const int kVerticalDetachMagnetism;
72
73   TabDragController();
74   virtual ~TabDragController();
75
76   // Initializes TabDragController to drag the tabs in |tabs| originating from
77   // |source_tabstrip|. |source_tab| is the tab that initiated the drag and is
78   // contained in |tabs|.  |mouse_offset| is the distance of the mouse pointer
79   // from the origin of the first tab in |tabs| and |source_tab_offset| the
80   // offset from |source_tab|. |source_tab_offset| is the horizontal offset of
81   // |mouse_offset| relative to |source_tab|. |initial_selection_model| is the
82   // selection model before the drag started and is only non-empty if
83   // |source_tab| was not initially selected.
84   void Init(TabStrip* source_tabstrip,
85             Tab* source_tab,
86             const std::vector<Tab*>& tabs,
87             const gfx::Point& mouse_offset,
88             int source_tab_offset,
89             const ui::ListSelectionModel& initial_selection_model,
90             MoveBehavior move_behavior,
91             EventSource event_source);
92
93   // Returns true if there is a drag underway and the drag is attached to
94   // |tab_strip|.
95   // NOTE: this returns false if the TabDragController is in the process of
96   // finishing the drag.
97   static bool IsAttachedTo(const TabStrip* tab_strip);
98
99   // Returns true if there is a drag underway.
100   static bool IsActive();
101
102   // Sets the move behavior. Has no effect if started_drag() is true.
103   void SetMoveBehavior(MoveBehavior behavior);
104   MoveBehavior move_behavior() const { return move_behavior_; }
105
106   EventSource event_source() const { return event_source_; }
107
108   // See description above fields for details on these.
109   bool active() const { return active_; }
110   const TabStrip* attached_tabstrip() const { return attached_tabstrip_; }
111
112   // Returns true if a drag started.
113   bool started_drag() const { return started_drag_; }
114
115   // Returns true if mutating the TabStripModel.
116   bool is_mutating() const { return is_mutating_; }
117
118   // Returns true if we've detached from a tabstrip and are running a nested
119   // move message loop.
120   bool is_dragging_window() const { return is_dragging_window_; }
121
122   // Invoked to drag to the new location, in screen coordinates.
123   void Drag(const gfx::Point& point_in_screen);
124
125   // Complete the current drag session.
126   void EndDrag(EndDragReason reason);
127
128  private:
129   // Used to indicate the direction the mouse has moved when attached.
130   static const int kMovedMouseLeft  = 1 << 0;
131   static const int kMovedMouseRight = 1 << 1;
132
133   // Enumeration of the ways a drag session can end.
134   enum EndDragType {
135     // Drag session exited normally: the user released the mouse.
136     NORMAL,
137
138     // The drag session was canceled (alt-tab during drag, escape ...)
139     CANCELED,
140
141     // The tab (NavigationController) was destroyed during the drag.
142     TAB_DESTROYED
143   };
144
145   // Whether Detach() should release capture or not.
146   enum ReleaseCapture {
147     RELEASE_CAPTURE,
148     DONT_RELEASE_CAPTURE,
149   };
150
151   // Specifies what should happen when RunMoveLoop completes.
152   enum EndRunLoopBehavior {
153     // Indicates the drag should end.
154     END_RUN_LOOP_STOP_DRAGGING,
155
156     // Indicates the drag should continue.
157     END_RUN_LOOP_CONTINUE_DRAGGING
158   };
159
160   // Enumeration of the possible positions the detached tab may detach from.
161   enum DetachPosition {
162     DETACH_BEFORE,
163     DETACH_AFTER,
164     DETACH_ABOVE_OR_BELOW
165   };
166
167   // Indicates what should happen after invoking DragBrowserToNewTabStrip().
168   enum DragBrowserResultType {
169     // The caller should return immediately. This return value is used if a
170     // nested message loop was created or we're in a nested message loop and
171     // need to exit it.
172     DRAG_BROWSER_RESULT_STOP,
173
174     // The caller should continue.
175     DRAG_BROWSER_RESULT_CONTINUE,
176   };
177
178   // Stores the date associated with a single tab that is being dragged.
179   struct TabDragData {
180     TabDragData();
181     ~TabDragData();
182
183     // The WebContents being dragged.
184     content::WebContents* contents;
185
186     // This is the index of the tab in |source_tabstrip_| when the drag
187     // began. This is used to restore the previous state if the drag is aborted.
188     int source_model_index;
189
190     // If attached this is the tab in |attached_tabstrip_|.
191     Tab* attached_tab;
192
193     // Is the tab pinned?
194     bool pinned;
195   };
196
197   typedef std::vector<TabDragData> DragData;
198
199   // Sets |drag_data| from |tab|. This also registers for necessary
200   // notifications and resets the delegate of the WebContents.
201   void InitTabDragData(Tab* tab, TabDragData* drag_data);
202
203   // Overridden from content::NotificationObserver:
204   virtual void Observe(int type,
205                        const content::NotificationSource& source,
206                        const content::NotificationDetails& details) OVERRIDE;
207
208   // Overriden from views::WidgetObserver:
209   virtual void OnWidgetBoundsChanged(views::Widget* widget,
210                                      const gfx::Rect& new_bounds) OVERRIDE;
211
212   // Overriden from TabStripModelObserver:
213   virtual void TabStripEmpty() OVERRIDE;
214
215   // Initialize the offset used to calculate the position to create windows
216   // in |GetWindowCreatePoint|. This should only be invoked from |Init|.
217   void InitWindowCreatePoint();
218
219   // Returns the point where a detached window should be created given the
220   // current mouse position |origin|.
221   gfx::Point GetWindowCreatePoint(const gfx::Point& origin) const;
222
223   void UpdateDockInfo(const gfx::Point& point_in_screen);
224
225   // Saves focus in the window that the drag initiated from. Focus will be
226   // restored appropriately if the drag ends within this same window.
227   void SaveFocus();
228
229   // Restore focus to the View that had focus before the drag was started, if
230   // the drag ends within the same Window as it began.
231   void RestoreFocus();
232
233   // Tests whether |point_in_screen| is past a minimum elasticity threshold
234   // required to start a drag.
235   bool CanStartDrag(const gfx::Point& point_in_screen) const;
236
237   // Invoked once a drag has started to determine the appropriate tabstrip to
238   // drag to (which may be the currently attached one).
239   void ContinueDragging(const gfx::Point& point_in_screen);
240
241   // Transitions dragging from |attached_tabstrip_| to |target_tabstrip|.
242   // |target_tabstrip| is NULL if the mouse is not over a valid tab strip.  See
243   // DragBrowserResultType for details of the return type.
244   DragBrowserResultType DragBrowserToNewTabStrip(
245       TabStrip* target_tabstrip,
246       const gfx::Point& point_in_screen);
247
248   // Handles dragging for a touch tabstrip when the tabs are stacked. Doesn't
249   // actually reorder the tabs in anyway, just changes what's visible.
250   void DragActiveTabStacked(const gfx::Point& point_in_screen);
251
252   // Moves the active tab to the next/previous tab. Used when the next/previous
253   // tab is stacked.
254   void MoveAttachedToNextStackedIndex(const gfx::Point& point_in_screen);
255   void MoveAttachedToPreviousStackedIndex(const gfx::Point& point_in_screen);
256
257   // Handles dragging tabs while the tabs are attached.
258   void MoveAttached(const gfx::Point& point_in_screen);
259
260   // If necessary starts the |move_stacked_timer_|. The timer is started if
261   // close enough to an edge with stacked tabs.
262   void StartMoveStackedTimerIfNecessary(
263       const gfx::Point& point_in_screen,
264       int delay_ms);
265
266   // Returns the TabStrip for the specified window, or NULL if one doesn't exist
267   // or isn't compatible.
268   TabStrip* GetTabStripForWindow(gfx::NativeWindow window);
269
270   // Returns the compatible TabStrip to drag to at the specified point (screen
271   // coordinates), or NULL if there is none.
272   TabStrip* GetTargetTabStripForPoint(const gfx::Point& point_in_screen);
273
274   // Returns true if |tabstrip| contains the specified point in screen
275   // coordinates.
276   bool DoesTabStripContain(TabStrip* tabstrip,
277                            const gfx::Point& point_in_screen) const;
278
279   // Returns the DetachPosition given the specified location in screen
280   // coordinates.
281   DetachPosition GetDetachPosition(const gfx::Point& point_in_screen);
282
283   // Attach the dragged Tab to the specified TabStrip.
284   void Attach(TabStrip* attached_tabstrip, const gfx::Point& point_in_screen);
285
286   // Detach the dragged Tab from the current TabStrip.
287   void Detach(ReleaseCapture release_capture);
288
289   // Detaches the tabs being dragged, creates a new Browser to contain them and
290   // runs a nested move loop.
291   void DetachIntoNewBrowserAndRunMoveLoop(const gfx::Point& point_in_screen);
292
293   // Runs a nested message loop that handles moving the current
294   // Browser. |drag_offset| is the offset from the window origin and is used in
295   // calculating the location of the window offset from the cursor while
296   // dragging.
297   void RunMoveLoop(const gfx::Vector2d& drag_offset);
298
299   // Determines the index to insert tabs at. |dragged_bounds| is the bounds of
300   // the tabs being dragged, |start| the index of the tab to start looking from.
301   // The search proceeds to the end of the strip.
302   int GetInsertionIndexFrom(const gfx::Rect& dragged_bounds, int start) const;
303
304   // Like GetInsertionIndexFrom(), but searches backwards from |start| to the
305   // beginning of the strip.
306   int GetInsertionIndexFromReversed(const gfx::Rect& dragged_bounds,
307                                     int start) const;
308
309   // Returns the index where the dragged WebContents should be inserted into
310   // |attached_tabstrip_| given the DraggedTabView's bounds |dragged_bounds| in
311   // coordinates relative to |attached_tabstrip_| and has had the mirroring
312   // transformation applied.
313   // NOTE: this is invoked from Attach() before the tabs have been inserted.
314   int GetInsertionIndexForDraggedBounds(const gfx::Rect& dragged_bounds) const;
315
316   // Returns true if |dragged_bounds| is close enough to the next stacked tab
317   // so that the active tab should be dragged there.
318   bool ShouldDragToNextStackedTab(const gfx::Rect& dragged_bounds,
319                                   int index) const;
320
321   // Returns true if |dragged_bounds| is close enough to the previous stacked
322   // tab so that the active tab should be dragged there.
323   bool ShouldDragToPreviousStackedTab(const gfx::Rect& dragged_bounds,
324                                       int index) const;
325
326   // Used by GetInsertionIndexForDraggedBounds() when the tabstrip is stacked.
327   int GetInsertionIndexForDraggedBoundsStacked(
328       const gfx::Rect& dragged_bounds) const;
329
330   // Retrieve the bounds of the DraggedTabView relative to the attached
331   // TabStrip. |tab_strip_point| is in the attached TabStrip's coordinate
332   // system.
333   gfx::Rect GetDraggedViewTabStripBounds(const gfx::Point& tab_strip_point);
334
335   // Get the position of the dragged tab view relative to the attached tab
336   // strip with the mirroring transform applied.
337   gfx::Point GetAttachedDragPoint(const gfx::Point& point_in_screen);
338
339   // Finds the Tabs within the specified TabStrip that corresponds to the
340   // WebContents of the dragged tabs. Returns an empty vector if not attached.
341   std::vector<Tab*> GetTabsMatchingDraggedContents(TabStrip* tabstrip);
342
343   // Returns the bounds for the tabs based on the attached tab strip.
344   std::vector<gfx::Rect> CalculateBoundsForDraggedTabs();
345
346   // Does the work for EndDrag(). If we actually started a drag and |how_end| is
347   // not TAB_DESTROYED then one of EndDrag() or RevertDrag() is invoked.
348   void EndDragImpl(EndDragType how_end);
349
350   // Reverts a cancelled drag operation.
351   void RevertDrag();
352
353   // Reverts the tab at |drag_index| in |drag_data_|.
354   void RevertDragAt(size_t drag_index);
355
356   // Selects the dragged tabs in |model|. Does nothing if there are no longer
357   // any dragged contents (as happens when a WebContents is deleted out from
358   // under us).
359   void ResetSelection(TabStripModel* model);
360
361   // Restores |initial_selection_model_| to the |source_tabstrip_|.
362   void RestoreInitialSelection();
363
364   // Finishes a succesful drag operation.
365   void CompleteDrag();
366
367   // Maximizes the attached window.
368   void MaximizeAttachedWindow();
369
370   // Returns the bounds (in screen coordinates) of the specified View.
371   gfx::Rect GetViewScreenBounds(views::View* tabstrip) const;
372
373   // Hides the frame for the window that contains the TabStrip the current
374   // drag session was initiated from.
375   void HideFrame();
376
377   void BringWindowUnderPointToFront(const gfx::Point& point_in_screen);
378
379   // Convenience for getting the TabDragData corresponding to the tab the user
380   // started dragging.
381   TabDragData* source_tab_drag_data() {
382     return &(drag_data_[source_tab_index_]);
383   }
384
385   // Convenience for |source_tab_drag_data()->contents|.
386   content::WebContents* source_dragged_contents() {
387     return source_tab_drag_data()->contents;
388   }
389
390   // Returns the Widget of the currently attached TabStrip's BrowserView.
391   views::Widget* GetAttachedBrowserWidget();
392
393   // Returns true if the tabs were originality one after the other in
394   // |source_tabstrip_|.
395   bool AreTabsConsecutive();
396
397   // Calculates and returns new bounds for the dragged browser window.
398   // Takes into consideration current and restore bounds of |source| tab strip
399   // preventing the dragged size from being too small. Positions the new bounds
400   // such that the tab that was dragged remains under the |point_in_screen|.
401   // Offsets |drag_bounds| if necessary when dragging to the right from the
402   // source browser.
403   gfx::Rect CalculateDraggedBrowserBounds(TabStrip* source,
404                                           const gfx::Point& point_in_screen,
405                                           std::vector<gfx::Rect>* drag_bounds);
406
407   // Calculates scaled |drag_bounds| for dragged tabs and sets the tabs bounds.
408   // Layout of the tabstrip is performed and a new tabstrip width calculated.
409   // When |last_tabstrip_width| is larger than the new tabstrip width the tabs
410   // in attached tabstrip are scaled and the attached browser is positioned such
411   // that the tab that was dragged remains under the |point_in_screen|.
412   void AdjustBrowserAndTabBoundsForDrag(int last_tabstrip_width,
413                                         const gfx::Point& point_in_screen,
414                                         std::vector<gfx::Rect>* drag_bounds);
415
416   // Creates and returns a new Browser to handle the drag.
417   Browser* CreateBrowserForDrag(TabStrip* source,
418                                 const gfx::Point& point_in_screen,
419                                 gfx::Vector2d* drag_offset,
420                                 std::vector<gfx::Rect>* drag_bounds);
421
422   // Returns the TabStripModel for the specified tabstrip.
423   TabStripModel* GetModel(TabStrip* tabstrip) const;
424
425   // Returns the location of the cursor. This is either the location of the
426   // mouse or the location of the current touch point.
427   gfx::Point GetCursorScreenPoint();
428
429   // Returns the offset from the top left corner of the window to
430   // |point_in_screen|.
431   gfx::Vector2d GetWindowOffset(const gfx::Point& point_in_screen);
432
433   // Returns true if moving the mouse only changes the visible tabs.
434   bool move_only() const {
435     return (move_behavior_ == MOVE_VISIBILE_TABS) != 0;
436   }
437
438   // Returns the NativeWindow at the specified point. If |exclude_dragged_view|
439   // is true, then the dragged view is not considered.
440   gfx::NativeWindow GetLocalProcessWindow(const gfx::Point& screen_point,
441                                           bool exclude_dragged_view);
442
443   // Handles registering for notifications.
444   content::NotificationRegistrar registrar_;
445
446   EventSource event_source_;
447
448   // The TabStrip the drag originated from.
449   TabStrip* source_tabstrip_;
450
451   // The TabStrip the dragged Tab is currently attached to, or NULL if the
452   // dragged Tab is detached.
453   TabStrip* attached_tabstrip_;
454
455   // The screen that this drag is associated with. Cached, because other UI
456   // elements are NULLd at various points during the lifetime of this object.
457   gfx::Screen* screen_;
458
459   // The desktop type that this drag is associated with. Cached, because other
460   // UI elements are NULLd at various points during the lifetime of this
461   // object.
462   chrome::HostDesktopType host_desktop_type_;
463
464   // Aura mouse capture and release is used on Ash platforms as well as on
465   // Linux to ensure that pointer grab is not released prematurely.
466   bool use_aura_capture_policy_;
467
468   // The position of the mouse (in screen coordinates) at the start of the drag
469   // operation. This is used to calculate minimum elasticity before a
470   // DraggedTabView is constructed.
471   gfx::Point start_point_in_screen_;
472
473   // This is the offset of the mouse from the top left of the first Tab where
474   // dragging began. This is used to ensure that the dragged view is always
475   // positioned at the correct location during the drag, and to ensure that the
476   // detached window is created at the right location.
477   gfx::Point mouse_offset_;
478
479   // Ratio of the x-coordinate of the |source_tab_offset| to the width of the
480   // tab.
481   float offset_to_width_ratio_;
482
483   // A hint to use when positioning new windows created by detaching Tabs. This
484   // is the distance of the mouse from the top left of the dragged tab as if it
485   // were the distance of the mouse from the top left of the first tab in the
486   // attached TabStrip from the top left of the window.
487   gfx::Point window_create_point_;
488
489   // Location of the first tab in the source tabstrip in screen coordinates.
490   // This is used to calculate |window_create_point_|.
491   gfx::Point first_source_tab_point_;
492
493   // Storage ID in ViewStorage where the last view that had focus in the window
494   // containing |source_tab_| is saved. This is saved so that focus can be
495   // restored properly when a drag begins and ends within this same window.
496   const int old_focused_view_id_;
497
498   // The horizontal position of the mouse cursor in screen coordinates at the
499   // time of the last re-order event.
500   int last_move_screen_loc_;
501
502   // Timer used to bring the window under the cursor to front. If the user
503   // stops moving the mouse for a brief time over a browser window, it is
504   // brought to front.
505   base::OneShotTimer<TabDragController> bring_to_front_timer_;
506
507   // Timer used to move the stacked tabs. See comment aboue
508   // StartMoveStackedTimerIfNecessary().
509   base::OneShotTimer<TabDragController> move_stacked_timer_;
510
511   // Did the mouse move enough that we started a drag?
512   bool started_drag_;
513
514   // Is the drag active?
515   bool active_;
516
517   DragData drag_data_;
518
519   // Index of the source tab in |drag_data_|.
520   size_t source_tab_index_;
521
522   // True until MoveAttached() is first invoked.
523   bool initial_move_;
524
525   // The selection model before the drag started. See comment above Init() for
526   // details.
527   ui::ListSelectionModel initial_selection_model_;
528
529   // The selection model of |attached_tabstrip_| before the tabs were attached.
530   ui::ListSelectionModel selection_model_before_attach_;
531
532   // Initial x-coordinates of the tabs when the drag started. Only used for
533   // touch mode.
534   std::vector<int> initial_tab_positions_;
535
536   MoveBehavior move_behavior_;
537
538   // Updated as the mouse is moved when attached. Indicates whether the mouse
539   // has ever moved to the left or right. If the tabs are ever detached this
540   // is set to kMovedMouseRight | kMovedMouseLeft.
541   int mouse_move_direction_;
542
543   // Last location used in screen coordinates.
544   gfx::Point last_point_in_screen_;
545
546   // The following are needed when detaching into a browser
547   // (|detach_into_browser_| is true).
548
549   // See description above getter.
550   bool is_dragging_window_;
551
552   // True if |attached_tabstrip_| is in a browser specifically created for
553   // the drag.
554   bool is_dragging_new_browser_;
555
556   // True if |source_tabstrip_| was maximized before the drag.
557   bool was_source_maximized_;
558
559   // True if |source_tabstrip_| was in immersive fullscreen before the drag.
560   bool was_source_fullscreen_;
561
562   // True if the initial drag resulted in restoring the window (because it was
563   // maximized).
564   bool did_restore_window_;
565
566   EndRunLoopBehavior end_run_loop_behavior_;
567
568   // If true, we're waiting for a move loop to complete.
569   bool waiting_for_run_loop_to_exit_;
570
571   // The TabStrip to attach to after the move loop completes.
572   TabStrip* tab_strip_to_attach_to_after_exit_;
573
574   // Non-null for the duration of RunMoveLoop.
575   views::Widget* move_loop_widget_;
576
577   // See description above getter.
578   bool is_mutating_;
579
580   // |attach_x_| and |attach_index_| are set to the x-coordinate of the mouse
581   // (in terms of the tabstrip) and the insertion index at the time tabs are
582   // dragged into a new browser (attached). They are used to ensure we don't
583   // shift the tabs around in the wrong direction. The two are only valid if
584   // |attach_index_| is not -1.
585   // See comment around use for more details.
586   int attach_x_;
587   int attach_index_;
588
589   scoped_ptr<ui::EventHandler> escape_tracker_;
590
591   base::WeakPtrFactory<TabDragController> weak_factory_;
592
593   DISALLOW_COPY_AND_ASSIGN(TabDragController);
594 };
595
596 #endif  // CHROME_BROWSER_UI_VIEWS_TABS_TAB_DRAG_CONTROLLER_H_