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