// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_TABS_DRAGGED_TAB_CONTROLLER_H_
#define CHROME_BROWSER_UI_VIEWS_TABS_DRAGGED_TAB_CONTROLLER_H_
#pragma once
#include <vector>
#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/timer.h"
#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
#include "chrome/browser/ui/tabs/dock_info.h"
#include "content/browser/tab_contents/tab_contents_delegate.h"
#include "content/common/notification_observer.h"
#include "content/common/notification_registrar.h"
#include "ui/gfx/rect.h"
namespace views {
class View;
}
class BaseTab;
class BaseTabStrip;
class DraggedTabView;
class TabStripModel;
struct TabRendererData;
///////////////////////////////////////////////////////////////////////////////
//
// DraggedTabController
//
// An object that handles a drag session for an individual Tab within a
// TabStrip. This object is created whenever the mouse is pressed down on a
// Tab and destroyed when the mouse is released or the drag operation is
// aborted. The Tab that the user dragged (the "source tab") owns this object
// and must be the only one to destroy it (via |DestroyDragController|).
//
///////////////////////////////////////////////////////////////////////////////
class DraggedTabController : public TabContentsDelegate,
public NotificationObserver,
public MessageLoopForUI::Observer {
public:
DraggedTabController();
virtual ~DraggedTabController();
// Initializes DraggedTabController to drag the tabs in |tabs| originating
// from |source_tabstrip|. |source_tab| is the tab that initiated the drag and
// is contained in |tabs|. |mouse_offset| is the distance of the mouse
// pointer from the origin of the first tab in |tabs| and |source_tab_offset|
// the offset from |source_tab|. |source_tab_offset| is the horizontal distant
// for a horizontal tab strip, and the vertical distance for a vertical tab
// strip.
void Init(BaseTabStrip* source_tabstrip,
BaseTab* source_tab,
const std::vector<BaseTab*>& tabs,
const gfx::Point& mouse_offset,
int source_tab_offset);
// Returns true if there is a drag underway and the drag is attached to
// |tab_strip|.
// NOTE: this returns false if the dragged tab controller is in the process
// of finishing the drag.
static bool IsAttachedTo(BaseTabStrip* tab_strip);
// Responds to drag events subsequent to StartDrag. If the mouse moves a
// sufficient distance before the mouse is released, a drag session is
// initiated.
void Drag();
// Complete the current drag session. If the drag session was canceled
// because the user pressed Escape or something interrupted it, |canceled|
// is true so the helper can revert the state to the world before the drag
// begun.
void EndDrag(bool canceled);
// Returns true if a drag started.
bool started_drag() const { return started_drag_; }
private:
class DockDisplayer;
friend class DockDisplayer;
typedef std::set<gfx::NativeView> DockWindows;
// Enumeration of the ways a drag session can end.
enum EndDragType {
// Drag session exited normally: the user released the mouse.
NORMAL,
// The drag session was canceled (alt-tab during drag, escape ...)
CANCELED,
// The tab (NavigationController) was destroyed during the drag.
TAB_DESTROYED
};
// Stores the date associated with a single tab that is being dragged.
struct TabDragData {
TabDragData();
~TabDragData();
// The TabContentsWrapper being dragged.
TabContentsWrapper* contents;
// The original TabContentsDelegate of |contents|, before it was detached
// from the browser window. We store this so that we can forward certain
// delegate notifications back to it if we can't handle them locally.
TabContentsDelegate* original_delegate;
// This is the index of the tab in |source_tabstrip_| when the drag
// began. This is used to restore the previous state if the drag is aborted.
int source_model_index;
// If attached this is the tab in |attached_tabstrip_|.
BaseTab* attached_tab;
// Is the tab pinned?
bool pinned;
};
typedef std::vector<TabDragData> DragData;
// Sets |drag_data| from |tab|. This also registers for necessary
// notifications and resets the delegate of the TabContentsWrapper.
void InitTabDragData(BaseTab* tab, TabDragData* drag_data);
// Overridden from TabContentsDelegate:
virtual void OpenURLFromTab(TabContents* source,
const GURL& url,
const GURL& referrer,
WindowOpenDisposition disposition,
PageTransition::Type transition) OVERRIDE;
virtual void NavigationStateChanged(const TabContents* source,
unsigned changed_flags) OVERRIDE;
virtual void AddNewContents(TabContents* source,
TabContents* new_contents,
WindowOpenDisposition disposition,
const gfx::Rect& initial_pos,
bool user_gesture) OVERRIDE;
virtual void ActivateContents(TabContents* contents) OVERRIDE;
virtual void DeactivateContents(TabContents* contents) OVERRIDE;
virtual void LoadingStateChanged(TabContents* source) OVERRIDE;
virtual void CloseContents(TabContents* source) OVERRIDE;
virtual void MoveContents(TabContents* source,
const gfx::Rect& pos) OVERRIDE;
virtual void UpdateTargetURL(TabContents* source, const GURL& url) OVERRIDE;
virtual bool ShouldSuppressDialogs() OVERRIDE;
// Overridden from NotificationObserver:
virtual void Observe(NotificationType type,
const NotificationSource& source,
const NotificationDetails& details) OVERRIDE;
// Overridden from MessageLoop::Observer:
#if defined(OS_WIN)
virtual void WillProcessMessage(const MSG& msg) OVERRIDE;
virtual void DidProcessMessage(const MSG& msg) OVERRIDE;
#else
virtual void WillProcessEvent(GdkEvent* event) OVERRIDE;
virtual void DidProcessEvent(GdkEvent* event) OVERRIDE;
#endif
// Initialize the offset used to calculate the position to create windows
// in |GetWindowCreatePoint|. This should only be invoked from |Init|.
void InitWindowCreatePoint();
// Returns the point where a detached window should be created given the
// current mouse position.
gfx::Point GetWindowCreatePoint() const;
void UpdateDockInfo(const gfx::Point& screen_point);
// Saves focus in the window that the drag initiated from. Focus will be
// restored appropriately if the drag ends within this same window.
void SaveFocus();
// Restore focus to the View that had focus before the drag was started, if
// the drag ends within the same Window as it began.
void RestoreFocus();
// Tests whether the position of the mouse is past a minimum elasticity
// threshold required to start a drag.
bool CanStartDrag() const;
// Move the DraggedTabView according to the current mouse screen position,
// potentially updating the source and other TabStrips.
void ContinueDragging();
// Handles dragging tabs while the tabs are attached.
void MoveAttached(const gfx::Point& screen_point);
// Handles dragging while the tabs are detached.
void MoveDetached(const gfx::Point& screen_point);
// Returns the compatible TabStrip that is under the specified point (screen
// coordinates), or NULL if there is none.
BaseTabStrip* GetTabStripForPoint(const gfx::Point& screen_point);
DockInfo GetDockInfoAtPoint(const gfx::Point& screen_point);
// Returns the specified |tabstrip| if it contains the specified point
// (screen coordinates), NULL if it does not.
BaseTabStrip* GetTabStripIfItContains(BaseTabStrip* tabstrip,
const gfx::Point& screen_point) const;
// Attach the dragged Tab to the specified TabStrip.
void Attach(BaseTabStrip* attached_tabstrip, const gfx::Point& screen_point);
// Detach the dragged Tab from the current TabStrip.
void Detach();
// Returns the index where the dragged TabContents should be inserted into
// |attached_tabstrip_| given the DraggedTabView's bounds |dragged_bounds| in
// coordinates relative to |attached_tabstrip_| and has had the mirroring
// transformation applied.
// NOTE: this is invoked from |Attach| before the tabs have been inserted.
int GetInsertionIndexForDraggedBounds(const gfx::Rect& dragged_bounds) const;
// Retrieve the bounds of the DraggedTabView relative to the attached
// TabStrip. |tab_strip_point| is in the attached TabStrip's coordinate
// system.
gfx::Rect GetDraggedViewTabStripBounds(const gfx::Point& tab_strip_point);
// Get the position of the dragged tab view relative to the attached tab
// strip with the mirroring transform applied.
gfx::Point GetAttachedDragPoint(const gfx::Point& screen_point);
// Finds the Tabs within the specified TabStrip that corresponds to the
// TabContents of the dragged tabs. Returns an empty vector if not attached.
std::vector<BaseTab*> GetTabsMatchingDraggedContents(BaseTabStrip* tabstrip);
// Does the work for EndDrag. If we actually started a drag and |how_end| is
// not TAB_DESTROYED then one of EndDrag or RevertDrag is invoked.
void EndDragImpl(EndDragType how_end);
// Reverts a cancelled drag operation.
void RevertDrag();
// Reverts the tab at |drag_index| in |drag_data_|.
void RevertDragAt(size_t drag_index);
// Selects the dragged tabs in |model|. Does nothing if there are no longer
// any dragged contents (as happens when a TabContents is deleted out from
// under us).
void ResetSelection(TabStripModel* model);
// Finishes a succesful drag operation.
void CompleteDrag();
// Resets the delegates of the TabContents.
void ResetDelegates();
// Create the DraggedTabView.
void CreateDraggedView(const std::vector<TabRendererData>& data,
const std::vector<gfx::Rect>& renderer_bounds);
// Utility for getting the mouse position in screen coordinates.
gfx::Point GetCursorScreenPoint() const;
// Returns the bounds (in screen coordinates) of the specified View.
gfx::Rect GetViewScreenBounds(views::View* tabstrip) const;
// Hides the frame for the window that contains the TabStrip the current
// drag session was initiated from.
void HideFrame();
// Closes a hidden frame at the end of a drag session.
void CleanUpHiddenFrame();
void DockDisplayerDestroyed(DockDisplayer* controller);
void BringWindowUnderMouseToFront();
// Convenience for getting the TabDragData corresponding to the tab the user
// started dragging.
TabDragData* source_tab_drag_data() {
return &(drag_data_[source_tab_index_]);
}
// Convenience for |source_tab_drag_data()->contents|.
TabContentsWrapper* source_dragged_contents() {
return source_tab_drag_data()->contents;
}
// Returns true if the tabs were originality one after the other in
// |source_tabstrip_|.
bool AreTabsConsecutive();
// Returns the TabStripModel for the specified tabstrip.
TabStripModel* GetModel(BaseTabStrip* tabstrip) const;
// Handles registering for notifications.
NotificationRegistrar registrar_;
// The TabStrip the drag originated from.
BaseTabStrip* source_tabstrip_;
// The TabStrip the dragged Tab is currently attached to, or NULL if the
// dragged Tab is detached.
BaseTabStrip* attached_tabstrip_;
// The visual representation of the dragged Tab.
scoped_ptr<DraggedTabView> view_;
// The position of the mouse (in screen coordinates) at the start of the drag
// operation. This is used to calculate minimum elasticity before a
// DraggedTabView is constructed.
gfx::Point start_screen_point_;
// This is the offset of the mouse from the top left of the Tab where
// dragging begun. This is used to ensure that the dragged view is always
// positioned at the correct location during the drag, and to ensure that the
// detached window is created at the right location.
gfx::Point mouse_offset_;
// Offset of the mouse relative to the source tab.
int source_tab_offset_;
// Ratio of the x-coordinate of the |source_tab_offset_| to the width of the
// tab. Not used for vertical tabs.
float offset_to_width_ratio_;
// A hint to use when positioning new windows created by detaching Tabs. This
// is the distance of the mouse from the top left of the dragged tab as if it
// were the distance of the mouse from the top left of the first tab in the
// attached TabStrip from the top left of the window.
gfx::Point window_create_point_;
// Location of the first tab in the source tabstrip in screen coordinates.
// This is used to calculate window_create_point_.
gfx::Point first_source_tab_point_;
// The bounds of the browser window before the last Tab was detached. When
// the last Tab is detached, rather than destroying the frame (which would
// abort the drag session), the frame is moved off-screen. If the drag is
// aborted (e.g. by the user pressing Esc, or capture being lost), the Tab is
// attached to the hidden frame and the frame moved back to these bounds.
gfx::Rect restore_bounds_;
// The last view that had focus in the window containing |source_tab_|. This
// is saved so that focus can be restored properly when a drag begins and
// ends within this same window.
views::View* old_focused_view_;
// The position along the major axis of the mouse cursor in screen coordinates
// at the time of the last re-order event.
int last_move_screen_loc_;
DockInfo dock_info_;
DockWindows dock_windows_;
std::vector<DockDisplayer*> dock_controllers_;
// Timer used to bring the window under the cursor to front. If the user
// stops moving the mouse for a brief time over a browser window, it is
// brought to front.
base::OneShotTimer<DraggedTabController> bring_to_front_timer_;
// Did the mouse move enough that we started a drag?
bool started_drag_;
// Is the drag active?
bool active_;
DragData drag_data_;
// Index of the source tab in drag_data_.
size_t source_tab_index_;
// True until |MoveAttached| is invoked once.
bool initial_move_;
DISALLOW_COPY_AND_ASSIGN(DraggedTabController);
};
#endif // CHROME_BROWSER_UI_VIEWS_TABS_DRAGGED_TAB_CONTROLLER_H_