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_GTK_TABS_DRAGGED_TAB_CONTROLLER_GTK_H_
6#define CHROME_BROWSER_UI_GTK_TABS_DRAGGED_TAB_CONTROLLER_GTK_H_
7#pragma once
8
9#include <gtk/gtk.h>
10
11#include <set>
12
13#include "base/memory/scoped_ptr.h"
14#include "base/timer.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/base/x/x11_util.h"
20
21class DraggedTabGtk;
22class TabGtk;
23class TabStripGtk;
24class TabContentsWrapper;
25
26class DraggedTabControllerGtk : public NotificationObserver,
27                                public TabContentsDelegate {
28 public:
29  DraggedTabControllerGtk(TabGtk* source_tab, TabStripGtk* source_tabstrip);
30  virtual ~DraggedTabControllerGtk();
31
32  // Capture information needed to be used during a drag session for this
33  // controller's associated source Tab and TabStrip. |mouse_offset| is the
34  // distance of the mouse pointer from the Tab's origin.
35  void CaptureDragInfo(const gfx::Point& mouse_offset);
36
37  // Responds to drag events subsequent to StartDrag. If the mouse moves a
38  // sufficient distance before the mouse is released, a drag session is
39  // initiated.
40  void Drag();
41
42  // Complete the current drag session. If the drag session was canceled
43  // because the user pressed Escape or something interrupted it, |canceled|
44  // is true so the helper can revert the state to the world before the drag
45  // begun. Returns whether the tab has been destroyed.
46  bool EndDrag(bool canceled);
47
48  // Retrieve the source tab if the TabContents specified matches the one being
49  // dragged by this controller, or NULL if the specified TabContents is not
50  // the same as the one being dragged.
51  TabGtk* GetDragSourceTabForContents(TabContents* contents) const;
52
53  // Returns true if the specified tab matches the tab being dragged.
54  bool IsDragSourceTab(const TabGtk* tab) const;
55
56  // Returns true if the specified tab is detached.
57  bool IsTabDetached(const TabGtk* tab) const;
58
59 private:
60  // Enumeration of the ways a drag session can end.
61  enum EndDragType {
62    // Drag session exited normally: the user released the mouse.
63    NORMAL,
64
65    // The drag session was canceled (alt-tab during drag, escape ...)
66    CANCELED,
67
68    // The tab (NavigationController) was destroyed during the drag.
69    TAB_DESTROYED
70  };
71
72  // Overridden from TabContentsDelegate:
73  virtual void OpenURLFromTab(TabContents* source,
74                              const GURL& url,
75                              const GURL& referrer,
76                              WindowOpenDisposition disposition,
77                              PageTransition::Type transition);
78  virtual void NavigationStateChanged(const TabContents* source,
79                                      unsigned changed_flags);
80  virtual void AddNewContents(TabContents* source,
81                              TabContents* new_contents,
82                              WindowOpenDisposition disposition,
83                              const gfx::Rect& initial_pos,
84                              bool user_gesture);
85  virtual void ActivateContents(TabContents* contents);
86  virtual void DeactivateContents(TabContents* contents);
87  virtual void LoadingStateChanged(TabContents* source);
88  virtual void CloseContents(TabContents* source);
89  virtual void MoveContents(TabContents* source, const gfx::Rect& pos);
90  virtual bool IsPopup(const TabContents* source) const;
91  virtual void UpdateTargetURL(TabContents* source, const GURL& url);
92
93  // Overridden from NotificationObserver:
94  virtual void Observe(NotificationType type,
95                       const NotificationSource& source,
96                       const NotificationDetails& details);
97
98  // Initialize the offset used to calculate the position to create windows
99  // in |GetWindowCreatePoint|.
100  void InitWindowCreatePoint();
101
102  // Returns the point where a detached window should be created given the
103  // current mouse position.
104  gfx::Point GetWindowCreatePoint() const;
105
106  // Sets the TabContents being dragged with the specified |new_contents|.
107  void SetDraggedContents(TabContentsWrapper* new_contents);
108
109  // Move the DraggedTabView according to the current mouse screen position,
110  // potentially updating the source and other TabStrips.
111  void ContinueDragging();
112
113  // Handles moving the Tab within a TabStrip as well as updating the View.
114  void MoveTab(const gfx::Point& screen_point);
115
116  // Returns the compatible TabStrip that is under the specified point (screen
117  // coordinates), or NULL if there is none.
118  TabStripGtk* GetTabStripForPoint(const gfx::Point& screen_point);
119
120  // Returns the specified |tabstrip| if it contains the specified point
121  // (screen coordinates), NULL if it does not.
122  TabStripGtk* GetTabStripIfItContains(TabStripGtk* tabstrip,
123                                       const gfx::Point& screen_point) const;
124
125  // Attach the dragged Tab to the specified TabStrip.
126  void Attach(TabStripGtk* attached_tabstrip, const gfx::Point& screen_point);
127
128  // Detach the dragged Tab from the current TabStrip.
129  void Detach();
130
131  // Converts a screen point to a point relative to the tab strip.
132  gfx::Point ConvertScreenPointToTabStripPoint(TabStripGtk* tabstrip,
133                                               const gfx::Point& screen_point);
134
135  // Retrieve the bounds of the DraggedTabGtk, relative to the attached
136  // TabStrip, given location of the dragged tab in screen coordinates.
137  gfx::Rect GetDraggedTabTabStripBounds(const gfx::Point& screen_point);
138
139  // Returns the index where the dragged TabContents should be inserted into
140  // the attached TabStripModel given the DraggedTabView's bounds
141  // |dragged_bounds| in coordinates relative to the attached TabStrip.
142  // |is_tab_attached| is true if the tab has already been added.
143  int GetInsertionIndexForDraggedBounds(const gfx::Rect& dragged_bounds,
144                                        bool is_tab_attached) const;
145
146  // Get the position of the dragged tab relative to the attached tab strip.
147  gfx::Point GetDraggedTabPoint(const gfx::Point& screen_point);
148
149  // Finds the Tab within the specified TabStrip that corresponds to the
150  // dragged TabContents.
151  TabGtk* GetTabMatchingDraggedContents(TabStripGtk* tabstrip) const;
152
153  // Does the work for EndDrag. Returns whether the tab has been destroyed.
154  bool EndDragImpl(EndDragType how_end);
155
156  // If the drag was aborted for some reason, this function is called to un-do
157  // the changes made during the drag operation.
158  void RevertDrag();
159
160  // Finishes the drag operation. Returns true if the drag controller should
161  // be destroyed immediately, false otherwise.
162  bool CompleteDrag();
163
164  // Create the DraggedTabGtk if it does not yet exist.
165  void EnsureDraggedTab();
166
167  // Utility for getting the mouse position in screen coordinates.
168  gfx::Point GetCursorScreenPoint() const;
169
170  // Gets the screen bounds of a tab.
171  static gfx::Rect GetTabScreenBounds(TabGtk* tab);
172
173  // Utility to convert the specified TabStripModel index to something valid
174  // for the attached TabStrip.
175  int NormalizeIndexToAttachedTabStrip(int index) const;
176
177  // Hides the window that contains the tab strip the current drag session was
178  // initiated from.
179  void HideWindow();
180
181  // Presents the window that was hidden by HideWindow.
182  void ShowWindow();
183
184  // Closes a hidden frame at the end of a drag session.
185  void CleanUpHiddenFrame();
186
187  // Cleans up a source tab that is no longer used.
188  void CleanUpSourceTab();
189
190  // Completes the drag session after the view has animated to its final
191  // position.
192  void OnAnimateToBoundsComplete();
193
194  // Activates whichever window is under the mouse.
195  void BringWindowUnderMouseToFront();
196
197  // Handles registering for notifications.
198  NotificationRegistrar registrar_;
199
200  // The TabContents being dragged.
201  TabContentsWrapper* dragged_contents_;
202
203  // The original TabContentsDelegate of |dragged_contents_|, before it was
204  // detached from the browser window. We store this so that we can forward
205  // certain delegate notifications back to it if we can't handle them locally.
206  TabContentsDelegate* original_delegate_;
207
208  // The tab that initiated the drag session.
209  TabGtk* source_tab_;
210
211  // The tab strip |source_tab_| originated from.
212  TabStripGtk* source_tabstrip_;
213
214  // This is the index of the |source_tab_| in |source_tabstrip_| when the drag
215  // began. This is used to restore the previous state if the drag is aborted.
216  int source_model_index_;
217
218  // The TabStrip the dragged Tab is currently attached to, or NULL if the
219  // dragged Tab is detached.
220  TabStripGtk* attached_tabstrip_;
221
222  // The visual representation of the dragged Tab.
223  scoped_ptr<DraggedTabGtk> dragged_tab_;
224
225  // The position of the mouse (in screen coordinates) at the start of the drag
226  // operation. This is used to calculate minimum elasticity before a
227  // DraggedTabView is constructed.
228  gfx::Point start_screen_point_;
229
230  // This is the offset of the mouse from the top left of the Tab where
231  // dragging begun. This is used to ensure that the dragged view is always
232  // positioned at the correct location during the drag, and to ensure that the
233  // detached window is created at the right location.
234  gfx::Point mouse_offset_;
235
236  // A hint to use when positioning new windows created by detaching Tabs. This
237  // is the distance of the mouse from the top left of the dragged tab as if it
238  // were the distance of the mouse from the top left of the first tab in the
239  // attached TabStrip from the top left of the window.
240  gfx::Point window_create_point_;
241
242  // Whether we're in the destructor or not.  Makes sure we don't destroy the
243  // drag controller more than once.
244  bool in_destructor_;
245
246  // The horizontal position of the mouse cursor in screen coordinates at the
247  // time of the last re-order event.
248  int last_move_screen_x_;
249
250  // DockInfo for the tabstrip.
251  DockInfo dock_info_;
252
253  typedef std::set<GtkWidget*> DockWindows;
254  DockWindows dock_windows_;
255
256  // Is the tab mini?
257  const bool mini_;
258
259  // Is the tab pinned?
260  const bool pinned_;
261
262  // Timer used to bring the window under the cursor to front. If the user
263  // stops moving the mouse for a brief time over a browser window, it is
264  // brought to front.
265  base::OneShotTimer<DraggedTabControllerGtk> bring_to_front_timer_;
266
267  DISALLOW_COPY_AND_ASSIGN(DraggedTabControllerGtk);
268};
269
270#endif  // CHROME_BROWSER_UI_GTK_TABS_DRAGGED_TAB_CONTROLLER_GTK_H_
271