dragged_tab_controller.h revision ddb351dbec246cf1fab5ec20d2d5520909041de1
1// Copyright (c) 2011 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_DRAGGED_TAB_CONTROLLER_H_
6#define CHROME_BROWSER_UI_VIEWS_TABS_DRAGGED_TAB_CONTROLLER_H_
7#pragma once
8
9#include <vector>
10
11#include "base/memory/scoped_ptr.h"
12#include "base/message_loop.h"
13#include "base/timer.h"
14#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
15#include "chrome/browser/ui/tabs/dock_info.h"
16#include "content/browser/tab_contents/tab_contents_delegate.h"
17#include "content/common/notification_observer.h"
18#include "content/common/notification_registrar.h"
19#include "ui/gfx/rect.h"
20
21namespace views {
22class View;
23}
24class BaseTab;
25class BaseTabStrip;
26class DraggedTabView;
27class TabStripModel;
28
29struct TabRendererData;
30
31///////////////////////////////////////////////////////////////////////////////
32//
33// DraggedTabController
34//
35//  An object that handles a drag session for an individual Tab within a
36//  TabStrip. This object is created whenever the mouse is pressed down on a
37//  Tab and destroyed when the mouse is released or the drag operation is
38//  aborted. The Tab that the user dragged (the "source tab") owns this object
39//  and must be the only one to destroy it (via |DestroyDragController|).
40//
41///////////////////////////////////////////////////////////////////////////////
42class DraggedTabController : public TabContentsDelegate,
43                             public NotificationObserver,
44                             public MessageLoopForUI::Observer {
45 public:
46  DraggedTabController();
47  virtual ~DraggedTabController();
48
49  // Initializes DraggedTabController to drag the tabs in |tabs| originating
50  // from |source_tabstrip|. |source_tab| is the tab that initiated the drag and
51  // is contained in |tabs|.  |mouse_offset| is the distance of the mouse
52  // pointer from the origin of the first tab in |tabs| and |source_tab_offset|
53  // the offset from |source_tab|. |source_tab_offset| is the horizontal distant
54  // for a horizontal tab strip, and the vertical distance for a vertical tab
55  // strip.
56  void Init(BaseTabStrip* source_tabstrip,
57            BaseTab* source_tab,
58            const std::vector<BaseTab*>& tabs,
59            const gfx::Point& mouse_offset,
60            int source_tab_offset);
61
62  // Returns true if there is a drag underway and the drag is attached to
63  // |tab_strip|.
64  // NOTE: this returns false if the dragged tab controller is in the process
65  // of finishing the drag.
66  static bool IsAttachedTo(BaseTabStrip* tab_strip);
67
68  // Responds to drag events subsequent to StartDrag. If the mouse moves a
69  // sufficient distance before the mouse is released, a drag session is
70  // initiated.
71  void Drag();
72
73  // Complete the current drag session. If the drag session was canceled
74  // because the user pressed Escape or something interrupted it, |canceled|
75  // is true so the helper can revert the state to the world before the drag
76  // begun.
77  void EndDrag(bool canceled);
78
79  // Returns true if a drag started.
80  bool started_drag() const { return started_drag_; }
81
82 private:
83  class DockDisplayer;
84  friend class DockDisplayer;
85
86  typedef std::set<gfx::NativeView> DockWindows;
87
88  // Enumeration of the ways a drag session can end.
89  enum EndDragType {
90    // Drag session exited normally: the user released the mouse.
91    NORMAL,
92
93    // The drag session was canceled (alt-tab during drag, escape ...)
94    CANCELED,
95
96    // The tab (NavigationController) was destroyed during the drag.
97    TAB_DESTROYED
98  };
99
100  // Stores the date associated with a single tab that is being dragged.
101  struct TabDragData {
102    TabDragData();
103    ~TabDragData();
104
105    // The TabContentsWrapper being dragged.
106    TabContentsWrapper* contents;
107
108    // The original TabContentsDelegate of |contents|, before it was detached
109    // from the browser window. We store this so that we can forward certain
110    // delegate notifications back to it if we can't handle them locally.
111    TabContentsDelegate* original_delegate;
112
113    // This is the index of the tab in |source_tabstrip_| when the drag
114    // began. This is used to restore the previous state if the drag is aborted.
115    int source_model_index;
116
117    // If attached this is the tab in |attached_tabstrip_|.
118    BaseTab* attached_tab;
119
120    // Is the tab pinned?
121    bool pinned;
122  };
123
124  typedef std::vector<TabDragData> DragData;
125
126  // Sets |drag_data| from |tab|. This also registers for necessary
127  // notifications and resets the delegate of the TabContentsWrapper.
128  void InitTabDragData(BaseTab* tab, TabDragData* drag_data);
129
130  // Overridden from TabContentsDelegate:
131  virtual void OpenURLFromTab(TabContents* source,
132                              const GURL& url,
133                              const GURL& referrer,
134                              WindowOpenDisposition disposition,
135                              PageTransition::Type transition) OVERRIDE;
136  virtual void NavigationStateChanged(const TabContents* source,
137                                      unsigned changed_flags) OVERRIDE;
138  virtual void AddNewContents(TabContents* source,
139                              TabContents* new_contents,
140                              WindowOpenDisposition disposition,
141                              const gfx::Rect& initial_pos,
142                              bool user_gesture) OVERRIDE;
143  virtual void ActivateContents(TabContents* contents) OVERRIDE;
144  virtual void DeactivateContents(TabContents* contents) OVERRIDE;
145  virtual void LoadingStateChanged(TabContents* source) OVERRIDE;
146  virtual void CloseContents(TabContents* source) OVERRIDE;
147  virtual void MoveContents(TabContents* source,
148                            const gfx::Rect& pos) OVERRIDE;
149  virtual void UpdateTargetURL(TabContents* source, const GURL& url) OVERRIDE;
150  virtual bool ShouldSuppressDialogs() OVERRIDE;
151
152  // Overridden from NotificationObserver:
153  virtual void Observe(NotificationType type,
154                       const NotificationSource& source,
155                       const NotificationDetails& details) OVERRIDE;
156
157  // Overridden from MessageLoop::Observer:
158#if defined(OS_WIN)
159  virtual void WillProcessMessage(const MSG& msg) OVERRIDE;
160  virtual void DidProcessMessage(const MSG& msg) OVERRIDE;
161#else
162  virtual void WillProcessEvent(GdkEvent* event) OVERRIDE;
163  virtual void DidProcessEvent(GdkEvent* event) OVERRIDE;
164#endif
165
166  // Initialize the offset used to calculate the position to create windows
167  // in |GetWindowCreatePoint|. This should only be invoked from |Init|.
168  void InitWindowCreatePoint();
169
170  // Returns the point where a detached window should be created given the
171  // current mouse position.
172  gfx::Point GetWindowCreatePoint() const;
173
174  void UpdateDockInfo(const gfx::Point& screen_point);
175
176  // Saves focus in the window that the drag initiated from. Focus will be
177  // restored appropriately if the drag ends within this same window.
178  void SaveFocus();
179
180  // Restore focus to the View that had focus before the drag was started, if
181  // the drag ends within the same Window as it began.
182  void RestoreFocus();
183
184  // Tests whether the position of the mouse is past a minimum elasticity
185  // threshold required to start a drag.
186  bool CanStartDrag() const;
187
188  // Move the DraggedTabView according to the current mouse screen position,
189  // potentially updating the source and other TabStrips.
190  void ContinueDragging();
191
192  // Handles dragging tabs while the tabs are attached.
193  void MoveAttached(const gfx::Point& screen_point);
194
195  // Handles dragging while the tabs are detached.
196  void MoveDetached(const gfx::Point& screen_point);
197
198  // Returns the compatible TabStrip that is under the specified point (screen
199  // coordinates), or NULL if there is none.
200  BaseTabStrip* GetTabStripForPoint(const gfx::Point& screen_point);
201
202  DockInfo GetDockInfoAtPoint(const gfx::Point& screen_point);
203
204  // Returns the specified |tabstrip| if it contains the specified point
205  // (screen coordinates), NULL if it does not.
206  BaseTabStrip* GetTabStripIfItContains(BaseTabStrip* tabstrip,
207                                        const gfx::Point& screen_point) const;
208
209  // Attach the dragged Tab to the specified TabStrip.
210  void Attach(BaseTabStrip* attached_tabstrip, const gfx::Point& screen_point);
211
212  // Detach the dragged Tab from the current TabStrip.
213  void Detach();
214
215  // Returns the index where the dragged TabContents should be inserted into
216  // |attached_tabstrip_| given the DraggedTabView's bounds |dragged_bounds| in
217  // coordinates relative to |attached_tabstrip_| and has had the mirroring
218  // transformation applied.
219  // NOTE: this is invoked from |Attach| before the tabs have been inserted.
220  int GetInsertionIndexForDraggedBounds(const gfx::Rect& dragged_bounds) const;
221
222  // Retrieve the bounds of the DraggedTabView relative to the attached
223  // TabStrip. |tab_strip_point| is in the attached TabStrip's coordinate
224  // system.
225  gfx::Rect GetDraggedViewTabStripBounds(const gfx::Point& tab_strip_point);
226
227  // Get the position of the dragged tab view relative to the attached tab
228  // strip with the mirroring transform applied.
229  gfx::Point GetAttachedDragPoint(const gfx::Point& screen_point);
230
231  // Finds the Tabs within the specified TabStrip that corresponds to the
232  // TabContents of the dragged tabs. Returns an empty vector if not attached.
233  std::vector<BaseTab*> GetTabsMatchingDraggedContents(BaseTabStrip* tabstrip);
234
235  // Does the work for EndDrag. If we actually started a drag and |how_end| is
236  // not TAB_DESTROYED then one of EndDrag or RevertDrag is invoked.
237  void EndDragImpl(EndDragType how_end);
238
239  // Reverts a cancelled drag operation.
240  void RevertDrag();
241
242  // Reverts the tab at |drag_index| in |drag_data_|.
243  void RevertDragAt(size_t drag_index);
244
245  // Selects the dragged tabs in |model|. Does nothing if there are no longer
246  // any dragged contents (as happens when a TabContents is deleted out from
247  // under us).
248  void ResetSelection(TabStripModel* model);
249
250  // Finishes a succesful drag operation.
251  void CompleteDrag();
252
253  // Resets the delegates of the TabContents.
254  void ResetDelegates();
255
256  // Create the DraggedTabView.
257  void CreateDraggedView(const std::vector<TabRendererData>& data,
258                         const std::vector<gfx::Rect>& renderer_bounds);
259
260  // Utility for getting the mouse position in screen coordinates.
261  gfx::Point GetCursorScreenPoint() const;
262
263  // Returns the bounds (in screen coordinates) of the specified View.
264  gfx::Rect GetViewScreenBounds(views::View* tabstrip) const;
265
266  // Hides the frame for the window that contains the TabStrip the current
267  // drag session was initiated from.
268  void HideFrame();
269
270  // Closes a hidden frame at the end of a drag session.
271  void CleanUpHiddenFrame();
272
273  void DockDisplayerDestroyed(DockDisplayer* controller);
274
275  void BringWindowUnderMouseToFront();
276
277  // Convenience for getting the TabDragData corresponding to the tab the user
278  // started dragging.
279  TabDragData* source_tab_drag_data() {
280    return &(drag_data_[source_tab_index_]);
281  }
282
283  // Convenience for |source_tab_drag_data()->contents|.
284  TabContentsWrapper* source_dragged_contents() {
285    return source_tab_drag_data()->contents;
286  }
287
288  // Returns true if the tabs were originality one after the other in
289  // |source_tabstrip_|.
290  bool AreTabsConsecutive();
291
292  // Returns the TabStripModel for the specified tabstrip.
293  TabStripModel* GetModel(BaseTabStrip* tabstrip) const;
294
295  // Handles registering for notifications.
296  NotificationRegistrar registrar_;
297
298  // The TabStrip the drag originated from.
299  BaseTabStrip* source_tabstrip_;
300
301  // The TabStrip the dragged Tab is currently attached to, or NULL if the
302  // dragged Tab is detached.
303  BaseTabStrip* attached_tabstrip_;
304
305  // The visual representation of the dragged Tab.
306  scoped_ptr<DraggedTabView> view_;
307
308  // The position of the mouse (in screen coordinates) at the start of the drag
309  // operation. This is used to calculate minimum elasticity before a
310  // DraggedTabView is constructed.
311  gfx::Point start_screen_point_;
312
313  // This is the offset of the mouse from the top left of the Tab where
314  // dragging begun. This is used to ensure that the dragged view is always
315  // positioned at the correct location during the drag, and to ensure that the
316  // detached window is created at the right location.
317  gfx::Point mouse_offset_;
318
319  // Offset of the mouse relative to the source tab.
320  int source_tab_offset_;
321
322  // Ratio of the x-coordinate of the |source_tab_offset_| to the width of the
323  // tab. Not used for vertical tabs.
324  float offset_to_width_ratio_;
325
326  // A hint to use when positioning new windows created by detaching Tabs. This
327  // is the distance of the mouse from the top left of the dragged tab as if it
328  // were the distance of the mouse from the top left of the first tab in the
329  // attached TabStrip from the top left of the window.
330  gfx::Point window_create_point_;
331
332  // Location of the first tab in the source tabstrip in screen coordinates.
333  // This is used to calculate window_create_point_.
334  gfx::Point first_source_tab_point_;
335
336  // The bounds of the browser window before the last Tab was detached. When
337  // the last Tab is detached, rather than destroying the frame (which would
338  // abort the drag session), the frame is moved off-screen. If the drag is
339  // aborted (e.g. by the user pressing Esc, or capture being lost), the Tab is
340  // attached to the hidden frame and the frame moved back to these bounds.
341  gfx::Rect restore_bounds_;
342
343  // The last view that had focus in the window containing |source_tab_|. This
344  // is saved so that focus can be restored properly when a drag begins and
345  // ends within this same window.
346  views::View* old_focused_view_;
347
348  // The position along the major axis of the mouse cursor in screen coordinates
349  // at the time of the last re-order event.
350  int last_move_screen_loc_;
351
352  DockInfo dock_info_;
353
354  DockWindows dock_windows_;
355
356  std::vector<DockDisplayer*> dock_controllers_;
357
358  // Timer used to bring the window under the cursor to front. If the user
359  // stops moving the mouse for a brief time over a browser window, it is
360  // brought to front.
361  base::OneShotTimer<DraggedTabController> bring_to_front_timer_;
362
363  // Did the mouse move enough that we started a drag?
364  bool started_drag_;
365
366  // Is the drag active?
367  bool active_;
368
369  DragData drag_data_;
370
371  // Index of the source tab in drag_data_.
372  size_t source_tab_index_;
373
374  // True until |MoveAttached| is invoked once.
375  bool initial_move_;
376
377  DISALLOW_COPY_AND_ASSIGN(DraggedTabController);
378};
379
380#endif  // CHROME_BROWSER_UI_VIEWS_TABS_DRAGGED_TAB_CONTROLLER_H_
381