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