diff options
Diffstat (limited to 'chrome/browser/views/tabs/dragged_tab_controller.cc')
-rw-r--r-- | chrome/browser/views/tabs/dragged_tab_controller.cc | 1341 |
1 files changed, 0 insertions, 1341 deletions
diff --git a/chrome/browser/views/tabs/dragged_tab_controller.cc b/chrome/browser/views/tabs/dragged_tab_controller.cc deleted file mode 100644 index 3798180..0000000 --- a/chrome/browser/views/tabs/dragged_tab_controller.cc +++ /dev/null @@ -1,1341 +0,0 @@ -// Copyright (c) 2010 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. - -#include "chrome/browser/views/tabs/dragged_tab_controller.h" - -#include <math.h> -#include <set> - -#include "app/animation.h" -#include "app/slide_animation.h" -#include "app/resource_bundle.h" -#include "base/callback.h" -#include "base/i18n/rtl.h" -#include "chrome/browser/browser_window.h" -#include "chrome/browser/extensions/extension_function_dispatcher.h" -#include "chrome/browser/tab_contents/tab_contents.h" -#include "chrome/browser/tabs/tab_strip_model.h" -#include "chrome/browser/metrics/user_metrics.h" -#include "chrome/browser/views/frame/browser_view.h" -#include "chrome/browser/views/tabs/base_tab.h" -#include "chrome/browser/views/tabs/base_tab_strip.h" -#include "chrome/browser/views/tabs/browser_tab_strip_controller.h" -#include "chrome/browser/views/tabs/dragged_tab_view.h" -#include "chrome/browser/views/tabs/native_view_photobooth.h" -#include "chrome/browser/views/tabs/side_tab.h" -#include "chrome/browser/views/tabs/side_tab_strip.h" -#include "chrome/browser/views/tabs/tab.h" -#include "chrome/browser/views/tabs/tab_strip.h" -#include "chrome/common/notification_service.h" -#include "gfx/canvas_skia.h" -#include "grit/theme_resources.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "views/event.h" -#include "views/widget/root_view.h" -#include "views/widget/widget.h" -#include "views/window/window.h" - -#if defined(OS_WIN) -#include "views/widget/widget_win.h" -#endif - -#if defined(OS_LINUX) -#include <gdk/gdk.h> -#include <gdk/gdkkeysyms.h> -#endif - -static const int kHorizontalMoveThreshold = 16; // Pixels. - -// Distance in pixels the user must move the mouse before we consider moving -// an attached vertical tab. -static const int kVerticalMoveThreshold = 8; - -// If non-null there is a drag underway. -static DraggedTabController* instance_; - -namespace { - -// Delay, in ms, during dragging before we bring a window to front. -const int kBringToFrontDelay = 750; - -// Radius of the rect drawn by DockView. -const int kRoundedRectRadius = 4; - -// Spacing between tab icons when DockView is showing a docking location that -// contains more than one tab. -const int kTabSpacing = 4; - -// DockView is the view responsible for giving a visual indicator of where a -// dock is going to occur. - -class DockView : public views::View { - public: - explicit DockView(DockInfo::Type type) : type_(type) {} - - virtual gfx::Size GetPreferredSize() { - return gfx::Size(DockInfo::popup_width(), DockInfo::popup_height()); - } - - virtual void PaintBackground(gfx::Canvas* canvas) { - SkRect outer_rect = { SkIntToScalar(0), SkIntToScalar(0), - SkIntToScalar(width()), - SkIntToScalar(height()) }; - - // Fill the background rect. - SkPaint paint; - paint.setColor(SkColorSetRGB(108, 108, 108)); - paint.setStyle(SkPaint::kFill_Style); - canvas->AsCanvasSkia()->drawRoundRect( - outer_rect, SkIntToScalar(kRoundedRectRadius), - SkIntToScalar(kRoundedRectRadius), paint); - - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - - SkBitmap* high_icon = rb.GetBitmapNamed(IDR_DOCK_HIGH); - SkBitmap* wide_icon = rb.GetBitmapNamed(IDR_DOCK_WIDE); - - canvas->Save(); - bool rtl_ui = base::i18n::IsRTL(); - if (rtl_ui) { - // Flip canvas to draw the mirrored tab images for RTL UI. - canvas->TranslateInt(width(), 0); - canvas->ScaleInt(-1, 1); - } - int x_of_active_tab = width() / 2 + kTabSpacing / 2; - int x_of_inactive_tab = width() / 2 - high_icon->width() - kTabSpacing / 2; - switch (type_) { - case DockInfo::LEFT_OF_WINDOW: - case DockInfo::LEFT_HALF: - if (!rtl_ui) - std::swap(x_of_active_tab, x_of_inactive_tab); - canvas->DrawBitmapInt(*high_icon, x_of_active_tab, - (height() - high_icon->height()) / 2); - if (type_ == DockInfo::LEFT_OF_WINDOW) { - DrawBitmapWithAlpha(canvas, *high_icon, x_of_inactive_tab, - (height() - high_icon->height()) / 2); - } - break; - - - case DockInfo::RIGHT_OF_WINDOW: - case DockInfo::RIGHT_HALF: - if (rtl_ui) - std::swap(x_of_active_tab, x_of_inactive_tab); - canvas->DrawBitmapInt(*high_icon, x_of_active_tab, - (height() - high_icon->height()) / 2); - if (type_ == DockInfo::RIGHT_OF_WINDOW) { - DrawBitmapWithAlpha(canvas, *high_icon, x_of_inactive_tab, - (height() - high_icon->height()) / 2); - } - break; - - case DockInfo::TOP_OF_WINDOW: - canvas->DrawBitmapInt(*wide_icon, (width() - wide_icon->width()) / 2, - height() / 2 - high_icon->height()); - break; - - case DockInfo::MAXIMIZE: { - SkBitmap* max_icon = rb.GetBitmapNamed(IDR_DOCK_MAX); - canvas->DrawBitmapInt(*max_icon, (width() - max_icon->width()) / 2, - (height() - max_icon->height()) / 2); - break; - } - - case DockInfo::BOTTOM_HALF: - case DockInfo::BOTTOM_OF_WINDOW: - canvas->DrawBitmapInt(*wide_icon, (width() - wide_icon->width()) / 2, - height() / 2 + kTabSpacing / 2); - if (type_ == DockInfo::BOTTOM_OF_WINDOW) { - DrawBitmapWithAlpha(canvas, *wide_icon, - (width() - wide_icon->width()) / 2, - height() / 2 - kTabSpacing / 2 - wide_icon->height()); - } - break; - - default: - NOTREACHED(); - break; - } - canvas->Restore(); - } - - private: - void DrawBitmapWithAlpha(gfx::Canvas* canvas, const SkBitmap& image, - int x, int y) { - SkPaint paint; - paint.setAlpha(128); - canvas->DrawBitmapInt(image, x, y, paint); - } - - DockInfo::Type type_; - - DISALLOW_COPY_AND_ASSIGN(DockView); -}; - -gfx::Point ConvertScreenPointToTabStripPoint(BaseTabStrip* tabstrip, - const gfx::Point& screen_point) { - gfx::Point tabstrip_topleft; - views::View::ConvertPointToScreen(tabstrip, &tabstrip_topleft); - return gfx::Point(screen_point.x() - tabstrip_topleft.x(), - screen_point.y() - tabstrip_topleft.y()); -} - -// Returns the the x-coordinate of |point| if the type of tabstrip is horizontal -// otherwise returns the y-coordinate. -int MajorAxisValue(const gfx::Point& point, BaseTabStrip* tabstrip) { - return (tabstrip->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) ? - point.x() : point.y(); -} - -} // namespace - -/////////////////////////////////////////////////////////////////////////////// -// DockDisplayer - -// DockDisplayer is responsible for giving the user a visual indication of a -// possible dock position (as represented by DockInfo). DockDisplayer shows -// a window with a DockView in it. Two animations are used that correspond to -// the state of DockInfo::in_enable_area. -class DraggedTabController::DockDisplayer : public AnimationDelegate { - public: - DockDisplayer(DraggedTabController* controller, - const DockInfo& info) - : controller_(controller), - popup_(NULL), - popup_view_(NULL), - ALLOW_THIS_IN_INITIALIZER_LIST(animation_(this)), - hidden_(false), - in_enable_area_(info.in_enable_area()) { -#if defined(OS_WIN) - views::WidgetWin* popup = new views::WidgetWin; - popup_ = popup; - popup->set_window_style(WS_POPUP); - popup->set_window_ex_style(WS_EX_LAYERED | WS_EX_TOOLWINDOW | - WS_EX_TOPMOST); - popup->SetOpacity(0x00); - popup->Init(NULL, info.GetPopupRect()); - popup->SetContentsView(new DockView(info.type())); - if (info.in_enable_area()) - animation_.Reset(1); - else - animation_.Show(); - popup->SetWindowPos(HWND_TOP, 0, 0, 0, 0, - SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOMOVE | SWP_SHOWWINDOW); -#else - NOTIMPLEMENTED(); -#endif - popup_view_ = popup_->GetNativeView(); - } - - ~DockDisplayer() { - if (controller_) - controller_->DockDisplayerDestroyed(this); - } - - // Updates the state based on |in_enable_area|. - void UpdateInEnabledArea(bool in_enable_area) { - if (in_enable_area != in_enable_area_) { - in_enable_area_ = in_enable_area; - UpdateLayeredAlpha(); - } - } - - // Resets the reference to the hosting DraggedTabController. This is invoked - // when the DraggedTabController is destoryed. - void clear_controller() { controller_ = NULL; } - - // NativeView of the window we create. - gfx::NativeView popup_view() { return popup_view_; } - - // Starts the hide animation. When the window is closed the - // DraggedTabController is notified by way of the DockDisplayerDestroyed - // method - void Hide() { - if (hidden_) - return; - - if (!popup_) { - delete this; - return; - } - hidden_ = true; - animation_.Hide(); - } - - virtual void AnimationProgressed(const Animation* animation) { - UpdateLayeredAlpha(); - } - - virtual void AnimationEnded(const Animation* animation) { - if (!hidden_) - return; -#if defined(OS_WIN) - static_cast<views::WidgetWin*>(popup_)->Close(); -#else - NOTIMPLEMENTED(); -#endif - delete this; - } - - virtual void UpdateLayeredAlpha() { -#if defined(OS_WIN) - double scale = in_enable_area_ ? 1 : .5; - static_cast<views::WidgetWin*>(popup_)->SetOpacity( - static_cast<BYTE>(animation_.GetCurrentValue() * scale * 255.0)); - popup_->GetRootView()->SchedulePaint(); -#else - NOTIMPLEMENTED(); -#endif - } - - private: - // DraggedTabController that created us. - DraggedTabController* controller_; - - // Window we're showing. - views::Widget* popup_; - - // NativeView of |popup_|. We cache this to avoid the possibility of - // invoking a method on popup_ after we close it. - gfx::NativeView popup_view_; - - // Animation for when first made visible. - SlideAnimation animation_; - - // Have we been hidden? - bool hidden_; - - // Value of DockInfo::in_enable_area. - bool in_enable_area_; -}; - -/////////////////////////////////////////////////////////////////////////////// -// DraggedTabController, public: - -DraggedTabController::DraggedTabController(BaseTab* source_tab, - BaseTabStrip* source_tabstrip) - : dragged_contents_(NULL), - original_delegate_(NULL), - source_tabstrip_(source_tabstrip), - source_model_index_(source_tabstrip->GetModelIndexOfBaseTab(source_tab)), - attached_tabstrip_(NULL), - attached_tab_(NULL), - offset_to_width_ratio_(0), - old_focused_view_(NULL), - last_move_screen_loc_(0), - mini_(source_tab->data().mini), - pinned_(source_tabstrip->IsTabPinned(source_tab)), - started_drag_(false), - active_(true) { - instance_ = this; - SetDraggedContents( - GetModel(source_tabstrip_)->GetTabContentsAt(source_model_index_)); - // Listen for Esc key presses. - MessageLoopForUI::current()->AddObserver(this); -} - -DraggedTabController::~DraggedTabController() { - if (instance_ == this) - instance_ = NULL; - - MessageLoopForUI::current()->RemoveObserver(this); - // Need to delete the view here manually _before_ we reset the dragged - // contents to NULL, otherwise if the view is animating to its destination - // bounds, it won't be able to clean up properly since its cleanup routine - // uses GetIndexForDraggedContents, which will be invalid. - view_.reset(NULL); - SetDraggedContents(NULL); // This removes our observer. -} - -// static -bool DraggedTabController::IsAttachedTo(BaseTabStrip* tab_strip) { - return instance_ && instance_->active_ && - instance_->attached_tabstrip_ == tab_strip; -} - -void DraggedTabController::CaptureDragInfo(views::View* tab, - const gfx::Point& mouse_offset) { - if (tab->width() > 0) { - offset_to_width_ratio_ = static_cast<float>(mouse_offset.x()) / - static_cast<float>(tab->width()); - } - start_screen_point_ = GetCursorScreenPoint(); - mouse_offset_ = mouse_offset; - InitWindowCreatePoint(); -} - -void DraggedTabController::Drag() { - bring_to_front_timer_.Stop(); - - // Before we get to dragging anywhere, ensure that we consider ourselves - // attached to the source tabstrip. - if (!started_drag_ && CanStartDrag()) { - started_drag_ = true; - SaveFocus(); - Attach(source_tabstrip_, gfx::Point()); - } - - if (started_drag_) - ContinueDragging(); -} - -void DraggedTabController::EndDrag(bool canceled) { - EndDragImpl(canceled ? CANCELED : NORMAL); -} - -/////////////////////////////////////////////////////////////////////////////// -// DraggedTabController, PageNavigator implementation: - -void DraggedTabController::OpenURLFromTab(TabContents* source, - const GURL& url, - const GURL& referrer, - WindowOpenDisposition disposition, - PageTransition::Type transition) { - if (original_delegate_) { - if (disposition == CURRENT_TAB) - disposition = NEW_WINDOW; - - original_delegate_->OpenURLFromTab(source, url, referrer, - disposition, transition); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// DraggedTabController, TabContentsDelegate implementation: - -void DraggedTabController::NavigationStateChanged(const TabContents* source, - unsigned changed_flags) { - if (view_.get()) - view_->Update(); -} - -void DraggedTabController::AddNewContents(TabContents* source, - TabContents* new_contents, - WindowOpenDisposition disposition, - const gfx::Rect& initial_pos, - bool user_gesture) { - DCHECK(disposition != CURRENT_TAB); - - // Theoretically could be called while dragging if the page tries to - // spawn a window. Route this message back to the browser in most cases. - if (original_delegate_) { - original_delegate_->AddNewContents(source, new_contents, disposition, - initial_pos, user_gesture); - } -} - -void DraggedTabController::ActivateContents(TabContents* contents) { - // Ignored. -} - -void DraggedTabController::DeactivateContents(TabContents* contents) { - // Ignored. -} - -void DraggedTabController::LoadingStateChanged(TabContents* source) { - // It would be nice to respond to this message by changing the - // screen shot in the dragged tab. - if (view_.get()) - view_->Update(); -} - -void DraggedTabController::CloseContents(TabContents* source) { - // Theoretically could be called by a window. Should be ignored - // because window.close() is ignored (usually, even though this - // method gets called.) -} - -void DraggedTabController::MoveContents(TabContents* source, - const gfx::Rect& pos) { - // Theoretically could be called by a web page trying to move its - // own window. Should be ignored since we're moving the window... -} - -void DraggedTabController::ToolbarSizeChanged(TabContents* source, - bool finished) { - // Dragged tabs don't care about this. -} - -void DraggedTabController::URLStarredChanged(TabContents* source, - bool starred) { - // Ignored. -} - -void DraggedTabController::UpdateTargetURL(TabContents* source, - const GURL& url) { - // Ignored. -} - -/////////////////////////////////////////////////////////////////////////////// -// DraggedTabController, NotificationObserver implementation: - -void DraggedTabController::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED); - DCHECK(Source<TabContents>(source).ptr() == dragged_contents_); - EndDragImpl(TAB_DESTROYED); -} - -/////////////////////////////////////////////////////////////////////////////// -// DraggedTabController, MessageLoop::Observer implementation: - -#if defined(OS_WIN) -void DraggedTabController::WillProcessMessage(const MSG& msg) { -} - -void DraggedTabController::DidProcessMessage(const MSG& msg) { - // If the user presses ESC during a drag, we need to abort and revert things - // to the way they were. This is the most reliable way to do this since no - // single view or window reliably receives events throughout all the various - // kinds of tab dragging. - if (msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE) - EndDrag(true); -} -#else -void DraggedTabController::WillProcessEvent(GdkEvent* event) { -} - -void DraggedTabController::DidProcessEvent(GdkEvent* event) { - if (event->type == GDK_KEY_PRESS && - reinterpret_cast<GdkEventKey*>(event)->keyval == GDK_Escape) { - EndDrag(true); - } -} -#endif - -/////////////////////////////////////////////////////////////////////////////// -// DraggedTabController, private: - -void DraggedTabController::InitWindowCreatePoint() { - // window_create_point_ is only used in CompleteDrag() (through - // GetWindowCreatePoint() to get the start point of the docked window) when - // the attached_tabstrip_ is NULL and all the window's related bound - // information are obtained from source_tabstrip_. So, we need to get the - // first_tab based on source_tabstrip_, not attached_tabstrip_. Otherwise, - // the window_create_point_ is not in the correct coordinate system. Please - // refer to http://crbug.com/6223 comment #15 for detailed information. - views::View* first_tab = source_tabstrip_->base_tab_at_tab_index(0); - views::View::ConvertPointToWidget(first_tab, &first_source_tab_point_); - UpdateWindowCreatePoint(); -} - -void DraggedTabController::UpdateWindowCreatePoint() { - // See comments in InitWindowCreatePoint for details on this. - window_create_point_ = first_source_tab_point_; - window_create_point_.Offset(mouse_offset_.x(), mouse_offset_.y()); -} - -gfx::Point DraggedTabController::GetWindowCreatePoint() const { - gfx::Point cursor_point = GetCursorScreenPoint(); - if (dock_info_.type() != DockInfo::NONE) { - // If we're going to dock, we need to return the exact coordinate, - // otherwise we may attempt to maximize on the wrong monitor. - return cursor_point; - } - return gfx::Point(cursor_point.x() - window_create_point_.x(), - cursor_point.y() - window_create_point_.y()); -} - -void DraggedTabController::UpdateDockInfo(const gfx::Point& screen_point) { - // Update the DockInfo for the current mouse coordinates. - DockInfo dock_info = GetDockInfoAtPoint(screen_point); - if (source_tabstrip_->type() == BaseTabStrip::VERTICAL_TAB_STRIP && - ((dock_info.type() == DockInfo::LEFT_OF_WINDOW && - !base::i18n::IsRTL()) || - (dock_info.type() == DockInfo::RIGHT_OF_WINDOW && - base::i18n::IsRTL()))) { - // For side tabs it's way to easy to trigger to docking along the left/right - // edge, so we disable it. - dock_info = DockInfo(); - } - if (!dock_info.equals(dock_info_)) { - // DockInfo for current position differs. - if (dock_info_.type() != DockInfo::NONE && - !dock_controllers_.empty()) { - // Hide old visual indicator. - dock_controllers_.back()->Hide(); - } - dock_info_ = dock_info; - if (dock_info_.type() != DockInfo::NONE) { - // Show new docking position. - DockDisplayer* controller = new DockDisplayer(this, dock_info_); - if (controller->popup_view()) { - dock_controllers_.push_back(controller); - dock_windows_.insert(controller->popup_view()); - } else { - delete controller; - } - } - } else if (dock_info_.type() != DockInfo::NONE && - !dock_controllers_.empty()) { - // Current dock position is the same as last, update the controller's - // in_enable_area state as it may have changed. - dock_controllers_.back()->UpdateInEnabledArea(dock_info_.in_enable_area()); - } -} - -void DraggedTabController::SetDraggedContents(TabContents* new_contents) { - if (dragged_contents_) { - registrar_.Remove(this, - NotificationType::TAB_CONTENTS_DESTROYED, - Source<TabContents>(dragged_contents_)); - if (original_delegate_) - dragged_contents_->set_delegate(original_delegate_); - } - original_delegate_ = NULL; - dragged_contents_ = new_contents; - if (dragged_contents_) { - registrar_.Add(this, - NotificationType::TAB_CONTENTS_DESTROYED, - Source<TabContents>(dragged_contents_)); - - // We need to be the delegate so we receive messages about stuff, - // otherwise our dragged_contents() may be replaced and subsequently - // collected/destroyed while the drag is in process, leading to - // nasty crashes. - original_delegate_ = dragged_contents_->delegate(); - dragged_contents_->set_delegate(this); - } -} - -void DraggedTabController::SaveFocus() { - if (!old_focused_view_) { - old_focused_view_ = source_tabstrip_->GetRootView()->GetFocusedView(); - source_tabstrip_->GetRootView()->FocusView(source_tabstrip_); - } -} - -void DraggedTabController::RestoreFocus() { - if (old_focused_view_ && attached_tabstrip_ == source_tabstrip_) - old_focused_view_->GetRootView()->FocusView(old_focused_view_); - old_focused_view_ = NULL; -} - -bool DraggedTabController::CanStartDrag() const { - // Determine if the mouse has moved beyond a minimum elasticity distance in - // any direction from the starting point. - static const int kMinimumDragDistance = 10; - gfx::Point screen_point = GetCursorScreenPoint(); - int x_offset = abs(screen_point.x() - start_screen_point_.x()); - int y_offset = abs(screen_point.y() - start_screen_point_.y()); - return sqrt(pow(static_cast<float>(x_offset), 2) + - pow(static_cast<float>(y_offset), 2)) > kMinimumDragDistance; -} - -void DraggedTabController::ContinueDragging() { - // Note that the coordinates given to us by |drag_event| are basically - // useless, since they're in source_tab_ coordinates. On the surface, you'd - // think we could just convert them to screen coordinates, however in the - // situation where we're dragging the last tab in a window when multiple - // windows are open, the coordinates of |source_tab_| are way off in - // hyperspace since the window was moved there instead of being closed so - // that we'd keep receiving events. And our ConvertPointToScreen methods - // aren't really multi-screen aware. So really it's just safer to get the - // actual position of the mouse cursor directly from Windows here, which is - // guaranteed to be correct regardless of monitor config. - gfx::Point screen_point = GetCursorScreenPoint(); - -#if defined(OS_CHROMEOS) - // We don't allow detaching in chrome os. - BaseTabStrip* target_tabstrip = source_tabstrip_; -#else - // Determine whether or not we have dragged over a compatible TabStrip in - // another browser window. If we have, we should attach to it and start - // dragging within it. - BaseTabStrip* target_tabstrip = GetTabStripForPoint(screen_point); -#endif - if (target_tabstrip != attached_tabstrip_) { - // Make sure we're fully detached from whatever TabStrip we're attached to - // (if any). - if (attached_tabstrip_) - Detach(); - if (target_tabstrip) - Attach(target_tabstrip, screen_point); - } - if (!target_tabstrip) { - bring_to_front_timer_.Start( - base::TimeDelta::FromMilliseconds(kBringToFrontDelay), this, - &DraggedTabController::BringWindowUnderMouseToFront); - } - - UpdateDockInfo(screen_point); - - if (attached_tabstrip_) - MoveAttachedTab(screen_point); - else - MoveDetachedTab(screen_point); -} - -void DraggedTabController::MoveAttachedTab(const gfx::Point& screen_point) { - DCHECK(attached_tabstrip_); - DCHECK(!view_.get()); - - gfx::Point dragged_view_point = GetAttachedTabDragPoint(screen_point); - TabStripModel* attached_model = GetModel(attached_tabstrip_); - int from_index = attached_model->GetIndexOfTabContents(dragged_contents_); - - int threshold = kVerticalMoveThreshold; - if (attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) { - TabStrip* tab_strip = static_cast<TabStrip*>(attached_tabstrip_); - - // Determine the horizontal move threshold. This is dependent on the width - // of tabs. The smaller the tabs compared to the standard size, the smaller - // the threshold. - double unselected, selected; - tab_strip->GetCurrentTabWidths(&unselected, &selected); - double ratio = unselected / Tab::GetStandardSize().width(); - threshold = static_cast<int>(ratio * kHorizontalMoveThreshold); - } - - // Update the model, moving the TabContents from one index to another. Do this - // only if we have moved a minimum distance since the last reorder (to prevent - // jitter). - if (abs(MajorAxisValue(screen_point, attached_tabstrip_) - - last_move_screen_loc_) > threshold) { - gfx::Point dragged_view_screen_point(dragged_view_point); - views::View::ConvertPointToScreen(attached_tabstrip_, - &dragged_view_screen_point); - gfx::Rect bounds = GetDraggedViewTabStripBounds(dragged_view_screen_point); - int to_index = GetInsertionIndexForDraggedBounds(bounds, true); - to_index = NormalizeIndexToAttachedTabStrip(to_index); - if (from_index != to_index) { - last_move_screen_loc_ = MajorAxisValue(screen_point, attached_tabstrip_); - attached_model->MoveTabContentsAt(from_index, to_index, true); - } - } - - attached_tab_->SchedulePaint(); - attached_tab_->SetX(dragged_view_point.x()); - attached_tab_->SetX( - attached_tabstrip_->MirroredLeftPointForRect(attached_tab_->bounds())); - attached_tab_->SetY(dragged_view_point.y()); - attached_tab_->SchedulePaint(); -} - -void DraggedTabController::MoveDetachedTab(const gfx::Point& screen_point) { - DCHECK(!attached_tabstrip_); - DCHECK(view_.get()); - - int x = screen_point.x() - mouse_offset_.x(); - int y = screen_point.y() - mouse_offset_.y(); - - // Move the View. There are no changes to the model if we're detached. - view_->MoveTo(gfx::Point(x, y)); -} - -DockInfo DraggedTabController::GetDockInfoAtPoint( - const gfx::Point& screen_point) { - if (attached_tabstrip_) { - // If the mouse is over a tab strip, don't offer a dock position. - return DockInfo(); - } - - if (dock_info_.IsValidForPoint(screen_point)) { - // It's possible any given screen coordinate has multiple docking - // positions. Check the current info first to avoid having the docking - // position bounce around. - return dock_info_; - } - - gfx::NativeView dragged_hwnd = view_->GetWidget()->GetNativeView(); - dock_windows_.insert(dragged_hwnd); - DockInfo info = DockInfo::GetDockInfoAtPoint(screen_point, dock_windows_); - dock_windows_.erase(dragged_hwnd); - return info; -} - -BaseTabStrip* DraggedTabController::GetTabStripForPoint( - const gfx::Point& screen_point) { - gfx::NativeView dragged_view = NULL; - if (view_.get()) { - dragged_view = view_->GetWidget()->GetNativeView(); - dock_windows_.insert(dragged_view); - } - gfx::NativeWindow local_window = - DockInfo::GetLocalProcessWindowAtPoint(screen_point, dock_windows_); - if (dragged_view) - dock_windows_.erase(dragged_view); - if (!local_window) - return NULL; - BrowserView* browser = - BrowserView::GetBrowserViewForNativeWindow(local_window); - // We don't allow drops on windows that don't have tabstrips. - if (!browser || - !browser->browser()->SupportsWindowFeature(Browser::FEATURE_TABSTRIP)) - return NULL; - - BaseTabStrip* other_tabstrip = browser->tabstrip(); - if (!other_tabstrip->controller()->IsCompatibleWith(source_tabstrip_)) - return NULL; - return GetTabStripIfItContains(other_tabstrip, screen_point); -} - -BaseTabStrip* DraggedTabController::GetTabStripIfItContains( - BaseTabStrip* tabstrip, const gfx::Point& screen_point) const { - static const int kVerticalDetachMagnetism = 15; - static const int kHorizontalDetachMagnetism = 15; - // Make sure the specified screen point is actually within the bounds of the - // specified tabstrip... - gfx::Rect tabstrip_bounds = GetViewScreenBounds(tabstrip); - if (tabstrip->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) { - if (screen_point.x() < tabstrip_bounds.right() && - screen_point.x() >= tabstrip_bounds.x()) { - // TODO(beng): make this be relative to the start position of the mouse - // for the source TabStrip. - int upper_threshold = tabstrip_bounds.bottom() + kVerticalDetachMagnetism; - int lower_threshold = tabstrip_bounds.y() - kVerticalDetachMagnetism; - if (screen_point.y() >= lower_threshold && - screen_point.y() <= upper_threshold) { - return tabstrip; - } - } - } else { - if (screen_point.y() < tabstrip_bounds.bottom() && - screen_point.y() >= tabstrip_bounds.y()) { - int upper_threshold = tabstrip_bounds.right() + - kHorizontalDetachMagnetism; - int lower_threshold = tabstrip_bounds.x() - kHorizontalDetachMagnetism; - if (screen_point.x() >= lower_threshold && - screen_point.x() <= upper_threshold) { - return tabstrip; - } - } - } - return NULL; -} - -void DraggedTabController::Attach(BaseTabStrip* attached_tabstrip, - const gfx::Point& screen_point) { - DCHECK(!attached_tabstrip_); // We should already have detached by the time - // we get here. - DCHECK(!attached_tab_); // Similarly there should be no attached tab. - - attached_tabstrip_ = attached_tabstrip; - - // We don't need the photo-booth while we're attached. - photobooth_.reset(NULL); - - // And we don't need the dragged view. - view_.reset(); - - BaseTab* tab = GetTabMatchingDraggedContents(attached_tabstrip_); - - if (!tab) { - // There is no Tab in |attached_tabstrip| that corresponds to the dragged - // TabContents. We must now create one. - - // Remove ourselves as the delegate now that the dragged TabContents is - // being inserted back into a Browser. - dragged_contents_->set_delegate(NULL); - original_delegate_ = NULL; - - // Return the TabContents' to normalcy. - dragged_contents_->set_capturing_contents(false); - - // Inserting counts as a move. We don't want the tabs to jitter when the - // user moves the tab immediately after attaching it. - last_move_screen_loc_ = MajorAxisValue(screen_point, attached_tabstrip); - - // Figure out where to insert the tab based on the bounds of the dragged - // representation and the ideal bounds of the other Tabs already in the - // strip. ("ideal bounds" are stable even if the Tabs' actual bounds are - // changing due to animation). - gfx::Rect bounds = GetDraggedViewTabStripBounds(screen_point); - int index = GetInsertionIndexForDraggedBounds(bounds, false); - attached_tabstrip_->set_attaching_dragged_tab(true); - GetModel(attached_tabstrip_)->InsertTabContentsAt( - index, dragged_contents_, - TabStripModel::ADD_SELECTED | - (pinned_ ? TabStripModel::ADD_PINNED : 0)); - attached_tabstrip_->set_attaching_dragged_tab(false); - - tab = GetTabMatchingDraggedContents(attached_tabstrip_); - } - DCHECK(tab); // We should now have a tab. - attached_tab_ = tab; - attached_tabstrip_->StartedDraggingTab(tab); - - if (attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) { - // The size of the dragged tab may have changed. Adjust the x offset so that - // ratio of mouse_offset_ to original width is maintained. - mouse_offset_.set_x(static_cast<int>(offset_to_width_ratio_ * - static_cast<int>(tab->width()))); - } - - // Move the corresponding window to the front. - attached_tabstrip_->GetWindow()->Activate(); -} - -void DraggedTabController::Detach() { - // Prevent the TabContents' HWND from being hidden by any of the model - // operations performed during the drag. - dragged_contents_->set_capturing_contents(true); - - // Update the Model. - TabRendererData tab_data = attached_tab_->data(); - TabStripModel* attached_model = GetModel(attached_tabstrip_); - int index = attached_model->GetIndexOfTabContents(dragged_contents_); - DCHECK(index != -1); - // Hide the tab so that the user doesn't see it animate closed. - attached_tab_->SetVisible(false); - int attached_tab_width = attached_tab_->width(); - attached_model->DetachTabContentsAt(index); - // Detaching may end up deleting the tab, drop references to it. - attached_tab_ = NULL; - - // If we've removed the last Tab from the TabStrip, hide the frame now. - if (attached_model->empty()) - HideFrame(); - - // Set up the photo booth to start capturing the contents of the dragged - // TabContents. - if (!photobooth_.get()) { - photobooth_.reset( - NativeViewPhotobooth::Create(dragged_contents_->GetNativeView())); - } - - // Create the dragged view. - EnsureDraggedView(tab_data); - view_->SetTabWidthAndUpdate(attached_tab_width, photobooth_.get()); - - // Detaching resets the delegate, but we still want to be the delegate. - dragged_contents_->set_delegate(this); - - attached_tabstrip_ = NULL; -} - -int DraggedTabController::GetInsertionIndexForDraggedBounds( - const gfx::Rect& dragged_bounds, - bool is_tab_attached) const { - int right_tab_x = 0; - int bottom_tab_y = 0; - - // If the UI layout of the tab strip is right-to-left, we need to mirror the - // bounds of the dragged tab before performing the drag/drop related - // calculations. We mirror the dragged bounds because we determine the - // position of each tab on the tab strip by calling GetBounds() (without the - // mirroring transformation flag) which effectively means that even though - // the tabs are rendered from right to left, the code performs the - // calculation as if the tabs are laid out from left to right. Mirroring the - // dragged bounds adjusts the coordinates of the tab we are dragging so that - // it uses the same orientation used by the tabs on the tab strip. - gfx::Rect adjusted_bounds(dragged_bounds); - adjusted_bounds.set_x( - attached_tabstrip_->MirroredLeftPointForRect(adjusted_bounds)); - - int index = -1; - for (int i = 0; i < attached_tabstrip_->tab_count(); ++i) { - const gfx::Rect& ideal_bounds = attached_tabstrip_->ideal_bounds(i); - if (attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) { - gfx::Rect left_half = ideal_bounds; - left_half.set_width(left_half.width() / 2); - gfx::Rect right_half = ideal_bounds; - right_half.set_width(ideal_bounds.width() - left_half.width()); - right_half.set_x(left_half.right()); - right_tab_x = right_half.right(); - if (adjusted_bounds.x() >= right_half.x() && - adjusted_bounds.x() < right_half.right()) { - index = i + 1; - break; - } else if (adjusted_bounds.x() >= left_half.x() && - adjusted_bounds.x() < left_half.right()) { - index = i; - break; - } - } else { - // Vertical tab strip. - int max_y = ideal_bounds.bottom(); - int mid_y = ideal_bounds.y() + ideal_bounds.height() / 2; - bottom_tab_y = max_y; - if (adjusted_bounds.y() < mid_y) { - index = i; - break; - } else if (adjusted_bounds.y() >= mid_y && adjusted_bounds.y() < max_y) { - index = i + 1; - break; - } - } - } - if (index == -1) { - if ((attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP && - adjusted_bounds.right() > right_tab_x) || - (attached_tabstrip_->type() == BaseTabStrip::VERTICAL_TAB_STRIP && - adjusted_bounds.y() >= bottom_tab_y)) { - index = GetModel(attached_tabstrip_)->count(); - } else { - index = 0; - } - } - - index = GetModel(attached_tabstrip_)->ConstrainInsertionIndex(index, mini_); - if (is_tab_attached && mini_ && - index == GetModel(attached_tabstrip_)->IndexOfFirstNonMiniTab()) { - index--; - } - return index; -} - -gfx::Rect DraggedTabController::GetDraggedViewTabStripBounds( - const gfx::Point& screen_point) { - gfx::Point client_point = - ConvertScreenPointToTabStripPoint(attached_tabstrip_, screen_point); - // attached_tab_ is NULL when inserting into a new tabstrip. - if (attached_tab_) { - return gfx::Rect(client_point.x(), client_point.y(), - attached_tab_->width(), attached_tab_->height()); - } - - if (attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) { - double sel_width, unselected_width; - static_cast<TabStrip*>(attached_tabstrip_)->GetCurrentTabWidths( - &sel_width, &unselected_width); - return gfx::Rect(client_point.x(), client_point.y(), - static_cast<int>(sel_width), - Tab::GetStandardSize().height()); - } - - return gfx::Rect(client_point.x(), client_point.y(), - attached_tabstrip_->width(), - SideTab::GetPreferredHeight()); -} - -gfx::Point DraggedTabController::GetAttachedTabDragPoint( - const gfx::Point& screen_point) { - DCHECK(attached_tabstrip_); // The tab must be attached. - - int x = screen_point.x() - mouse_offset_.x(); - int y = screen_point.y() - mouse_offset_.y(); - - gfx::Point tab_loc(x, y); - views::View::ConvertPointToView(NULL, attached_tabstrip_, &tab_loc); - - x = tab_loc.x(); - y = tab_loc.y(); - - const gfx::Size& tab_size = attached_tab_->bounds().size(); - - if (attached_tabstrip_->type() == BaseTabStrip::HORIZONTAL_TAB_STRIP) { - int max_x = attached_tabstrip_->bounds().right() - tab_size.width(); - x = std::min(std::max(x, 0), max_x); - y = 0; - } else { - x = SideTabStrip::kTabStripInset; - int max_y = attached_tabstrip_->bounds().bottom() - tab_size.height(); - y = std::min(std::max(y, SideTabStrip::kTabStripInset), max_y); - } - return gfx::Point(x, y); -} - -BaseTab* DraggedTabController::GetTabMatchingDraggedContents( - BaseTabStrip* tabstrip) const { - int model_index = - GetModel(tabstrip)->GetIndexOfTabContents(dragged_contents_); - return model_index == TabStripModel::kNoTab ? - NULL : tabstrip->GetBaseTabAtModelIndex(model_index); -} - -void DraggedTabController::EndDragImpl(EndDragType type) { - // WARNING: this may be invoked multiple times. In particular, if deletion - // occurs after a delay (as it does when the tab is released in the original - // tab strip) and the navigation controller/tab contents is deleted before - // the animation finishes, this is invoked twice. The second time through - // type == TAB_DESTROYED. - - active_ = false; - - bring_to_front_timer_.Stop(); - - // Hide the current dock controllers. - for (size_t i = 0; i < dock_controllers_.size(); ++i) { - // Be sure and clear the controller first, that way if Hide ends up - // deleting the controller it won't call us back. - dock_controllers_[i]->clear_controller(); - dock_controllers_[i]->Hide(); - } - dock_controllers_.clear(); - dock_windows_.clear(); - - if (type != TAB_DESTROYED) { - // We only finish up the drag if we were actually dragging. If start_drag_ - // is false, the user just clicked and released and didn't move the mouse - // enough to trigger a drag. - if (started_drag_) { - RestoreFocus(); - if (type == CANCELED) - RevertDrag(); - else - CompleteDrag(); - } - if (dragged_contents_ && dragged_contents_->delegate() == this) - dragged_contents_->set_delegate(original_delegate_); - } else { - // If we get here it means the NavigationController is going down. Don't - // attempt to do any cleanup other than resetting the delegate (if we're - // still the delegate). - if (dragged_contents_ && dragged_contents_->delegate() == this) - dragged_contents_->set_delegate(NULL); - dragged_contents_ = NULL; - } - - // The delegate of the dragged contents should have been reset. Unset the - // original delegate so that we don't attempt to reset the delegate when - // deleted. - DCHECK(!dragged_contents_ || dragged_contents_->delegate() != this); - original_delegate_ = NULL; - - source_tabstrip_->DestroyDragController(); -} - -void DraggedTabController::RevertDrag() { - DCHECK(started_drag_); - - // We save this here because code below will modify |attached_tabstrip_|. - bool restore_frame = attached_tabstrip_ != source_tabstrip_; - if (attached_tabstrip_) { - int index = GetModel(attached_tabstrip_)->GetIndexOfTabContents( - dragged_contents_); - if (attached_tabstrip_ != source_tabstrip_) { - // The Tab was inserted into another TabStrip. We need to put it back - // into the original one. - GetModel(attached_tabstrip_)->DetachTabContentsAt(index); - // TODO(beng): (Cleanup) seems like we should use Attach() for this - // somehow. - attached_tabstrip_ = source_tabstrip_; - GetModel(source_tabstrip_)->InsertTabContentsAt( - source_model_index_, dragged_contents_, - TabStripModel::ADD_SELECTED | - (pinned_ ? TabStripModel::ADD_PINNED : 0)); - } else { - // The Tab was moved within the TabStrip where the drag was initiated. - // Move it back to the starting location. - GetModel(source_tabstrip_)->MoveTabContentsAt(index, source_model_index_, - true); - source_tabstrip_->StoppedDraggingTab(attached_tab_); - } - } else { - // TODO(beng): (Cleanup) seems like we should use Attach() for this - // somehow. - attached_tabstrip_ = source_tabstrip_; - // The Tab was detached from the TabStrip where the drag began, and has not - // been attached to any other TabStrip. We need to put it back into the - // source TabStrip. - GetModel(source_tabstrip_)->InsertTabContentsAt( - source_model_index_, dragged_contents_, - TabStripModel::ADD_SELECTED | - (pinned_ ? TabStripModel::ADD_PINNED : 0)); - } - - // If we're not attached to any TabStrip, or attached to some other TabStrip, - // we need to restore the bounds of the original TabStrip's frame, in case - // it has been hidden. - if (restore_frame) { - if (!restore_bounds_.IsEmpty()) { -#if defined(OS_WIN) - HWND frame_hwnd = source_tabstrip_->GetWidget()->GetNativeView(); - MoveWindow(frame_hwnd, restore_bounds_.x(), restore_bounds_.y(), - restore_bounds_.width(), restore_bounds_.height(), TRUE); -#else - NOTIMPLEMENTED(); -#endif - } - } -} - -void DraggedTabController::CompleteDrag() { - DCHECK(started_drag_); - - if (attached_tabstrip_) { - attached_tabstrip_->StoppedDraggingTab(attached_tab_); - } else { - if (dock_info_.type() != DockInfo::NONE) { - Profile* profile = GetModel(source_tabstrip_)->profile(); - switch (dock_info_.type()) { - case DockInfo::LEFT_OF_WINDOW: - UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Left"), - profile); - break; - - case DockInfo::RIGHT_OF_WINDOW: - UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Right"), - profile); - break; - - case DockInfo::BOTTOM_OF_WINDOW: - UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Bottom"), - profile); - break; - - case DockInfo::TOP_OF_WINDOW: - UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Top"), - profile); - break; - - case DockInfo::MAXIMIZE: - UserMetrics::RecordAction(UserMetricsAction("DockingWindow_Maximize"), - profile); - break; - - case DockInfo::LEFT_HALF: - UserMetrics::RecordAction(UserMetricsAction("DockingWindow_LeftHalf"), - profile); - break; - - case DockInfo::RIGHT_HALF: - UserMetrics::RecordAction( - UserMetricsAction("DockingWindow_RightHalf"), - profile); - break; - - case DockInfo::BOTTOM_HALF: - UserMetrics::RecordAction( - UserMetricsAction("DockingWindow_BottomHalf"), - profile); - break; - - default: - NOTREACHED(); - break; - } - } - // Compel the model to construct a new window for the detached TabContents. - views::Window* window = source_tabstrip_->GetWindow(); - gfx::Rect window_bounds(window->GetNormalBounds()); - window_bounds.set_origin(GetWindowCreatePoint()); - // When modifying the following if statement, please make sure not to - // introduce issue listed in http://crbug.com/6223 comment #11. - bool rtl_ui = base::i18n::IsRTL(); - bool has_dock_position = (dock_info_.type() != DockInfo::NONE); - if (rtl_ui && has_dock_position) { - // Mirror X axis so the docked tab is aligned using the mouse click as - // the top-right corner. - window_bounds.set_x(window_bounds.x() - window_bounds.width()); - } - Browser* new_browser = - GetModel(source_tabstrip_)->delegate()->CreateNewStripWithContents( - dragged_contents_, window_bounds, dock_info_, window->IsMaximized()); - TabStripModel* new_model = new_browser->tabstrip_model(); - new_model->SetTabPinned(new_model->GetIndexOfTabContents(dragged_contents_), - pinned_); - new_browser->window()->Show(); - } - - CleanUpHiddenFrame(); -} - -void DraggedTabController::EnsureDraggedView(const TabRendererData& data) { - if (!view_.get()) { - gfx::Rect tab_bounds; - dragged_contents_->GetContainerBounds(&tab_bounds); - BaseTab* renderer = source_tabstrip_->CreateTabForDragging(); - renderer->SetData(data); - // DraggedTabView takes ownership of renderer. - view_.reset(new DraggedTabView(renderer, mouse_offset_, - tab_bounds.size(), - Tab::GetMinimumSelectedSize())); - } -} - -gfx::Point DraggedTabController::GetCursorScreenPoint() const { -#if defined(OS_WIN) - DWORD pos = GetMessagePos(); - return gfx::Point(pos); -#else - gint x, y; - gdk_display_get_pointer(gdk_display_get_default(), NULL, &x, &y, NULL); - return gfx::Point(x, y); -#endif -} - -gfx::Rect DraggedTabController::GetViewScreenBounds(views::View* view) const { - gfx::Point view_topleft; - views::View::ConvertPointToScreen(view, &view_topleft); - gfx::Rect view_screen_bounds = view->GetLocalBounds(true); - view_screen_bounds.Offset(view_topleft.x(), view_topleft.y()); - return view_screen_bounds; -} - -int DraggedTabController::NormalizeIndexToAttachedTabStrip(int index) const { - DCHECK(attached_tabstrip_) << "Can only be called when attached!"; - TabStripModel* attached_model = GetModel(attached_tabstrip_); - if (index >= attached_model->count()) - return attached_model->count() - 1; - if (index == TabStripModel::kNoTab) - return 0; - return index; -} - -void DraggedTabController::HideFrame() { -#if defined(OS_WIN) - // We don't actually hide the window, rather we just move it way off-screen. - // If we actually hide it, we stop receiving drag events. - HWND frame_hwnd = source_tabstrip_->GetWidget()->GetNativeView(); - RECT wr; - GetWindowRect(frame_hwnd, &wr); - MoveWindow(frame_hwnd, 0xFFFF, 0xFFFF, wr.right - wr.left, - wr.bottom - wr.top, TRUE); - - // We also save the bounds of the window prior to it being moved, so that if - // the drag session is aborted we can restore them. - restore_bounds_ = gfx::Rect(wr); -#else - NOTIMPLEMENTED(); -#endif -} - -void DraggedTabController::CleanUpHiddenFrame() { - // If the model we started dragging from is now empty, we must ask the - // delegate to close the frame. - if (GetModel(source_tabstrip_)->empty()) - GetModel(source_tabstrip_)->delegate()->CloseFrameAfterDragSession(); -} - -void DraggedTabController::DockDisplayerDestroyed( - DockDisplayer* controller) { - DockWindows::iterator dock_i = - dock_windows_.find(controller->popup_view()); - if (dock_i != dock_windows_.end()) - dock_windows_.erase(dock_i); - else - NOTREACHED(); - - std::vector<DockDisplayer*>::iterator i = - std::find(dock_controllers_.begin(), dock_controllers_.end(), - controller); - if (i != dock_controllers_.end()) - dock_controllers_.erase(i); - else - NOTREACHED(); -} - -void DraggedTabController::BringWindowUnderMouseToFront() { - // If we're going to dock to another window, bring it to the front. - gfx::NativeWindow window = dock_info_.window(); - if (!window) { - gfx::NativeView dragged_view = view_->GetWidget()->GetNativeView(); - dock_windows_.insert(dragged_view); - window = DockInfo::GetLocalProcessWindowAtPoint(GetCursorScreenPoint(), - dock_windows_); - dock_windows_.erase(dragged_view); - } - if (window) { -#if defined(OS_WIN) - // Move the window to the front. - SetWindowPos(window, HWND_TOP, 0, 0, 0, 0, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); - - // The previous call made the window appear on top of the dragged window, - // move the dragged window to the front. - SetWindowPos(view_->GetWidget()->GetNativeView(), HWND_TOP, 0, 0, 0, 0, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); -#else - NOTIMPLEMENTED(); -#endif - } -} - -TabStripModel* DraggedTabController::GetModel(BaseTabStrip* tabstrip) const { - return static_cast<BrowserTabStripController*>(tabstrip->controller())-> - model(); -} |