Home | History | Annotate | Download | only in tabs
      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.
     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