172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/views/tabs/tab_strip.h"
672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include <algorithm>
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/compiler_specific.h"
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/stl_util-inl.h"
113f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/utf_string_conversions.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/defaults.h"
13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/themes/theme_service.h"
1421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/view_ids.h"
1521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/views/tabs/tab.h"
1621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/views/tabs/tab_strip_controller.h"
17c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/generated_resources.h"
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/theme_resources.h"
19ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "ui/base/accessibility/accessible_view_state.h"
203f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "ui/base/animation/animation_container.h"
2172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/dragdrop/drag_drop_types.h"
2272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/l10n/l10n_util.h"
2372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/resource/resource_bundle.h"
2472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/canvas_skia.h"
2572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/path.h"
2672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/size.h"
27c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/controls/image_view.h"
28c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/default_theme_provider.h"
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/window/non_client_view.h"
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/window/window.h"
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN)
3372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "views/widget/monitor_win.h"
34c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/widget_win.h"
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#elif defined(OS_LINUX)
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "views/widget/widget_gtk.h"
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#undef min
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#undef max
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(COMPILER_GCC)
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Squash false positive signed overflow warning in GenerateStartAndEndWidths
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// when doing 'start_tab_count < end_tab_count'.
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#pragma GCC diagnostic ignored "-Wstrict-overflow"
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing views::DropTargetEvent;
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kNewTabButtonHOffset = -5;
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kNewTabButtonVOffset = 5;
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kSuspendAnimationsTimeMs = 200;
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kTabHOffset = -16;
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kTabStripAnimationVSlop = 40;
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Size of the drop indicator.
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic int drop_indicator_width;
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic int drop_indicator_height;
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic inline int Round(double x) {
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Why oh why is this not in a standard header?
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return static_cast<int>(floor(x + 0.5));
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch///////////////////////////////////////////////////////////////////////////////
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// NewTabButton
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//  A subclass of button that hit-tests to the shape of the new tab button.
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass NewTabButton : public views::ImageButton {
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  explicit NewTabButton(views::ButtonListener* listener)
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      : views::ImageButton(listener) {
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual ~NewTabButton() {}
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch protected:
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Overridden from views::View:
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual bool HasHitTestMask() const {
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // When the button is sized to the top of the tab strip we want the user to
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // be able to click on complete bounds, and so don't return a custom hit
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // mask.
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return !browser_defaults::kSizeTabButtonToTopOfTabStrip;
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual void GetHitTestMask(gfx::Path* path) const {
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    DCHECK(path);
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SkScalar w = SkIntToScalar(width());
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // These values are defined by the shape of the new tab bitmap. Should that
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // bitmap ever change, these values will need to be updated. They're so
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // custom it's not really worth defining constants for.
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    path->moveTo(0, 1);
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    path->lineTo(w - 7, 1);
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    path->lineTo(w - 4, 4);
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    path->lineTo(w, 16);
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    path->lineTo(w - 1, 17);
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    path->lineTo(7, 17);
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    path->lineTo(4, 13);
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    path->lineTo(0, 1);
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    path->close();
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(NewTabButton);
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch///////////////////////////////////////////////////////////////////////////////
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStrip, public:
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int TabStrip::mini_to_non_mini_gap_ = 3;
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabStrip::TabStrip(TabStripController* controller)
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : BaseTabStrip(controller, BaseTabStrip::HORIZONTAL_TAB_STRIP),
120ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      newtab_button_(NULL),
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      current_unselected_width_(Tab::GetStandardSize().width()),
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      current_selected_width_(Tab::GetStandardSize().width()),
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      available_width_for_tabs_(-1),
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      in_tab_close_(false),
1253f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen      animation_container_(new ui::AnimationContainer()) {
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Init();
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabStrip::~TabStrip() {
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The animations may reference the tabs. Shut down the animation before we
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // delete the tabs.
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StopAnimating(false);
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DestroyDragController();
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make sure we unhook ourselves as a message loop observer so that we don't
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // crash in the case where the user closes the window after closing a tab
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // but before moving the mouse.
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  RemoveMessageLoopObserver();
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The children (tabs) may callback to us from their destructor. Delete them
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // so that if they call back we aren't in a weird state.
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  RemoveAllChildViews(true);
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::InitTabStripButtons() {
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  newtab_button_ = new NewTabButton(this);
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (browser_defaults::kSizeTabButtonToTopOfTabStrip) {
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    newtab_button_->SetImageAlignment(views::ImageButton::ALIGN_LEFT,
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                      views::ImageButton::ALIGN_BOTTOM);
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  LoadNewTabButtonImage();
1533f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  newtab_button_->SetAccessibleName(
15472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      l10n_util::GetStringUTF16(IDS_ACCNAME_NEWTAB));
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  AddChildView(newtab_button_);
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Rect TabStrip::GetNewTabButtonBounds() {
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return newtab_button_->bounds();
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid TabStrip::MouseMovedOutOfView() {
1633345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  ResizeLayoutTabs();
1643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick}
1653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
167ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// TabStrip, AbstractTabStripView implementation:
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool TabStrip::IsPositionInWindowCaption(const gfx::Point& point) {
170dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  views::View* v = GetEventHandlerForPoint(point);
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If there is no control at this location, claim the hit was in the title
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // bar to get a move action.
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (v == this)
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return true;
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Check to see if the point is within the non-button parts of the new tab
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // button. The button has a non-rectangular shape, so if it's not in the
179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // visual portions of the button we treat it as a click to the caption.
180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point point_in_newtab_coords(point);
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  View::ConvertPointToView(this, newtab_button_, &point_in_newtab_coords);
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (newtab_button_->bounds().Contains(point) &&
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      !newtab_button_->HitTest(point_in_newtab_coords)) {
184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return true;
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // All other regions, including the new Tab button, should be considered part
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // of the containing Window's client area so that regular events can be
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // processed for them.
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return false;
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
193ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid TabStrip::SetBackgroundOffset(const gfx::Point& offset) {
194ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (int i = 0; i < tab_count(); ++i)
195ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    GetTabAtTabDataIndex(i)->set_background_offset(offset);
196ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
197ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
198ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen////////////////////////////////////////////////////////////////////////////////
199ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// TabStrip, BaseTabStrip implementation:
200ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::PrepareForCloseAt(int model_index) {
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!in_tab_close_ && IsAnimating()) {
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Cancel any current animations. We do this as remove uses the current
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // ideal bounds and we need to know ideal bounds is in a good state.
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StopAnimating(true);
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int model_count = GetModelCount();
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (model_index + 1 != model_count && model_count > 1) {
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // The user is about to close a tab other than the last tab. Set
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // available_width_for_tabs_ so that if we do a layout we don't position a
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // tab past the end of the second to last tab. We do this so that as the
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // user closes tabs with the mouse a tab continues to fall under the mouse.
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    available_width_for_tabs_ = GetAvailableWidthForTabs(
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        GetTabAtModelIndex(model_count - 2));
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  in_tab_close_ = true;
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  AddMessageLoopObserver();
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::RemoveTabAt(int model_index) {
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (in_tab_close_ && model_index != GetModelCount())
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartMouseInitiatedRemoveTabAnimation(model_index);
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  else
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartRemoveTabAnimation(model_index);
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::SelectTabAt(int old_model_index, int new_model_index) {
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We have "tiny tabs" if the tabs are so tiny that the unselected ones are
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // a different size to the selected ones.
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool tiny_tabs = current_unselected_width_ != current_selected_width_;
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!IsAnimating() && (!in_tab_close_ || tiny_tabs)) {
234731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    DoLayout();
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SchedulePaint();
237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (old_model_index >= 0) {
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GetTabAtTabDataIndex(ModelIndexToTabIndex(old_model_index))->
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        StopMiniTabTitleAnimation();
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::TabTitleChangedNotLoading(int model_index) {
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Tab* tab = GetTabAtModelIndex(model_index);
247ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (tab->data().mini && !tab->IsActive())
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab->StartMiniTabTitleAnimation();
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::StartHighlight(int model_index) {
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetTabAtModelIndex(model_index)->StartPulse();
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::StopAllHighlighting() {
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0; i < tab_count(); ++i)
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GetTabAtTabDataIndex(i)->StopPulse();
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBaseTab* TabStrip::CreateTabForDragging() {
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Tab* tab = new Tab(NULL);
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make sure the dragged tab shares our theme provider. We need to explicitly
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // do this as during dragging there isn't a theme provider.
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab->set_theme_provider(GetThemeProvider());
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return tab;
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch///////////////////////////////////////////////////////////////////////////////
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStrip, views::View overrides:
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::PaintChildren(gfx::Canvas* canvas) {
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Tabs are painted in reverse order, so they stack to the left.
273ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  Tab* active_tab = NULL;
274ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  std::vector<Tab*> tabs_dragging;
275ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  std::vector<Tab*> selected_tabs;
276ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  bool is_dragging = false;
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = tab_count() - 1; i >= 0; --i) {
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We must ask the _Tab's_ model, not ourselves, because in some situations
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // the model will be different to this object, e.g. when a Tab is being
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // removed after its TabContents has been destroyed.
282ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    Tab* tab = GetTabAtTabDataIndex(i);
283731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    if (tab->dragging()) {
284ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      is_dragging = true;
285ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (tab->IsActive())
286ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        active_tab = tab;
287ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      else
288ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        tabs_dragging.push_back(tab);
289ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else if (!tab->IsActive()) {
290ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (!tab->IsSelected())
291ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        tab->Paint(canvas);
292ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      else
293ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        selected_tabs.push_back(tab);
294731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    } else {
295ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      active_tab = tab;
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
299dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (GetWindow()->non_client_view()->UseNativeFrame()) {
300ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    bool multiple_tabs_selected = (!selected_tabs.empty() ||
301ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                   tabs_dragging.size() > 1);
302ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // Make sure non-active tabs are somewhat transparent.
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SkPaint paint;
304ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // If there are multiple tabs selected, fade non-selected tabs more to make
305ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // the selected tabs more noticable.
306ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    paint.setColor(SkColorSetARGB(
307ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                       multiple_tabs_selected ? 150 : 200, 255, 255, 255));
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    paint.setStyle(SkPaint::kFill_Style);
3103345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    canvas->DrawRectInt(0, 0, width(),
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        height() - 2,  // Visible region that overlaps the toolbar.
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        paint);
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
315ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Now selected but not active. We don't want these dimmed if using native
316ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // frame, so they're painted after initial pass.
317ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (size_t i = 0; i < selected_tabs.size(); ++i)
318ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    selected_tabs[i]->Paint(canvas);
319ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
320ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Next comes the active tab.
321ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (active_tab && !is_dragging)
322ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    active_tab->Paint(canvas);
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Paint the New Tab button.
325dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  newtab_button_->Paint(canvas);
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
327ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // And the dragged tabs.
328ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (size_t i = 0; i < tabs_dragging.size(); ++i)
329ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    tabs_dragging[i]->Paint(canvas);
330ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
331ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // If the active tab is being dragged, it goes last.
332ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (active_tab && is_dragging)
333ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    active_tab->Paint(canvas);
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Overridden to support automation. See automation_proxy_uitest.cc.
33772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenconst views::View* TabStrip::GetViewByID(int view_id) const {
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_count() > 0) {
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (view_id == VIEW_ID_TAB_LAST) {
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return GetTabAtTabDataIndex(tab_count() - 1);
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else if ((view_id >= VIEW_ID_TAB_0) && (view_id < VIEW_ID_TAB_LAST)) {
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      int index = view_id - VIEW_ID_TAB_0;
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (index >= 0 && index < tab_count()) {
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return GetTabAtTabDataIndex(index);
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else {
346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return NULL;
347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return View::GetViewByID(view_id);
352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Size TabStrip::GetPreferredSize() {
355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return gfx::Size(0, Tab::GetMinimumUnselectedSize().height());
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::OnDragEntered(const DropTargetEvent& event) {
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Force animations to stop, otherwise it makes the index calculation tricky.
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StopAnimating(true);
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UpdateDropIndex(event);
363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStrip::OnDragUpdated(const DropTargetEvent& event) {
366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UpdateDropIndex(event);
367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return GetDropEffect(event);
368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::OnDragExited() {
371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetDropIndex(-1, false);
372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStrip::OnPerformDrop(const DropTargetEvent& event) {
375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!drop_info_.get())
37672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return ui::DragDropTypes::DRAG_NONE;
377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const int drop_index = drop_info_->drop_index;
379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const bool drop_before = drop_info_->drop_before;
380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Hide the drop indicator.
382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetDropIndex(-1, false);
383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GURL url;
385dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  string16 title;
38672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (!event.data().GetURLAndTitle(&url, &title) || !url.is_valid())
38772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return ui::DragDropTypes::DRAG_NONE;
388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  controller()->PerformDrop(drop_before, drop_index, url);
390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return GetDropEffect(event);
392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
394ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid TabStrip::GetAccessibleState(ui::AccessibleViewState* state) {
395ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  state->role = ui::AccessibilityTypes::ROLE_PAGETABLIST;
396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
398dc0f95d653279beabeb9817299e2902918ba123eKristian Monsenviews::View* TabStrip::GetEventHandlerForPoint(const gfx::Point& point) {
399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Return any view that isn't a Tab or this TabStrip immediately. We don't
400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // want to interfere.
401dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  views::View* v = View::GetEventHandlerForPoint(point);
402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (v && v != this && v->GetClassName() != Tab::kViewClassName)
403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return v;
404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The display order doesn't necessarily match the child list order, so we
406ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // walk the display list hit-testing Tabs. Since the active tab always
407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // renders on top of adjacent tabs, it needs to be hit-tested before any
408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // left-adjacent Tab, so we look ahead for it as we walk.
409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0; i < tab_count(); ++i) {
410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Tab* next_tab = i < (tab_count() - 1) ? GetTabAtTabDataIndex(i + 1) : NULL;
411ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (next_tab && next_tab->IsActive() && IsPointInTab(next_tab, point))
412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return next_tab;
413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Tab* tab = GetTabAtTabDataIndex(i);
414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (IsPointInTab(tab, point))
415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return tab;
416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // No need to do any floating view stuff, we don't use them in the TabStrip.
419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return this;
420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
4223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickvoid TabStrip::OnThemeChanged() {
423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  LoadNewTabButtonImage();
424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
426c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochBaseTab* TabStrip::CreateTab() {
427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Tab* tab = new Tab(this);
428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab->set_animation_container(animation_container_.get());
429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return tab;
430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
432ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid TabStrip::StartInsertTabAnimation(int model_index) {
433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  PrepareForAnimation();
434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The TabStrip can now use its entire width to lay out Tabs.
436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  in_tab_close_ = false;
437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  available_width_for_tabs_ = -1;
438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GenerateIdealBounds();
440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int tab_data_index = ModelIndexToTabIndex(model_index);
442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BaseTab* tab = base_tab_at_tab_index(tab_data_index);
443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (model_index == 0) {
444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab->SetBounds(0, ideal_bounds(tab_data_index).y(), 0,
445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   ideal_bounds(tab_data_index).height());
446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BaseTab* last_tab = base_tab_at_tab_index(tab_data_index - 1);
448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab->SetBounds(last_tab->bounds().right() + kTabHOffset,
449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   ideal_bounds(tab_data_index).y(), 0,
450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   ideal_bounds(tab_data_index).height());
451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  AnimateToIdealBounds();
454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::AnimateToIdealBounds() {
457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0; i < tab_count(); ++i) {
458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Tab* tab = GetTabAtTabDataIndex(i);
459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!tab->closing() && !tab->dragging())
460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      bounds_animator().AnimateViewTo(tab, ideal_bounds(i));
461c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
463c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bounds_animator().AnimateViewTo(newtab_button_, newtab_button_bounds_);
464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool TabStrip::ShouldHighlightCloseButtonAfterRemove() {
467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return in_tab_close_;
468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
470731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickvoid TabStrip::DoLayout() {
471731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  BaseTabStrip::DoLayout();
472731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
47372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  newtab_button_->SetBoundsRect(newtab_button_bounds_);
474731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
475731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
476ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid TabStrip::LayoutDraggedTabsAt(const std::vector<BaseTab*>& tabs,
477ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                   BaseTab* active_tab,
478ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                   const gfx::Point& location,
479ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                   bool initial_drag) {
480ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  std::vector<gfx::Rect> bounds;
481ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  CalculateBoundsForDraggedTabs(tabs, &bounds);
482ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK_EQ(tabs.size(), bounds.size());
483ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  int active_tab_model_index = GetModelIndexOfBaseTab(active_tab);
484ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  int active_tab_index = static_cast<int>(
485ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      std::find(tabs.begin(), tabs.end(), active_tab) - tabs.begin());
486ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (size_t i = 0; i < tabs.size(); ++i) {
487ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    BaseTab* tab = tabs[i];
488ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    gfx::Rect new_bounds = bounds[i];
489ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new_bounds.Offset(location.x(), location.y());
490ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    int consecutive_index =
491ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        active_tab_model_index - (active_tab_index - static_cast<int>(i));
492ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // If this is the initial layout during a drag and the tabs aren't
493ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // consecutive animate the view into position. Do the same if the tab is
494ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // already animating (which means we previously caused it to animate).
495ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if ((initial_drag &&
496ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen         GetModelIndexOfBaseTab(tabs[i]) != consecutive_index) ||
497ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        bounds_animator().IsAnimating(tabs[i])) {
498ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      bounds_animator().SetTargetBounds(tabs[i], new_bounds);
499ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else {
500ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      tab->SetBoundsRect(new_bounds);
501ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
502ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
503ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
504ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
505ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid TabStrip::CalculateBoundsForDraggedTabs(const std::vector<BaseTab*>& tabs,
506ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                             std::vector<gfx::Rect>* bounds) {
507ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  int x = 0;
508ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (size_t i = 0; i < tabs.size(); ++i) {
509ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    BaseTab* tab = tabs[i];
510ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (i > 0 && tab->data().mini != tabs[i - 1]->data().mini)
511ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      x += mini_to_non_mini_gap_;
512ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    gfx::Rect new_bounds = tab->bounds();
513ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    new_bounds.set_origin(gfx::Point(x, 0));
514ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    bounds->push_back(new_bounds);
515ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    x += tab->width() + kTabHOffset;
516ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
517ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
518ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
519ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenint TabStrip::GetSizeNeededForTabs(const std::vector<BaseTab*>& tabs) {
520ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  int width = 0;
521ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (size_t i = 0; i < tabs.size(); ++i) {
522ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    BaseTab* tab = tabs[i];
523ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    width += tab->width();
524ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (i > 0 && tab->data().mini != tabs[i - 1]->data().mini)
525ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      width += mini_to_non_mini_gap_;
526ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
527ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (tabs.size() > 0)
528ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    width += kTabHOffset * static_cast<int>(tabs.size() - 1);
529ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return width;
530ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
531ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
532c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::ViewHierarchyChanged(bool is_add,
533c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    views::View* parent,
534c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    views::View* child) {
535c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (is_add && child == this)
536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    InitTabStripButtons();
537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
538c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch///////////////////////////////////////////////////////////////////////////////
540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStrip, views::BaseButton::ButtonListener implementation:
541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
542c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::ButtonPressed(views::Button* sender, const views::Event& event) {
543c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (sender == newtab_button_)
544c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    controller()->CreateNewTab();
545c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
546c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
547c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch///////////////////////////////////////////////////////////////////////////////
548c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStrip, private:
549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
550c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::Init() {
551c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetID(VIEW_ID_TAB_STRIP);
552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  newtab_button_bounds_.SetRect(0, 0, kNewTabButtonWidth, kNewTabButtonHeight);
553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (browser_defaults::kSizeTabButtonToTopOfTabStrip) {
554c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    newtab_button_bounds_.set_height(
555c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        kNewTabButtonHeight + kNewTabButtonVOffset);
556c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
557c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (drop_indicator_width == 0) {
558c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Direction doesn't matter, both images are the same size.
559c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SkBitmap* drop_image = GetDropArrowImage(true);
560c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drop_indicator_width = drop_image->width();
561c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drop_indicator_height = drop_image->height();
562c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
563c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
564c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
565c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::LoadNewTabButtonImage() {
56672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ui::ThemeProvider* tp = GetThemeProvider();
567c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
568c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If we don't have a theme provider yet, it means we do not have a
569c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // root view, and are therefore in a test.
570c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool in_test = false;
571c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tp == NULL) {
572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tp = new views::DefaultThemeProvider();
573c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    in_test = true;
574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
575c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
576c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SkBitmap* bitmap = tp->GetBitmapNamed(IDR_NEWTAB_BUTTON);
577ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  SkColor color = tp->GetColor(ThemeService::COLOR_BUTTON_BACKGROUND);
578c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SkBitmap* background = tp->GetBitmapNamed(
579c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      IDR_THEME_WINDOW_CONTROL_BACKGROUND);
580c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
581c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  newtab_button_->SetImage(views::CustomButton::BS_NORMAL, bitmap);
582c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  newtab_button_->SetImage(views::CustomButton::BS_PUSHED,
583c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           tp->GetBitmapNamed(IDR_NEWTAB_BUTTON_P));
584c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  newtab_button_->SetImage(views::CustomButton::BS_HOT,
585c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           tp->GetBitmapNamed(IDR_NEWTAB_BUTTON_H));
586c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  newtab_button_->SetBackground(color, background,
587c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                tp->GetBitmapNamed(IDR_NEWTAB_BUTTON_MASK));
588c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (in_test)
589c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    delete tp;
590c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
591c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
592c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTab* TabStrip::GetTabAtTabDataIndex(int tab_data_index) const {
593c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return static_cast<Tab*>(base_tab_at_tab_index(tab_data_index));
594c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
595c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
596c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTab* TabStrip::GetTabAtModelIndex(int model_index) const {
597c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return GetTabAtTabDataIndex(ModelIndexToTabIndex(model_index));
598c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
599c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
600c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::GetCurrentTabWidths(double* unselected_width,
601c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   double* selected_width) const {
602c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *unselected_width = current_unselected_width_;
603c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *selected_width = current_selected_width_;
604c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
605c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
606c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::GetDesiredTabWidths(int tab_count,
607c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   int mini_tab_count,
608c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   double* unselected_width,
609c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   double* selected_width) const {
610c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(tab_count >= 0 && mini_tab_count >= 0 && mini_tab_count <= tab_count);
611c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const double min_unselected_width = Tab::GetMinimumUnselectedSize().width();
612c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const double min_selected_width = Tab::GetMinimumSelectedSize().width();
613c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
614c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *unselected_width = min_unselected_width;
615c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *selected_width = min_selected_width;
616c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
617c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_count == 0) {
618c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Return immediately to avoid divide-by-zero below.
619c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
620c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
621c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
622c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Determine how much space we can actually allocate to tabs.
623c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int available_width;
624c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (available_width_for_tabs_ < 0) {
625c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    available_width = width();
626c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    available_width -= (kNewTabButtonHOffset + newtab_button_bounds_.width());
627c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
628c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Interesting corner case: if |available_width_for_tabs_| > the result
629c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // of the calculation in the conditional arm above, the strip is in
630c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // overflow.  We can either use the specified width or the true available
631c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // width here; the first preserves the consistent "leave the last tab under
632c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // the user's mouse so they can close many tabs" behavior at the cost of
633c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // prolonging the glitchy appearance of the overflow state, while the second
634c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // gets us out of overflow as soon as possible but forces the user to move
635c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // their mouse for a few tabs' worth of closing.  We choose visual
636c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // imperfection over behavioral imperfection and select the first option.
637c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    available_width = available_width_for_tabs_;
638c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
639c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
640c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (mini_tab_count > 0) {
641c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    available_width -= mini_tab_count * (Tab::GetMiniWidth() + kTabHOffset);
642c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_count -= mini_tab_count;
643c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tab_count == 0) {
644c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      *selected_width = *unselected_width = Tab::GetStandardSize().width();
645c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
646c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
647c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Account for gap between the last mini-tab and first non-mini-tab.
648c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    available_width -= mini_to_non_mini_gap_;
649c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
650c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
651c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Calculate the desired tab widths by dividing the available space into equal
652c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // portions.  Don't let tabs get larger than the "standard width" or smaller
653c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // than the minimum width for each type, respectively.
654c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const int total_offset = kTabHOffset * (tab_count - 1);
655c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const double desired_tab_width = std::min((static_cast<double>(
656c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      available_width - total_offset) / static_cast<double>(tab_count)),
657c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      static_cast<double>(Tab::GetStandardSize().width()));
658c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *unselected_width = std::max(desired_tab_width, min_unselected_width);
659c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *selected_width = std::max(desired_tab_width, min_selected_width);
660c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
661c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // When there are multiple tabs, we'll have one selected and some unselected
662c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // tabs.  If the desired width was between the minimum sizes of these types,
663c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // try to shrink the tabs with the smaller minimum.  For example, if we have
664c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // a strip of width 10 with 4 tabs, the desired width per tab will be 2.5.  If
665c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // selected tabs have a minimum width of 4 and unselected tabs have a minimum
666c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // width of 1, the above code would set *unselected_width = 2.5,
667c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // *selected_width = 4, which results in a total width of 11.5.  Instead, we
668c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // want to set *unselected_width = 2, *selected_width = 4, for a total width
669c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // of 10.
670c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_count > 1) {
671c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if ((min_unselected_width < min_selected_width) &&
672c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        (desired_tab_width < min_selected_width)) {
673c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Unselected width = (total width - selected width) / (num_tabs - 1)
674c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      *unselected_width = std::max(static_cast<double>(
675c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          available_width - total_offset - min_selected_width) /
676c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          static_cast<double>(tab_count - 1), min_unselected_width);
677c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else if ((min_unselected_width > min_selected_width) &&
678c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch               (desired_tab_width < min_unselected_width)) {
679c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Selected width = (total width - (unselected width * (num_tabs - 1)))
680c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      *selected_width = std::max(available_width - total_offset -
681c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          (min_unselected_width * (tab_count - 1)), min_selected_width);
682c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
683c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
684c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
685c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
686c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::ResizeLayoutTabs() {
687c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We've been called back after the TabStrip has been emptied out (probably
688c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // just prior to the window being destroyed). We need to do nothing here or
689c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // else GetTabAt below will crash.
690c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_count() == 0)
691c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
692c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
693c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // It is critically important that this is unhooked here, otherwise we will
694c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // keep spying on messages forever.
695c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  RemoveMessageLoopObserver();
696c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
697c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  in_tab_close_ = false;
698c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  available_width_for_tabs_ = -1;
699c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int mini_tab_count = GetMiniTabCount();
700c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (mini_tab_count == tab_count()) {
701c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Only mini-tabs, we know the tab widths won't have changed (all
702c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // mini-tabs have the same width), so there is nothing to do.
703c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
704c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
705c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Tab* first_tab  = GetTabAtTabDataIndex(mini_tab_count);
706c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double unselected, selected;
707c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetDesiredTabWidths(tab_count(), mini_tab_count, &unselected, &selected);
708ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // TODO: this is always selected, should it be 'selected : unselected'?
709ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  int w = Round(first_tab->IsActive() ? selected : selected);
710c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
711c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We only want to run the animation if we're not already at the desired
712c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // size.
713c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (abs(first_tab->width() - w) > 1)
714c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartResizeLayoutAnimation();
715c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
716c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
717c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::AddMessageLoopObserver() {
7183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (!mouse_watcher_.get()) {
7193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    mouse_watcher_.reset(
7203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        new views::MouseWatcher(this, this,
7213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                gfx::Insets(0, 0, kTabStripAnimationVSlop, 0)));
722c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
7233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  mouse_watcher_->Start();
724c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
725c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
726c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::RemoveMessageLoopObserver() {
7273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  mouse_watcher_.reset(NULL);
728c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
729c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
730c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Rect TabStrip::GetDropBounds(int drop_index,
731c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  bool drop_before,
732c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  bool* is_beneath) {
733c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(drop_index != -1);
734c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int center_x;
735c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (drop_index < tab_count()) {
736c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Tab* tab = GetTabAtTabDataIndex(drop_index);
737c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (drop_before)
738c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      center_x = tab->x() - (kTabHOffset / 2);
739c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    else
740c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      center_x = tab->x() + (tab->width() / 2);
741c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
742c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Tab* last_tab = GetTabAtTabDataIndex(drop_index - 1);
743c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    center_x = last_tab->x() + last_tab->width() + (kTabHOffset / 2);
744c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
745c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
746c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Mirror the center point if necessary.
74772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  center_x = GetMirroredXInView(center_x);
748c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
749c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Determine the screen bounds.
750c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point drop_loc(center_x - drop_indicator_width / 2,
751c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                      -drop_indicator_height);
752c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ConvertPointToScreen(this, &drop_loc);
753c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect drop_bounds(drop_loc.x(), drop_loc.y(), drop_indicator_width,
754c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        drop_indicator_height);
755c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
756c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If the rect doesn't fit on the monitor, push the arrow to the bottom.
757c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#if defined(OS_WIN)
75872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  gfx::Rect monitor_bounds = views::GetMonitorBoundsForRect(drop_bounds);
759c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *is_beneath = (monitor_bounds.IsEmpty() ||
760c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                 !monitor_bounds.Contains(drop_bounds));
761c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#else
762c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *is_beneath = false;
763c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  NOTIMPLEMENTED();
764c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif
765c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (*is_beneath)
766c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drop_bounds.Offset(0, drop_bounds.height() + height());
767c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
768c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return drop_bounds;
769c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
770c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
771c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::UpdateDropIndex(const DropTargetEvent& event) {
772c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If the UI layout is right-to-left, we need to mirror the mouse
773c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // coordinates since we calculate the drop index based on the
774c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // original (and therefore non-mirrored) positions of the tabs.
77572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  const int x = GetMirroredXInView(event.x());
776c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We don't allow replacing the urls of mini-tabs.
777c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = GetMiniTabCount(); i < tab_count(); ++i) {
778c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Tab* tab = GetTabAtTabDataIndex(i);
779c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const int tab_max_x = tab->x() + tab->width();
780c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const int hot_width = tab->width() / 3;
781c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (x < tab_max_x) {
782c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (x < tab->x() + hot_width)
783c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        SetDropIndex(i, true);
784c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      else if (x >= tab_max_x - hot_width)
785c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        SetDropIndex(i + 1, true);
786c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      else
787c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        SetDropIndex(i, false);
788c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
789c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
790c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
791c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
792c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The drop isn't over a tab, add it to the end.
793c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetDropIndex(tab_count(), true);
794c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
795c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
796c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::SetDropIndex(int tab_data_index, bool drop_before) {
797c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_data_index == -1) {
798c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (drop_info_.get())
799c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      drop_info_.reset(NULL);
800c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
801c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
802c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
803c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (drop_info_.get() && drop_info_->drop_index == tab_data_index &&
804c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      drop_info_->drop_before == drop_before) {
805c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
806c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
807c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
808c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool is_beneath;
809c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect drop_bounds = GetDropBounds(tab_data_index, drop_before,
810c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                        &is_beneath);
811c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
812c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!drop_info_.get()) {
813c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drop_info_.reset(new DropInfo(tab_data_index, drop_before, !is_beneath));
814c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
815c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drop_info_->drop_index = tab_data_index;
816c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drop_info_->drop_before = drop_before;
817c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (is_beneath == drop_info_->point_down) {
818c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      drop_info_->point_down = !is_beneath;
819c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      drop_info_->arrow_view->SetImage(
820c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          GetDropArrowImage(drop_info_->point_down));
821c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
822c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
823c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
824c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Reposition the window. Need to show it too as the window is initially
825c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // hidden.
826c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  drop_info_->arrow_window->SetBounds(drop_bounds);
827c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  drop_info_->arrow_window->Show();
828c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
829c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
830c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStrip::GetDropEffect(const views::DropTargetEvent& event) {
83172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  const int source_ops = event.source_operations();
83272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (source_ops & ui::DragDropTypes::DRAG_COPY)
83372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return ui::DragDropTypes::DRAG_COPY;
83472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (source_ops & ui::DragDropTypes::DRAG_LINK)
83572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    return ui::DragDropTypes::DRAG_LINK;
83672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  return ui::DragDropTypes::DRAG_MOVE;
837c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
838c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
839c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
840c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochSkBitmap* TabStrip::GetDropArrowImage(bool is_down) {
841c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return ResourceBundle::GetSharedInstance().GetBitmapNamed(
842c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      is_down ? IDR_TAB_DROP_DOWN : IDR_TAB_DROP_UP);
843c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
844c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
845c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStrip::DropInfo ----------------------------------------------------------
846c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
847c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabStrip::DropInfo::DropInfo(int drop_index, bool drop_before, bool point_down)
848c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : drop_index(drop_index),
849c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      drop_before(drop_before),
850c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      point_down(point_down) {
851c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  arrow_view = new views::ImageView;
852c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  arrow_view->SetImage(GetDropArrowImage(point_down));
853c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
854ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  views::Widget::CreateParams params(views::Widget::CreateParams::TYPE_POPUP);
855ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  params.keep_on_top = true;
856ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  params.transparent = true;
857ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  params.accept_events = false;
858ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  params.can_activate = false;
859ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  arrow_window = views::Widget::CreateWidget(params);
860c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  arrow_window->Init(
861c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NULL,
862c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gfx::Rect(0, 0, drop_indicator_width, drop_indicator_height));
863c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  arrow_window->SetContentsView(arrow_view);
864c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
865c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
866c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabStrip::DropInfo::~DropInfo() {
867c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Close eventually deletes the window, which deletes arrow_view too.
868c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  arrow_window->Close();
869c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
870c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
871c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch///////////////////////////////////////////////////////////////////////////////
872c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
873c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Called from:
874c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// - BasicLayout
875c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// - Tab insertion/removal
876c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// - Tab reorder
877c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::GenerateIdealBounds() {
878c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int non_closing_tab_count = 0;
879c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int mini_tab_count = 0;
880c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0; i < tab_count(); ++i) {
881c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BaseTab* tab = base_tab_at_tab_index(i);
882c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!tab->closing()) {
883c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      ++non_closing_tab_count;
884c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (tab->data().mini)
885c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        mini_tab_count++;
886c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
887c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
888c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
889c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double unselected, selected;
890c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetDesiredTabWidths(non_closing_tab_count, mini_tab_count, &unselected,
891c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                      &selected);
892c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
893c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  current_unselected_width_ = unselected;
894c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  current_selected_width_ = selected;
895c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
896c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // NOTE: This currently assumes a tab's height doesn't differ based on
897c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // selected state or the number of tabs in the strip!
898c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int tab_height = Tab::GetStandardSize().height();
899c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double tab_x = 0;
900c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool last_was_mini = false;
901c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0; i < tab_count(); ++i) {
902ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    Tab* tab = GetTabAtTabDataIndex(i);
903c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!tab->closing()) {
904c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      double tab_width = unselected;
905c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (tab->data().mini) {
906c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        tab_width = Tab::GetMiniWidth();
907c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else {
908c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        if (last_was_mini) {
909c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          // Give a bigger gap between mini and non-mini tabs.
910c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          tab_x += mini_to_non_mini_gap_;
911c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        }
912ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        if (tab->IsActive())
913c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          tab_width = selected;
914c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
915c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      double end_of_tab = tab_x + tab_width;
916c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      int rounded_tab_x = Round(tab_x);
917c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      set_ideal_bounds(i,
918c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          gfx::Rect(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
919c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    tab_height));
920c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tab_x = end_of_tab + kTabHOffset;
921c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      last_was_mini = tab->data().mini;
922c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
923c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
924c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
925c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Update bounds of new tab button.
926c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int new_tab_x;
927c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int new_tab_y = browser_defaults::kSizeTabButtonToTopOfTabStrip ?
928c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      0 : kNewTabButtonVOffset;
929c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (abs(Round(unselected) - Tab::GetStandardSize().width()) > 1 &&
930c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      !in_tab_close_) {
931c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We're shrinking tabs, so we need to anchor the New Tab button to the
932c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // right edge of the TabStrip's bounds, rather than the right edge of the
933c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // right-most Tab, otherwise it'll bounce when animating.
934c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    new_tab_x = width() - newtab_button_bounds_.width();
935c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
936c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    new_tab_x = Round(tab_x - kTabHOffset) + kNewTabButtonHOffset;
937c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
938c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  newtab_button_bounds_.set_origin(gfx::Point(new_tab_x, new_tab_y));
939c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
940c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
941c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::StartResizeLayoutAnimation() {
942c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  PrepareForAnimation();
943c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GenerateIdealBounds();
944c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  AnimateToIdealBounds();
945c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
946c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
947c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::StartMiniTabAnimation() {
948c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  in_tab_close_ = false;
949c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  available_width_for_tabs_ = -1;
950c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
951c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  PrepareForAnimation();
952c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
953c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GenerateIdealBounds();
954c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  AnimateToIdealBounds();
955c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
956c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
957c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStrip::StartMouseInitiatedRemoveTabAnimation(int model_index) {
958c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The user initiated the close. We want to persist the bounds of all the
959c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // existing tabs, so we manually shift ideal_bounds then animate.
960c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int tab_data_index = ModelIndexToTabIndex(model_index);
961c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(tab_data_index != tab_count());
962c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  BaseTab* tab_closing = base_tab_at_tab_index(tab_data_index);
963c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int delta = tab_closing->width() + kTabHOffset;
964c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_closing->data().mini && model_index + 1 < GetModelCount() &&
965c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      !GetBaseTabAtModelIndex(model_index + 1)->data().mini) {
966c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    delta += mini_to_non_mini_gap_;
967c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
968c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
969c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = tab_data_index + 1; i < tab_count(); ++i) {
970c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    BaseTab* tab = base_tab_at_tab_index(i);
971c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!tab->closing()) {
972c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gfx::Rect bounds = ideal_bounds(i);
973c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      bounds.set_x(bounds.x() - delta);
974c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      set_ideal_bounds(i, bounds);
975c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
976c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
977c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
978c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  newtab_button_bounds_.set_x(newtab_button_bounds_.x() - delta);
979c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
980c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  PrepareForAnimation();
981c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
982c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Mark the tab as closing.
983c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab_closing->set_closing(true);
984c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
985c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  AnimateToIdealBounds();
986c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
987c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect tab_bounds = tab_closing->bounds();
988c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (type() == HORIZONTAL_TAB_STRIP)
989c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_bounds.set_width(0);
990c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  else
991c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_bounds.set_height(0);
992c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bounds_animator().AnimateViewTo(tab_closing, tab_bounds);
993c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
994c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Register delegate to do cleanup when done, BoundsAnimator takes
995c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // ownership of RemoveTabDelegate.
996c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bounds_animator().SetAnimationDelegate(tab_closing,
997c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                         CreateRemoveTabDelegate(tab_closing),
998c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                         true);
999c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1000c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1001c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStrip::GetMiniTabCount() const {
1002c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int mini_count = 0;
1003c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0; i < tab_count(); ++i) {
1004c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (base_tab_at_tab_index(i)->data().mini)
1005c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      mini_count++;
1006c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    else
1007c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return mini_count;
1008c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1009c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return mini_count;
1010c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1011c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1012c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStrip::GetAvailableWidthForTabs(Tab* last_tab) const {
1013c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return last_tab->x() + last_tab->width();
1014c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1015c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1016c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool TabStrip::IsPointInTab(Tab* tab,
1017c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                            const gfx::Point& point_in_tabstrip_coords) {
1018c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point point_in_tab_coords(point_in_tabstrip_coords);
1019c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  View::ConvertPointToView(this, tab, &point_in_tab_coords);
1020c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return tab->HitTest(point_in_tab_coords);
1021c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1022