• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 namespace views {
22 class View;
23 }
24 class BaseTab;
25 class BaseTabStrip;
26 class DraggedTabView;
27 class TabStripModel;
28 
29 struct 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 ///////////////////////////////////////////////////////////////////////////////
42 class 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.
started_drag()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.
source_tab_drag_data()279   TabDragData* source_tab_drag_data() {
280     return &(drag_data_[source_tab_index_]);
281   }
282 
283   // Convenience for |source_tab_drag_data()->contents|.
source_dragged_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