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/gtk/tabs/tab_strip_gtk.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <algorithm>
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/i18n/rtl.h"
10c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h"
1121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "base/utf_string_conversions.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/autocomplete/autocomplete.h"
1321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/autocomplete/autocomplete_classifier.h"
1421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/autocomplete/autocomplete_match.h"
1521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile.h"
163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/tabs/tab_strip_model_delegate.h"
17ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/themes/theme_service.h"
184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/browser/ui/browser.h"
194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "chrome/browser/ui/browser_navigator.h"
2072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/browser_window_gtk.h"
2172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/custom_button.h"
22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/browser/ui/gtk/gtk_theme_service.h"
2372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/gtk_util.h"
2472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "chrome/browser/ui/gtk/tabs/dragged_tab_controller_gtk.h"
2521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
26dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/browser/tab_contents/tab_contents.h"
27dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/common/notification_service.h"
28dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen#include "content/common/notification_type.h"
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/app_resources.h"
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "grit/theme_resources.h"
313f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "ui/base/animation/animation_delegate.h"
323f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "ui/base/animation/slide_animation.h"
3372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/dragdrop/gtk_dnd_util.h"
3472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/base/resource/resource_bundle.h"
3572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/gtk_util.h"
3672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen#include "ui/gfx/point.h"
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kDefaultAnimationDurationMs = 100;
41c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kResizeLayoutAnimationDurationMs = 166;
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kReorderAnimationDurationMs = 166;
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kAnimateToBoundsDurationMs = 150;
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kMiniTabAnimationDurationMs = 150;
45c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
46c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kNewTabButtonHOffset = -5;
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kNewTabButtonVOffset = 5;
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The delay between when the mouse leaves the tabstrip and the resize animation
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// is started.
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kResizeTabsTimeMs = 300;
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The range outside of the tabstrip where the pointer must enter/leave to
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// start/stop the resize animation.
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kTabStripAnimationVSlop = 40;
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kHorizontalMoveThreshold = 16;  // pixels
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The horizontal offset from one tab to the next, which results in overlapping
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// tabs.
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kTabHOffset = -16;
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// A linux specific menu item for toggling window decorations.
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kShowWindowDecorationsCommand = 200;
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Size of the drop indicator.
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic int drop_indicator_width;
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic int drop_indicator_height;
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
70c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochinline int Round(double x) {
71c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return static_cast<int>(x + 0.5);
72c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
73c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
74c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// widget->allocation is not guaranteed to be set.  After window creation,
75c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// we pick up the normal bounds by connecting to the configure-event signal.
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Rect GetInitialWidgetBounds(GtkWidget* widget) {
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GtkRequisition request;
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_size_request(widget, &request);
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return gfx::Rect(0, 0, request.width, request.height);
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Sort rectangles based on their x position.  We don't care about y position
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// so we don't bother breaking ties.
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint CompareGdkRectangles(const void* p1, const void* p2) {
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int p1_x = static_cast<const GdkRectangle*>(p1)->x;
86c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int p2_x = static_cast<const GdkRectangle*>(p2)->x;
87c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (p1_x < p2_x)
88c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return -1;
89c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  else if (p1_x == p2_x)
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return 0;
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return 1;
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
94ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool GdkRectMatchesTabFaviconBounds(const GdkRectangle& gdk_rect, TabGtk* tab) {
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect favicon_bounds = tab->favicon_bounds();
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return gdk_rect.x == favicon_bounds.x() + tab->x() &&
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gdk_rect.y == favicon_bounds.y() + tab->y() &&
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gdk_rect.width == favicon_bounds.width() &&
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gdk_rect.height == favicon_bounds.height();
100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabAnimation
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//  A base class for all TabStrip animations.
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
1103f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsenclass TabStripGtk::TabAnimation : public ui::AnimationDelegate {
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  friend class TabStripGtk;
113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Possible types of animation.
115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  enum Type {
116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    INSERT,
117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    REMOVE,
118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    MOVE,
119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    RESIZE,
120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    MINI,
121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    MINI_MOVE
122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  };
123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabAnimation(TabStripGtk* tabstrip, Type type)
125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      : tabstrip_(tabstrip),
126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        animation_(this),
127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        start_selected_width_(0),
128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        start_unselected_width_(0),
129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        end_selected_width_(0),
130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        end_unselected_width_(0),
131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        layout_on_completion_(false),
132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        type_(type) {
133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual ~TabAnimation() {}
135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Type type() const { return type_; }
137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void Start() {
139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    animation_.SetSlideDuration(GetDuration());
1403f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen    animation_.SetTweenType(ui::Tween::EASE_OUT);
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!animation_.IsShowing()) {
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      animation_.Reset();
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      animation_.Show();
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void Stop() {
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    animation_.Stop();
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void set_layout_on_completion(bool layout_on_completion) {
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    layout_on_completion_ = layout_on_completion;
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Retrieves the width for the Tab at the specified index if an animation is
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // active.
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  static double GetCurrentTabWidth(TabStripGtk* tabstrip,
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   TabStripGtk::TabAnimation* animation,
159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   int index) {
160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabGtk* tab = tabstrip->GetTabAt(index);
161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    double tab_width;
162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tab->mini()) {
163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tab_width = TabGtk::GetMiniWidth();
164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      double unselected, selected;
166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tabstrip->GetCurrentTabWidths(&unselected, &selected);
167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tab_width = tab->IsSelected() ? selected : unselected;
168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (animation) {
171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      double specified_tab_width = animation->GetWidthForTab(index);
172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (specified_tab_width != -1)
173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        tab_width = specified_tab_width;
174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return tab_width;
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1793f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // Overridden from ui::AnimationDelegate:
1803f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  virtual void AnimationProgressed(const ui::Animation* animation) {
181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tabstrip_->AnimationLayout(end_unselected_width_);
182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1843f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  virtual void AnimationEnded(const ui::Animation* animation) {
185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tabstrip_->FinishAnimation(this, layout_on_completion_);
186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // This object is destroyed now, so we can't do anything else after this.
187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1893f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  virtual void AnimationCanceled(const ui::Animation* animation) {
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    AnimationEnded(animation);
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns the gap before the tab at the specified index. Subclass if during
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // an animation you need to insert a gap before a tab.
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual double GetGapWidth(int index) {
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return 0;
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch protected:
200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns the duration of the animation.
201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual int GetDuration() const {
202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return kDefaultAnimationDurationMs;
203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Subclasses override to return the width of the Tab at the specified index
206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // at the current animation frame. -1 indicates the default width should be
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // used for the Tab.
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual double GetWidthForTab(int index) const {
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return -1;  // Use default.
210c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Figure out the desired start and end widths for the specified pre- and
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // post- animation tab counts.
214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void GenerateStartAndEndWidths(int start_tab_count, int end_tab_count,
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                 int start_mini_count,
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                 int end_mini_count) {
217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tabstrip_->GetDesiredTabWidths(start_tab_count, start_mini_count,
218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   &start_unselected_width_,
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   &start_selected_width_);
220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    double standard_tab_width =
221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        static_cast<double>(TabRendererGtk::GetStandardSize().width());
222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if ((end_tab_count - start_tab_count) > 0 &&
224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        start_unselected_width_ < standard_tab_width) {
225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      double minimum_tab_width = static_cast<double>(
226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          TabRendererGtk::GetMinimumUnselectedSize().width());
227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      start_unselected_width_ -= minimum_tab_width / start_tab_count;
228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tabstrip_->GenerateIdealBounds();
231c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tabstrip_->GetDesiredTabWidths(end_tab_count, end_mini_count,
232c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   &end_unselected_width_,
233c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   &end_selected_width_);
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabStripGtk* tabstrip_;
2373f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  ui::SlideAnimation animation_;
238c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double start_selected_width_;
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double start_unselected_width_;
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double end_selected_width_;
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double end_unselected_width_;
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // True if a complete re-layout is required upon completion of the animation.
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Subclasses set this if they don't perform a complete layout
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // themselves and canceling the animation may leave the strip in an
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // inconsistent state.
249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool layout_on_completion_;
250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const Type type_;
252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(TabAnimation);
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Handles insertion of a Tab at |index|.
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass InsertTabAnimation : public TabStripGtk::TabAnimation {
260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  explicit InsertTabAnimation(TabStripGtk* tabstrip, int index)
262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      : TabAnimation(tabstrip, INSERT),
263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        index_(index) {
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int tab_count = tabstrip->GetTabCount();
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int end_mini_count = tabstrip->GetMiniTabCount();
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int start_mini_count = end_mini_count;
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (index < end_mini_count)
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      start_mini_count--;
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GenerateStartAndEndWidths(tab_count - 1, tab_count, start_mini_count,
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              end_mini_count);
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual ~InsertTabAnimation() {}
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch protected:
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Overridden from TabStripGtk::TabAnimation:
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual double GetWidthForTab(int index) const {
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (index == index_) {
278ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      bool is_selected = tabstrip_->model()->active_index() == index;
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      double start_width, target_width;
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (index < tabstrip_->GetMiniTabCount()) {
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        start_width = TabGtk::GetMinimumSelectedSize().width();
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        target_width = TabGtk::GetMiniWidth();
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else {
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        target_width =
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            is_selected ? end_unselected_width_ : end_selected_width_;
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        start_width =
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            is_selected ? TabGtk::GetMinimumSelectedSize().width() :
288c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                          TabGtk::GetMinimumUnselectedSize().width();
289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      double delta = target_width - start_width;
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (delta > 0)
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return start_width + (delta * animation_.GetCurrentValue());
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return start_width;
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tabstrip_->GetTabAt(index)->mini())
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return TabGtk::GetMiniWidth();
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tabstrip_->GetTabAt(index)->IsSelected()) {
302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      double delta = end_selected_width_ - start_selected_width_;
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return start_selected_width_ + (delta * animation_.GetCurrentValue());
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    double delta = end_unselected_width_ - start_unselected_width_;
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return start_unselected_width_ + (delta * animation_.GetCurrentValue());
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int index_;
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(InsertTabAnimation);
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Handles removal of a Tab from |index|
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass RemoveTabAnimation : public TabStripGtk::TabAnimation {
320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  RemoveTabAnimation(TabStripGtk* tabstrip, int index, TabContents* contents)
322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      : TabAnimation(tabstrip, REMOVE),
323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        index_(index) {
324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int tab_count = tabstrip->GetTabCount();
325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int start_mini_count = tabstrip->GetMiniTabCount();
326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int end_mini_count = start_mini_count;
327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (index < start_mini_count)
328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      end_mini_count--;
329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GenerateStartAndEndWidths(tab_count, tab_count - 1, start_mini_count,
330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              end_mini_count);
331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // If the last non-mini-tab is being removed we force a layout on
332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // completion. This is necessary as the value returned by GetTabHOffset
333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // changes once the tab is actually removed (which happens at the end of
334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // the animation), and unless we layout GetTabHOffset won't be called after
335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // the removal.
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We do the same when the last mini-tab is being removed for the same
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // reason.
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    set_layout_on_completion(start_mini_count > 0 &&
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                             (end_mini_count == 0 ||
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              (start_mini_count == end_mini_count &&
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               tab_count == start_mini_count + 1)));
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual ~RemoveTabAnimation() {}
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Returns the index of the tab being removed.
347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int index() const { return index_; }
348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch protected:
350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Overridden from TabStripGtk::TabAnimation:
351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual double GetWidthForTab(int index) const {
352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabGtk* tab = tabstrip_->GetTabAt(index);
353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (index == index_) {
355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // The tab(s) being removed are gradually shrunken depending on the state
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // of the animation.
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (tab->mini()) {
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return animation_.CurrentValueBetween(TabGtk::GetMiniWidth(),
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                              -kTabHOffset);
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Removed animated Tabs are never selected.
363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      double start_width = start_unselected_width_;
364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Make sure target_width is at least abs(kTabHOffset), otherwise if
365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // less than kTabHOffset during layout tabs get negatively offset.
366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      double target_width =
367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          std::max(abs(kTabHOffset),
368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   TabGtk::GetMinimumUnselectedSize().width() + kTabHOffset);
369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return animation_.CurrentValueBetween(start_width, target_width);
370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tab->mini())
373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return TabGtk::GetMiniWidth();
374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tabstrip_->available_width_for_tabs_ != -1 &&
376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        index_ != tabstrip_->GetTabCount() - 1) {
377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return TabStripGtk::TabAnimation::GetWidthForTab(index);
378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // All other tabs are sized according to the start/end widths specified at
381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // the start of the animation.
382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tab->IsSelected()) {
383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      double delta = end_selected_width_ - start_selected_width_;
384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return start_selected_width_ + (delta * animation_.GetCurrentValue());
385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    double delta = end_unselected_width_ - start_unselected_width_;
388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return start_unselected_width_ + (delta * animation_.GetCurrentValue());
389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
3913f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  virtual void AnimationEnded(const ui::Animation* animation) {
392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tabstrip_->RemoveTabAt(index_);
393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabStripGtk::TabAnimation::AnimationEnded(animation);
394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int index_;
398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(RemoveTabAnimation);
400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Handles the movement of a Tab from one position to another.
405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass MoveTabAnimation : public TabStripGtk::TabAnimation {
406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  MoveTabAnimation(TabStripGtk* tabstrip, int tab_a_index, int tab_b_index)
408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      : TabAnimation(tabstrip, MOVE),
409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        start_tab_a_bounds_(tabstrip_->GetIdealBounds(tab_b_index)),
410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        start_tab_b_bounds_(tabstrip_->GetIdealBounds(tab_a_index)) {
411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_a_ = tabstrip_->GetTabAt(tab_a_index);
412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_b_ = tabstrip_->GetTabAt(tab_b_index);
413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Since we don't do a full TabStrip re-layout, we need to force a full
415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // layout upon completion since we're not guaranteed to be in a good state
416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // if for example the animation is canceled.
417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    set_layout_on_completion(true);
418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual ~MoveTabAnimation() {}
420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
4213f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // Overridden from ui::AnimationDelegate:
4223f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  virtual void AnimationProgressed(const ui::Animation* animation) {
423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Position Tab A
424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    double distance = start_tab_b_bounds_.x() - start_tab_a_bounds_.x();
425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    double delta = distance * animation_.GetCurrentValue();
426c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    double new_x = start_tab_a_bounds_.x() + delta;
427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect bounds(Round(new_x), start_tab_a_bounds_.y(), tab_a_->width(),
428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        tab_a_->height());
429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tabstrip_->SetTabBounds(tab_a_, bounds);
430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Position Tab B
432c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    distance = start_tab_a_bounds_.x() - start_tab_b_bounds_.x();
433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    delta = distance * animation_.GetCurrentValue();
434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    new_x = start_tab_b_bounds_.x() + delta;
435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bounds = gfx::Rect(Round(new_x), start_tab_b_bounds_.y(), tab_b_->width(),
436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        tab_b_->height());
437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tabstrip_->SetTabBounds(tab_b_, bounds);
438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch protected:
441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Overridden from TabStripGtk::TabAnimation:
442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual int GetDuration() const { return kReorderAnimationDurationMs; }
443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The two tabs being exchanged.
446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabGtk* tab_a_;
447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabGtk* tab_b_;
448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // ...and their bounds.
450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect start_tab_a_bounds_;
451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect start_tab_b_bounds_;
452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(MoveTabAnimation);
454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Handles the animated resize layout of the entire TabStrip from one width
459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// to another.
460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass ResizeLayoutAnimation : public TabStripGtk::TabAnimation {
461c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  explicit ResizeLayoutAnimation(TabStripGtk* tabstrip)
463c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      : TabAnimation(tabstrip, RESIZE) {
464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int tab_count = tabstrip->GetTabCount();
465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int mini_tab_count = tabstrip->GetMiniTabCount();
466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GenerateStartAndEndWidths(tab_count, tab_count, mini_tab_count,
467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              mini_tab_count);
468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    InitStartState();
469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual ~ResizeLayoutAnimation() {}
471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
4723f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // Overridden from ui::AnimationDelegate:
4733f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  virtual void AnimationEnded(const ui::Animation* animation) {
474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tabstrip_->needs_resize_layout_ = false;
475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabStripGtk::TabAnimation::AnimationEnded(animation);
476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch protected:
479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Overridden from TabStripGtk::TabAnimation:
480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual int GetDuration() const {
481c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return kResizeLayoutAnimationDurationMs;
482c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
484c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual double GetWidthForTab(int index) const {
485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabGtk* tab = tabstrip_->GetTabAt(index);
486c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
487c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tab->mini())
488c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return TabGtk::GetMiniWidth();
489c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
490c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tab->IsSelected()) {
491c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return animation_.CurrentValueBetween(start_selected_width_,
492c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                            end_selected_width_);
493c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
494c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
495c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return animation_.CurrentValueBetween(start_unselected_width_,
496c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          end_unselected_width_);
497c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
500c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We need to start from the current widths of the Tabs as they were last
501c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // laid out, _not_ the last known good state, which is what'll be done if we
502c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // don't measure the Tab sizes here and just go with the default TabAnimation
503c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // behavior...
504c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void InitStartState() {
505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (int i = 0; i < tabstrip_->GetTabCount(); ++i) {
506c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      TabGtk* current_tab = tabstrip_->GetTabAt(i);
507c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!current_tab->mini()) {
508c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        if (current_tab->IsSelected()) {
509c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          start_selected_width_ = current_tab->width();
510c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        } else {
511c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          start_unselected_width_ = current_tab->width();
512c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        }
513c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
514c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
515c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
516c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
517c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(ResizeLayoutAnimation);
518c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
519c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
520c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Handles a tabs mini-state changing while the tab does not change position
521c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// in the model.
522c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass MiniTabAnimation : public TabStripGtk::TabAnimation {
523c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
524c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  explicit MiniTabAnimation(TabStripGtk* tabstrip, int index)
525c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      : TabAnimation(tabstrip, MINI),
526c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        index_(index) {
527c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int tab_count = tabstrip->GetTabCount();
528c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int start_mini_count = tabstrip->GetMiniTabCount();
529c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int end_mini_count = start_mini_count;
530c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tabstrip->GetTabAt(index)->mini())
531c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      start_mini_count--;
532c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    else
533c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      start_mini_count++;
534c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tabstrip_->GetTabAt(index)->set_animating_mini_change(true);
535c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GenerateStartAndEndWidths(tab_count, tab_count, start_mini_count,
536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              end_mini_count);
537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
538c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch protected:
540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Overridden from TabStripGtk::TabAnimation:
541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual int GetDuration() const {
542c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return kMiniTabAnimationDurationMs;
543c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
544c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
545c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual double GetWidthForTab(int index) const {
546c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabGtk* tab = tabstrip_->GetTabAt(index);
547c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
548c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (index == index_) {
549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (tab->mini()) {
550c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return animation_.CurrentValueBetween(
551c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            start_selected_width_,
552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            static_cast<double>(TabGtk::GetMiniWidth()));
553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else {
554c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return animation_.CurrentValueBetween(
555c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            static_cast<double>(TabGtk::GetMiniWidth()),
556c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            end_selected_width_);
557c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
558c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else if (tab->mini()) {
559c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return TabGtk::GetMiniWidth();
560c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
561c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
562c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tab->IsSelected()) {
563c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return animation_.CurrentValueBetween(start_selected_width_,
564c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                            end_selected_width_);
565c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
566c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
567c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return animation_.CurrentValueBetween(start_unselected_width_,
568c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          end_unselected_width_);
569c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
570c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
571c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Index of the tab whose mini-state changed.
573c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int index_;
574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
575c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(MiniTabAnimation);
576c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
577c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
578c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
579c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
580c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Handles the animation when a tabs mini-state changes and the tab moves as a
581c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// result.
582c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass MiniMoveAnimation : public TabStripGtk::TabAnimation {
583c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
584c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  explicit MiniMoveAnimation(TabStripGtk* tabstrip,
585c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                             int from_index,
586c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                             int to_index,
587c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                             const gfx::Rect& start_bounds)
588c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      : TabAnimation(tabstrip, MINI_MOVE),
589c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        tab_(tabstrip->GetTabAt(to_index)),
590c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        start_bounds_(start_bounds),
591c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        from_index_(from_index),
592c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        to_index_(to_index) {
593c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int tab_count = tabstrip->GetTabCount();
594c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int start_mini_count = tabstrip->GetMiniTabCount();
595c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int end_mini_count = start_mini_count;
596c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tabstrip->GetTabAt(to_index)->mini())
597c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      start_mini_count--;
598c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    else
599c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      start_mini_count++;
600c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GenerateStartAndEndWidths(tab_count, tab_count, start_mini_count,
601c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              end_mini_count);
602c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    target_bounds_ = tabstrip->GetIdealBounds(to_index);
603c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_->set_animating_mini_change(true);
604c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
605c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6063f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  // Overridden from ui::AnimationDelegate:
6073f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  virtual void AnimationProgressed(const ui::Animation* animation) {
608c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Do the normal layout.
609c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabAnimation::AnimationProgressed(animation);
610c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
611c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Then special case the position of the tab being moved.
612c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int x = animation_.CurrentValueBetween(start_bounds_.x(),
613c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                           target_bounds_.x());
614c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int width = animation_.CurrentValueBetween(start_bounds_.width(),
615c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                               target_bounds_.width());
616c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect tab_bounds(x, start_bounds_.y(), width,
617c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                         start_bounds_.height());
618c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tabstrip_->SetTabBounds(tab_, tab_bounds);
619c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
620c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
6213f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen  virtual void AnimationEnded(const ui::Animation* animation) {
622c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tabstrip_->needs_resize_layout_ = false;
623c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabStripGtk::TabAnimation::AnimationEnded(animation);
624c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
625c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
626c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual double GetGapWidth(int index) {
627c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (to_index_ < from_index_) {
628c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // The tab was made mini.
629c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (index == to_index_) {
630c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        double current_size =
631c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            animation_.CurrentValueBetween(0, target_bounds_.width());
632c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        if (current_size < -kTabHOffset)
633c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          return -(current_size + kTabHOffset);
634c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else if (index == from_index_ + 1) {
635c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return animation_.CurrentValueBetween(start_bounds_.width(), 0);
636c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
637c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
638c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // The tab was was made a normal tab.
639c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (index == from_index_) {
640c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return animation_.CurrentValueBetween(
641c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            TabGtk::GetMiniWidth() + kTabHOffset, 0);
642c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
643c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
644c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return 0;
645c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
646c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
647c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch protected:
648c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Overridden from TabStripGtk::TabAnimation:
649c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual int GetDuration() const { return kReorderAnimationDurationMs; }
650c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
651c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual double GetWidthForTab(int index) const {
652c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabGtk* tab = tabstrip_->GetTabAt(index);
653c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
654c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (index == to_index_)
655c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return animation_.CurrentValueBetween(0, target_bounds_.width());
656c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
657c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tab->mini())
658c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return TabGtk::GetMiniWidth();
659c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
660c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tab->IsSelected()) {
661c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return animation_.CurrentValueBetween(start_selected_width_,
662c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                            end_selected_width_);
663c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
664c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
665c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return animation_.CurrentValueBetween(start_unselected_width_,
666c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          end_unselected_width_);
667c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
668c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
669c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
670c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The tab being moved.
671c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabGtk* tab_;
672c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
673c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Initial bounds of tab_.
674c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect start_bounds_;
675c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
676c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Target bounds.
677c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect target_bounds_;
678c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
679c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Start and end indices of the tab.
680c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int from_index_;
681c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int to_index_;
682c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
683c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DISALLOW_COPY_AND_ASSIGN(MiniMoveAnimation);
684c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
685c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
686c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
687c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStripGtk, public:
688c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
689c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
690c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int TabStripGtk::mini_to_non_mini_gap_ = 3;
691c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
692c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabStripGtk::TabStripGtk(TabStripModel* model, BrowserWindowGtk* window)
693c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : current_unselected_width_(TabGtk::GetStandardSize().width()),
694c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      current_selected_width_(TabGtk::GetStandardSize().width()),
695c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      available_width_for_tabs_(-1),
696c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      needs_resize_layout_(false),
697c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tab_vertical_offset_(0),
698c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      model_(model),
699c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      window_(window),
700ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      theme_service_(GtkThemeService::GetFrom(model->profile())),
701c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      resize_layout_factory_(this),
702c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      added_as_message_loop_observer_(false) {
703ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  theme_service_->InitThemesFor(this);
704c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED,
705c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                 NotificationService::AllSources());
706c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
707c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
708c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabStripGtk::~TabStripGtk() {
709c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  model_->RemoveObserver(this);
710c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tabstrip_.Destroy();
711c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
712c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Free any remaining tabs.  This is needed to free the very last tab,
713c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // because it is not animated on close.  This also happens when all of the
714c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // tabs are closed at once.
715c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<TabData>::iterator iterator = tab_data_.begin();
716c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (; iterator < tab_data_.end(); iterator++) {
717c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    delete iterator->tab;
718c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
719c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
720c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab_data_.clear();
721c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
722c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make sure we unhook ourselves as a message loop observer so that we don't
723c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // crash in the case where the user closes the last tab in a window.
724c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  RemoveMessageLoopObserver();
725c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
726c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
727c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::Init() {
728c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  model_->AddObserver(this);
729c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
730c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tabstrip_.Own(gtk_fixed_new());
731c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ViewIDUtil::SetID(tabstrip_.get(), VIEW_ID_TAB_STRIP);
732c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We want the tab strip to be horizontally shrinkable, so that the Chrome
733c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // window can be resized freely.
734c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_set_size_request(tabstrip_.get(), 0,
735c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                              TabGtk::GetMinimumUnselectedSize().height());
736c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_set_app_paintable(tabstrip_.get(), TRUE);
737c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_drag_dest_set(tabstrip_.get(), GTK_DEST_DEFAULT_ALL,
738c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    NULL, 0,
739c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    static_cast<GdkDragAction>(
740c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK));
74172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  static const int targets[] = { ui::TEXT_URI_LIST,
74272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                 ui::NETSCAPE_URL,
74372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                 ui::TEXT_PLAIN,
744c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                 -1 };
74572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  ui::SetDestTargetList(tabstrip_.get(), targets);
746c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
747c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_signal_connect(tabstrip_.get(), "expose-event",
748c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnExposeThunk), this);
749c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_signal_connect(tabstrip_.get(), "size-allocate",
750c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnSizeAllocateThunk), this);
751c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_signal_connect(tabstrip_.get(), "drag-motion",
752c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnDragMotionThunk), this);
753c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_signal_connect(tabstrip_.get(), "drag-drop",
754c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnDragDropThunk), this);
755c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_signal_connect(tabstrip_.get(), "drag-leave",
756c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnDragLeaveThunk), this);
757c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_signal_connect(tabstrip_.get(), "drag-data-received",
758c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnDragDataReceivedThunk), this);
759c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
760c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  newtab_button_.reset(MakeNewTabButton());
761c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
762c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_show_all(tabstrip_.get());
763c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
764c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bounds_ = GetInitialWidgetBounds(tabstrip_.get());
765c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
766c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (drop_indicator_width == 0) {
767c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Direction doesn't matter, both images are the same size.
768c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GdkPixbuf* drop_image = GetDropArrowImage(true);
769c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drop_indicator_width = gdk_pixbuf_get_width(drop_image);
770c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drop_indicator_height = gdk_pixbuf_get_height(drop_image);
771c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
772c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
773c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ViewIDUtil::SetDelegateForWidget(widget(), this);
774c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
775c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
776c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::Show() {
777c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_show(tabstrip_.get());
778c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
779c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
780c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::Hide() {
781c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_hide(tabstrip_.get());
782c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
783c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
78421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenbool TabStripGtk::IsActiveDropTarget() const {
78521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  for (int i = 0; i < GetTabCount(); ++i) {
78621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    TabGtk* tab = GetTabAt(i);
78721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    if (tab->dragging())
78821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      return true;
78921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  }
79021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  return false;
79121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen}
79221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen
793c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::Layout() {
794c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Called from:
795c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // - window resize
796c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // - animation completion
797c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StopAnimation();
798c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
799c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GenerateIdealBounds();
800c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int tab_count = GetTabCount();
801c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int tab_right = 0;
802c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0; i < tab_count; ++i) {
803c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const gfx::Rect& bounds = tab_data_.at(i).ideal_bounds;
804c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabGtk* tab = GetTabAt(i);
805c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab->set_animating_mini_change(false);
806c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab->set_vertical_offset(tab_vertical_offset_);
807c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SetTabBounds(tab, bounds);
808c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_right = bounds.right();
809c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_right += GetTabHOffset(i + 1);
810c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
811c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
812c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  LayoutNewTabButton(static_cast<double>(tab_right), current_unselected_width_);
813c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
814c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
815c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::SchedulePaint() {
816c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_queue_draw(tabstrip_.get());
817c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
818c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
819c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::SetBounds(const gfx::Rect& bounds) {
820c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bounds_ = bounds;
821c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
822c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
823c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::UpdateLoadingAnimations() {
824c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0, index = 0; i < GetTabCount(); ++i, ++index) {
825c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabGtk* current_tab = GetTabAt(i);
826c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (current_tab->closing()) {
827c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      --index;
828c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
829c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      TabRendererGtk::AnimationState state;
830201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      TabContentsWrapper* contents = model_->GetTabContentsAt(index);
831201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      if (!contents || !contents->tab_contents()->is_loading()) {
832c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        state = TabGtk::ANIMATION_NONE;
833201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch      } else if (contents->tab_contents()->waiting_for_response()) {
834c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        state = TabGtk::ANIMATION_WAITING;
835c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else {
836c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        state = TabGtk::ANIMATION_LOADING;
837c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
838c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (current_tab->ValidateLoadingAnimation(state)) {
839c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // Queue the tab's icon area to be repainted.
840c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        gfx::Rect favicon_bounds = current_tab->favicon_bounds();
841c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        gtk_widget_queue_draw_area(tabstrip_.get(),
842c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            favicon_bounds.x() + current_tab->x(),
843c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            favicon_bounds.y() + current_tab->y(),
844c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            favicon_bounds.width(),
845c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch            favicon_bounds.height());
846c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
847c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
848c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
849c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
850c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
851c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool TabStripGtk::IsCompatibleWith(TabStripGtk* other) {
852c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return model_->profile() == other->model()->profile();
853c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
854c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
855c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool TabStripGtk::IsAnimating() const {
856c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return active_animation_.get() != NULL;
857c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
858c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
859c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::DestroyDragController() {
860c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  drag_controller_.reset();
861c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
862c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
863c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::DestroyDraggedSourceTab(TabGtk* tab) {
864c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We could be running an animation that references this Tab.
865c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StopAnimation();
866c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
867c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make sure we leave the tab_data_ vector in a consistent state, otherwise
868c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // we'll be pointing to tabs that have been deleted and removed from the
869c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // child view list.
870c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<TabData>::iterator it = tab_data_.begin();
871c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (; it != tab_data_.end(); ++it) {
872c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (it->tab == tab) {
873c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!model_->closing_all())
874c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        NOTREACHED() << "Leaving in an inconsistent state!";
875c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tab_data_.erase(it);
876c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
877c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
878c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
879c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
880c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_container_remove(GTK_CONTAINER(tabstrip_.get()), tab->widget());
881c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If we delete the dragged source tab here, the DestroyDragWidget posted
882c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // task will be run after the tab is deleted, leading to a crash.
883c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  MessageLoop::current()->DeleteSoon(FROM_HERE, tab);
884c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
885c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Force a layout here, because if we've just quickly drag detached a Tab,
886c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // the stopping of the active animation above may have left the TabStrip in a
887c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // bad (visual) state.
888c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Layout();
889c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
890c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
891c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Rect TabStripGtk::GetIdealBounds(int index) {
892c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(index >= 0 && index < GetTabCount());
893c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return tab_data_.at(index).ideal_bounds;
894c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
895c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
896c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::SetVerticalOffset(int offset) {
897c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab_vertical_offset_ = offset;
898c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Layout();
899c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
900c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
901c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Point TabStripGtk::GetTabStripOriginForWidget(GtkWidget* target) {
902c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int x, y;
903c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!gtk_widget_translate_coordinates(widget(), target,
904c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      -widget()->allocation.x, 0, &x, &y)) {
905c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // If the tab strip isn't showing, give the coordinates relative to the
906c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // toplevel instead.
907c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!gtk_widget_translate_coordinates(
908c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        gtk_widget_get_toplevel(widget()), target, 0, 0, &x, &y)) {
909c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED();
910c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
911c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
912c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (GTK_WIDGET_NO_WINDOW(target)) {
913c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    x += target->allocation.x;
914c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    y += target->allocation.y;
915c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
916c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return gfx::Point(x, y);
917c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
918c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
919c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
920c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// ViewIDUtil::Delegate implementation
921c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
922c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochGtkWidget* TabStripGtk::GetWidgetForViewID(ViewID view_id) {
923c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (GetTabCount() > 0) {
924c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (view_id == VIEW_ID_TAB_LAST) {
925c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return GetTabAt(GetTabCount() - 1)->widget();
926c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else if ((view_id >= VIEW_ID_TAB_0) && (view_id < VIEW_ID_TAB_LAST)) {
927c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      int index = view_id - VIEW_ID_TAB_0;
928c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (index >= 0 && index < GetTabCount()) {
929c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return GetTabAt(index)->widget();
930c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else {
931c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return NULL;
932c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
933c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
934c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
935c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
936c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return NULL;
937c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
938c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
939c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
940c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStripGtk, TabStripModelObserver implementation:
941c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
942201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochvoid TabStripGtk::TabInsertedAt(TabContentsWrapper* contents,
943c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                int index,
944c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                bool foreground) {
945c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(contents);
946c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(index == TabStripModel::kNoTab || model_->ContainsIndex(index));
947c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
948c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StopAnimation();
949c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
950c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool contains_tab = false;
951c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabGtk* tab = NULL;
952c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // First see if this Tab is one that was dragged out of this TabStrip and is
953c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // now being dragged back in. In this case, the DraggedTabController actually
954c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // has the Tab already constructed and we can just insert it into our list
955c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // again.
956c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (IsDragSessionActive()) {
957201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    tab = drag_controller_->GetDragSourceTabForContents(
958201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        contents->tab_contents());
959c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tab) {
960c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // If the Tab was detached, it would have been animated closed but not
961c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // removed, so we need to reset this property.
962c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tab->set_closing(false);
963c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tab->ValidateLoadingAnimation(TabRendererGtk::ANIMATION_NONE);
964c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tab->SetVisible(true);
965c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
966c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
967c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // See if we're already in the list. We don't want to add ourselves twice.
968c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::vector<TabData>::const_iterator iter = tab_data_.begin();
969c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (; iter != tab_data_.end() && !contains_tab; ++iter) {
970c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (iter->tab == tab)
971c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        contains_tab = true;
972c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
973c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
974c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
975c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!tab)
976c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab = new TabGtk(this);
977c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
978c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Only insert if we're not already in the list.
979c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!contains_tab) {
980c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabData d = { tab, gfx::Rect() };
981c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_data_.insert(tab_data_.begin() + index, d);
982201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    tab->UpdateData(contents->tab_contents(), model_->IsAppTab(index), false);
983c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
984c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab->set_mini(model_->IsMiniTab(index));
985c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab->set_app(model_->IsAppTab(index));
986c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab->SetBlocked(model_->IsTabBlocked(index));
987c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
988c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (gtk_widget_get_parent(tab->widget()) != tabstrip_.get())
989c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_fixed_put(GTK_FIXED(tabstrip_.get()), tab->widget(), 0, 0);
990c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
991c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Don't animate the first tab; it looks weird.
992c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (GetTabCount() > 1) {
993c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartInsertTabAnimation(index);
994c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We added the tab at 0x0, we need to force an animation step otherwise
995c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // if GTK paints before the animation event the tab is painted at 0x0
996c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // which is most likely not where it should be positioned.
997c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    active_animation_->AnimationProgressed(NULL);
998c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
999c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Layout();
1000c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1001c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1002c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1003201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochvoid TabStripGtk::TabDetachedAt(TabContentsWrapper* contents, int index) {
1004c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GenerateIdealBounds();
1005201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  StartRemoveTabAnimation(index, contents->tab_contents());
1006c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Have to do this _after_ calling StartRemoveTabAnimation, so that any
1007c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // previous remove is completed fully and index is valid in sync with the
1008c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // model index.
1009c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetTabAt(index)->set_closing(true);
1010c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1011c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1012201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochvoid TabStripGtk::TabSelectedAt(TabContentsWrapper* old_contents,
1013201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch                                TabContentsWrapper* new_contents,
1014c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                int index,
1015c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                bool user_gesture) {
1016c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(index >= 0 && index < static_cast<int>(GetTabCount()));
1017c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1018c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We have "tiny tabs" if the tabs are so tiny that the unselected ones are
1019c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // a different size to the selected ones.
1020c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool tiny_tabs = current_unselected_width_ != current_selected_width_;
1021c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!IsAnimating() && (!needs_resize_layout_ || tiny_tabs))
1022c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Layout();
1023c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1024c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetTabAt(index)->SchedulePaint();
1025c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1026c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int old_index = model_->GetIndexOfTabContents(old_contents);
1027c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (old_index >= 0) {
1028c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GetTabAt(old_index)->SchedulePaint();
1029c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GetTabAt(old_index)->StopMiniTabTitleAnimation();
1030c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1031c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1032c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1033201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochvoid TabStripGtk::TabMoved(TabContentsWrapper* contents,
1034c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           int from_index,
1035c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           int to_index) {
1036c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect start_bounds = GetIdealBounds(from_index);
1037c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabGtk* tab = GetTabAt(from_index);
1038c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab_data_.erase(tab_data_.begin() + from_index);
1039c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabData data = {tab, gfx::Rect()};
1040c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab->set_mini(model_->IsMiniTab(to_index));
1041c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab->SetBlocked(model_->IsTabBlocked(to_index));
1042c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab_data_.insert(tab_data_.begin() + to_index, data);
1043c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GenerateIdealBounds();
1044c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StartMoveTabAnimation(from_index, to_index);
1045c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1046c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1047201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochvoid TabStripGtk::TabChangedAt(TabContentsWrapper* contents, int index,
1048c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               TabChangeType change_type) {
1049c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Index is in terms of the model. Need to make sure we adjust that index in
1050c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // case we have an animation going.
1051c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabGtk* tab = GetTabAtAdjustForAnimation(index);
1052c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (change_type == TITLE_NOT_LOADING) {
1053c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tab->mini() && !tab->IsSelected())
1054c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tab->StartMiniTabTitleAnimation();
1055c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We'll receive another notification of the change asynchronously.
1056c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
1057c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1058201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch  tab->UpdateData(contents->tab_contents(),
1059c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                  model_->IsAppTab(index),
1060c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                  change_type == LOADING_ONLY);
1061c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab->UpdateFromModel();
1062c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1063c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
106472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid TabStripGtk::TabReplacedAt(TabStripModel* tab_strip_model,
106572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                TabContentsWrapper* old_contents,
1066201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch                                TabContentsWrapper* new_contents,
1067c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                int index) {
1068c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabChangedAt(new_contents, index, ALL);
1069c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1070c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1071201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochvoid TabStripGtk::TabMiniStateChanged(TabContentsWrapper* contents, int index) {
1072513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  // Don't do anything if we've already picked up the change from TabMoved.
1073513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (GetTabAt(index)->mini() == model_->IsMiniTab(index))
1074513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    return;
1075513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
1076c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetTabAt(index)->set_mini(model_->IsMiniTab(index));
1077c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Don't animate if the window isn't visible yet. The window won't be visible
1078c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // when dragging a mini-tab to a new window.
1079c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (window_ && window_->window() &&
1080c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      GTK_WIDGET_VISIBLE(GTK_WIDGET(window_->window()))) {
1081c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartMiniTabAnimation(index);
1082c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
1083c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Layout();
1084c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1085c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1086c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1087201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdochvoid TabStripGtk::TabBlockedStateChanged(TabContentsWrapper* contents,
1088201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch                                         int index) {
1089c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetTabAt(index)->SetBlocked(model_->IsTabBlocked(index));
1090c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1091c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1092c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
1093c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStripGtk, TabGtk::TabDelegate implementation:
1094c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1095c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool TabStripGtk::IsTabSelected(const TabGtk* tab) const {
1096c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab->closing())
1097c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
1098c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1099ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return GetIndexOfTab(tab) == model_->active_index();
1100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1101c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool TabStripGtk::IsTabDetached(const TabGtk* tab) const {
1103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (drag_controller_.get())
1104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return drag_controller_->IsTabDetached(tab);
1105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return false;
1106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::GetCurrentTabWidths(double* unselected_width,
1109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                      double* selected_width) const {
1110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *unselected_width = current_unselected_width_;
1111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *selected_width = current_selected_width_;
1112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool TabStripGtk::IsTabPinned(const TabGtk* tab) const {
1115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab->closing())
1116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
1117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return model_->IsTabPinned(GetIndexOfTab(tab));
1119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::SelectTab(TabGtk* tab) {
1122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int index = GetIndexOfTab(tab);
1123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (model_->ContainsIndex(index))
1124ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    model_->ActivateTabAt(index, true);
1125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::CloseTab(TabGtk* tab) {
1128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int tab_index = GetIndexOfTab(tab);
1129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (model_->ContainsIndex(tab_index)) {
1130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabGtk* last_tab = GetTabAt(GetTabCount() - 1);
1131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Limit the width available to the TabStrip for laying out Tabs, so that
1132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Tabs are not resized until a later time (when the mouse pointer leaves
1133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // the TabStrip).
1134c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    available_width_for_tabs_ = GetAvailableWidthForTabs(last_tab);
1135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    needs_resize_layout_ = true;
1136c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We hook into the message loop in order to receive mouse move events when
1137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // the mouse is outside of the tabstrip.  We unhook once the resize layout
1138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // animation is started.
1139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    AddMessageLoopObserver();
1140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    model_->CloseTabContentsAt(tab_index,
1141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               TabStripModel::CLOSE_USER_GESTURE |
1142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                               TabStripModel::CLOSE_CREATE_HISTORICAL_TAB);
1143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool TabStripGtk::IsCommandEnabledForTab(
1147c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabStripModel::ContextMenuCommand command_id, const TabGtk* tab) const {
1148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int index = GetIndexOfTab(tab);
1149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (model_->ContainsIndex(index))
1150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return model_->IsContextMenuCommandEnabled(index, command_id);
1151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return false;
1152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::ExecuteCommandForTab(
1155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabStripModel::ContextMenuCommand command_id, TabGtk* tab) {
1156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int index = GetIndexOfTab(tab);
1157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (model_->ContainsIndex(index))
1158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    model_->ExecuteContextMenuCommand(index, command_id);
1159c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1160c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1161c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::StartHighlightTabsForCommand(
1162c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabStripModel::ContextMenuCommand command_id, TabGtk* tab) {
1163c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (command_id == TabStripModel::CommandCloseOtherTabs ||
1164c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      command_id == TabStripModel::CommandCloseTabsToRight) {
1165c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTIMPLEMENTED();
1166c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1167c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1168c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1169c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::StopHighlightTabsForCommand(
1170c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabStripModel::ContextMenuCommand command_id, TabGtk* tab) {
1171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (command_id == TabStripModel::CommandCloseTabsToRight ||
1172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      command_id == TabStripModel::CommandCloseOtherTabs) {
1173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Just tell all Tabs to stop pulsing - it's safe.
1174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StopAllHighlighting();
1175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::StopAllHighlighting() {
1179c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(jhawkins): Hook up animations.
1180c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  NOTIMPLEMENTED();
1181c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1182c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::MaybeStartDrag(TabGtk* tab, const gfx::Point& point) {
1184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Don't accidentally start any drag operations during animations if the
1185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // mouse is down.
1186c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (IsAnimating() || tab->closing() || !HasAvailableDragActions())
1187c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
1188c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  drag_controller_.reset(new DraggedTabControllerGtk(tab, this));
1190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  drag_controller_->CaptureDragInfo(point);
1191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1193c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::ContinueDrag(GdkDragContext* context) {
1194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We can get called even if |MaybeStartDrag| wasn't called in the event of
1195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // a TabStrip animation when the mouse button is down. In this case we should
1196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // _not_ continue the drag because it can lead to weird bugs.
1197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (drag_controller_.get())
1198c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drag_controller_->Drag();
1199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1200c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1201c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool TabStripGtk::EndDrag(bool canceled) {
1202c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return drag_controller_.get() ? drag_controller_->EndDrag(canceled) : false;
1203c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1204c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1205c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool TabStripGtk::HasAvailableDragActions() const {
1206c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return model_->delegate()->GetDragActions() != 0;
1207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
120972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenui::ThemeProvider* TabStripGtk::GetThemeProvider() {
1210ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return theme_service_;
1211c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1212c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch///////////////////////////////////////////////////////////////////////////////
1214c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStripGtk, MessageLoop::Observer implementation:
1215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::WillProcessEvent(GdkEvent* event) {
1217c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Nothing to do.
1218c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1220c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::DidProcessEvent(GdkEvent* event) {
1221c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  switch (event->type) {
1222c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case GDK_MOTION_NOTIFY:
1223c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case GDK_LEAVE_NOTIFY:
1224c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      HandleGlobalMouseMoveEvent();
1225c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
1226c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    default:
1227c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
1228c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1229c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1230c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
123172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen///////////////////////////////////////////////////////////////////////////////
123272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// TabStripGtk, NotificationObserver implementation:
123372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
1234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::Observe(NotificationType type,
1235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                          const NotificationSource& source,
1236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                          const NotificationDetails& details) {
1237c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (type == NotificationType::BROWSER_THEME_CHANGED) {
1238ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    TabRendererGtk::SetSelectedTitleColor(theme_service_->GetColor(
1239ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        ThemeService::COLOR_TAB_TEXT));
1240ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    TabRendererGtk::SetUnselectedTitleColor(theme_service_->GetColor(
1241ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        ThemeService::COLOR_BACKGROUND_TAB_TEXT));
1242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch////////////////////////////////////////////////////////////////////////////////
1246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStripGtk, private:
1247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStripGtk::GetTabCount() const {
1249c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return static_cast<int>(tab_data_.size());
1250c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1251c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1252c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStripGtk::GetMiniTabCount() const {
1253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int mini_count = 0;
1254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < tab_data_.size(); ++i) {
1255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tab_data_[i].tab->mini())
1256c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      mini_count++;
1257c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    else
1258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return mini_count;
1259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1260c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return mini_count;
1261c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1262c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1263c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStripGtk::GetAvailableWidthForTabs(TabGtk* last_tab) const {
1264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!base::i18n::IsRTL())
1265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return last_tab->x() - bounds_.x() + last_tab->width();
1266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  else
1267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return bounds_.width() - last_tab->x();
1268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStripGtk::GetIndexOfTab(const TabGtk* tab) const {
1271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0, index = 0; i < GetTabCount(); ++i, ++index) {
1272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabGtk* current_tab = GetTabAt(i);
1273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (current_tab->closing()) {
1274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      --index;
1275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else if (current_tab == tab) {
1276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return index;
1277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return -1;
1280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1282c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabGtk* TabStripGtk::GetTabAt(int index) const {
1283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK_GE(index, 0);
1284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK_LT(index, GetTabCount());
1285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return tab_data_.at(index).tab;
1286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1288c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabGtk* TabStripGtk::GetTabAtAdjustForAnimation(int index) const {
1289c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (active_animation_.get() &&
1290c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      active_animation_->type() == TabAnimation::REMOVE &&
1291c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      index >=
1292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      static_cast<RemoveTabAnimation*>(active_animation_.get())->index()) {
1293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    index++;
1294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return GetTabAt(index);
1296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::RemoveTabAt(int index) {
1299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabGtk* removed = tab_data_.at(index).tab;
1300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Remove the Tab from the TabStrip's list.
1302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab_data_.erase(tab_data_.begin() + index);
1303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!IsDragSessionActive() || !drag_controller_->IsDragSourceTab(removed)) {
1305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_container_remove(GTK_CONTAINER(tabstrip_.get()), removed->widget());
1306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    delete removed;
1307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::HandleGlobalMouseMoveEvent() {
1311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!IsCursorInTabStripZone()) {
1312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Mouse moved outside the tab slop zone, start a timer to do a resize
1313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // layout after a short while...
1314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (resize_layout_factory_.empty()) {
1315c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      MessageLoop::current()->PostDelayedTask(FROM_HERE,
1316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          resize_layout_factory_.NewRunnableMethod(
1317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              &TabStripGtk::ResizeLayoutTabs),
1318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          kResizeTabsTimeMs);
1319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1320c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
1321c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Mouse moved quickly out of the tab strip and then into it again, so
1322c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // cancel the timer so that the strip doesn't move when the mouse moves
1323c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // back over it.
1324c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    resize_layout_factory_.RevokeAll();
1325c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1326c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1327c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1328c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::GenerateIdealBounds() {
1329c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int tab_count = GetTabCount();
1330c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double unselected, selected;
1331c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetDesiredTabWidths(tab_count, GetMiniTabCount(), &unselected, &selected);
1332c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1333c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  current_unselected_width_ = unselected;
1334c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  current_selected_width_ = selected;
1335c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // NOTE: This currently assumes a tab's height doesn't differ based on
1337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // selected state or the number of tabs in the strip!
1338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int tab_height = TabGtk::GetStandardSize().height();
1339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double tab_x = tab_start_x();
1340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0; i < tab_count; ++i) {
1341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabGtk* tab = GetTabAt(i);
1342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    double tab_width = unselected;
1343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tab->mini())
1344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tab_width = TabGtk::GetMiniWidth();
1345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    else if (tab->IsSelected())
1346c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tab_width = selected;
1347c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    double end_of_tab = tab_x + tab_width;
1348c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int rounded_tab_x = Round(tab_x);
1349c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect state(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
1350c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    tab_height);
1351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_data_.at(i).ideal_bounds = state;
1352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_x = end_of_tab + GetTabHOffset(i + 1);
1353c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::LayoutNewTabButton(double last_tab_right,
1357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                     double unselected_width) {
1358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect bounds(0, kNewTabButtonVOffset,
1359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   newtab_button_->width(), newtab_button_->height());
1360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int delta = abs(Round(unselected_width) - TabGtk::GetStandardSize().width());
1361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (delta > 1 && !needs_resize_layout_) {
1362c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We're shrinking tabs, so we need to anchor the New Tab button to the
1363c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // right edge of the TabStrip's bounds, rather than the right edge of the
1364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // right-most Tab, otherwise it'll bounce when animating.
1365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bounds.set_x(bounds_.width() - newtab_button_->width());
1366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
1367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bounds.set_x(Round(last_tab_right - kTabHOffset) + kNewTabButtonHOffset);
1368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bounds.set_x(gtk_util::MirroredLeftPointForRect(tabstrip_.get(), bounds));
1370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_fixed_move(GTK_FIXED(tabstrip_.get()), newtab_button_->widget(),
1372c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                 bounds.x(), bounds.y());
1373c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1374c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1375c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::GetDesiredTabWidths(int tab_count,
1376c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                      int mini_tab_count,
1377c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                      double* unselected_width,
1378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                      double* selected_width) const {
1379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(tab_count >= 0 && mini_tab_count >= 0 && mini_tab_count <= tab_count);
1380c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const double min_unselected_width =
1381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      TabGtk::GetMinimumUnselectedSize().width();
1382c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const double min_selected_width =
1383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      TabGtk::GetMinimumSelectedSize().width();
1384c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *unselected_width = min_unselected_width;
1386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *selected_width = min_selected_width;
1387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1388c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_count == 0) {
1389c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Return immediately to avoid divide-by-zero below.
1390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
1391c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Determine how much space we can actually allocate to tabs.
1394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int available_width = tabstrip_->allocation.width;
1395c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (available_width_for_tabs_ < 0) {
1396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    available_width = bounds_.width();
1397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    available_width -=
1398c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        (kNewTabButtonHOffset + newtab_button_->width());
1399c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
1400c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Interesting corner case: if |available_width_for_tabs_| > the result
1401c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // of the calculation in the conditional arm above, the strip is in
1402c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // overflow.  We can either use the specified width or the true available
1403c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // width here; the first preserves the consistent "leave the last tab under
1404c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // the user's mouse so they can close many tabs" behavior at the cost of
1405c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // prolonging the glitchy appearance of the overflow state, while the second
1406c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // gets us out of overflow as soon as possible but forces the user to move
1407c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // their mouse for a few tabs' worth of closing.  We choose visual
1408c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // imperfection over behavioral imperfection and select the first option.
1409c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    available_width = available_width_for_tabs_;
1410c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1411c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1412c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (mini_tab_count > 0) {
1413c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    available_width -= mini_tab_count * (TabGtk::GetMiniWidth() + kTabHOffset);
1414c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_count -= mini_tab_count;
1415c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (tab_count == 0) {
1416c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      *selected_width = *unselected_width = TabGtk::GetStandardSize().width();
1417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
1418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Account for gap between the last mini-tab and first normal tab.
1420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    available_width -= mini_to_non_mini_gap_;
1421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1422c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1423c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Calculate the desired tab widths by dividing the available space into equal
1424c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // portions.  Don't let tabs get larger than the "standard width" or smaller
1425c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // than the minimum width for each type, respectively.
1426c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const int total_offset = kTabHOffset * (tab_count - 1);
1427c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const double desired_tab_width = std::min(
1428c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      (static_cast<double>(available_width - total_offset) /
1429c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       static_cast<double>(tab_count)),
1430c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      static_cast<double>(TabGtk::GetStandardSize().width()));
1431c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1432c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *unselected_width = std::max(desired_tab_width, min_unselected_width);
1433c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *selected_width = std::max(desired_tab_width, min_selected_width);
1434c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1435c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // When there are multiple tabs, we'll have one selected and some unselected
1436c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // tabs.  If the desired width was between the minimum sizes of these types,
1437c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // try to shrink the tabs with the smaller minimum.  For example, if we have
1438c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // a strip of width 10 with 4 tabs, the desired width per tab will be 2.5.  If
1439c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // selected tabs have a minimum width of 4 and unselected tabs have a minimum
1440c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // width of 1, the above code would set *unselected_width = 2.5,
1441c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // *selected_width = 4, which results in a total width of 11.5.  Instead, we
1442c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // want to set *unselected_width = 2, *selected_width = 4, for a total width
1443c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // of 10.
1444c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_count > 1) {
1445c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if ((min_unselected_width < min_selected_width) &&
1446c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        (desired_tab_width < min_selected_width)) {
1447c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      double calc_width =
1448c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          static_cast<double>(
1449c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              available_width - total_offset - min_selected_width) /
1450c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          static_cast<double>(tab_count - 1);
1451c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      *unselected_width = std::max(calc_width, min_unselected_width);
1452c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else if ((min_unselected_width > min_selected_width) &&
1453c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch               (desired_tab_width < min_unselected_width)) {
1454c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      *selected_width = std::max(available_width - total_offset -
1455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          (min_unselected_width * (tab_count - 1)), min_selected_width);
1456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1457c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1458c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1459c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1460c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStripGtk::GetTabHOffset(int tab_index) {
1461c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (tab_index < GetTabCount() && GetTabAt(tab_index - 1)->mini() &&
1462c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      !GetTabAt(tab_index)->mini()) {
1463c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return mini_to_non_mini_gap_ + kTabHOffset;
1464c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1465c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return kTabHOffset;
1466c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1467c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1468c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochint TabStripGtk::tab_start_x() const {
1469c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return 0;
1470c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1471c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
14723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickbool TabStripGtk::ResizeLayoutTabs() {
1473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  resize_layout_factory_.RevokeAll();
1474c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // It is critically important that this is unhooked here, otherwise we will
1476c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // keep spying on messages forever.
1477c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  RemoveMessageLoopObserver();
1478c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1479c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  available_width_for_tabs_ = -1;
1480c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int mini_tab_count = GetMiniTabCount();
1481c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (mini_tab_count == GetTabCount()) {
1482c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Only mini tabs, we know the tab widths won't have changed (all mini-tabs
1483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // have the same width), so there is nothing to do.
14843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return false;
1485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1486c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabGtk* first_tab = GetTabAt(mini_tab_count);
1487c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double unselected, selected;
1488c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GetDesiredTabWidths(GetTabCount(), mini_tab_count, &unselected, &selected);
1489c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int w = Round(first_tab->IsSelected() ? selected : unselected);
1490c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1491c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We only want to run the animation if we're not already at the desired
1492c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // size.
14933345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (abs(first_tab->width() - w) > 1) {
1494c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartResizeLayoutAnimation();
14953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return true;
14963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
14973345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
14983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  return false;
1499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1500c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1501c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool TabStripGtk::IsCursorInTabStripZone() const {
1502c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point tabstrip_topleft;
1503c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_util::ConvertWidgetPointToScreen(tabstrip_.get(), &tabstrip_topleft);
1504c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect bds = bounds();
1506c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bds.set_origin(tabstrip_topleft);
1507c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bds.set_height(bds.height() + kTabStripAnimationVSlop);
1508c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1509c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GdkScreen* screen = gdk_screen_get_default();
1510c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GdkDisplay* display = gdk_screen_get_display(screen);
1511c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gint x, y;
1512c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gdk_display_get_pointer(display, NULL, &x, &y, NULL);
1513c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point cursor_point(x, y);
1514c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1515c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return bds.Contains(cursor_point);
1516c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1517c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1518c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::AddMessageLoopObserver() {
1519c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!added_as_message_loop_observer_) {
1520c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    MessageLoopForUI::current()->AddObserver(this);
1521c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    added_as_message_loop_observer_ = true;
1522c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1523c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1524c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1525c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::RemoveMessageLoopObserver() {
1526c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (added_as_message_loop_observer_) {
1527c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    MessageLoopForUI::current()->RemoveObserver(this);
1528c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    added_as_message_loop_observer_ = false;
1529c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1530c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1531c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1532c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgfx::Rect TabStripGtk::GetDropBounds(int drop_index,
1533c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                     bool drop_before,
1534c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                     bool* is_beneath) {
1535c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK_NE(drop_index, -1);
1536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int center_x;
1537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (drop_index < GetTabCount()) {
1538c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabGtk* tab = GetTabAt(drop_index);
1539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect bounds = tab->GetNonMirroredBounds(tabstrip_.get());
1540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // TODO(sky): update these for pinned tabs.
1541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (drop_before)
1542c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      center_x = bounds.x() - (kTabHOffset / 2);
1543c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    else
1544c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      center_x = bounds.x() + (bounds.width() / 2);
1545c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
1546c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabGtk* last_tab = GetTabAt(drop_index - 1);
1547c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect bounds = last_tab->GetNonMirroredBounds(tabstrip_.get());
1548c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    center_x = bounds.x() + bounds.width() + (kTabHOffset / 2);
1549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1550c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1551c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  center_x = gtk_util::MirroredXCoordinate(tabstrip_.get(), center_x);
1552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Determine the screen bounds.
1554c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Point drop_loc(center_x - drop_indicator_width / 2,
1555c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                      -drop_indicator_height);
1556c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_util::ConvertWidgetPointToScreen(tabstrip_.get(), &drop_loc);
1557c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect drop_bounds(drop_loc.x(), drop_loc.y(), drop_indicator_width,
1558c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                        drop_indicator_height);
1559c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1560c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(jhawkins): We always display the arrow underneath the tab because we
1561c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // don't have custom frame support yet.
1562c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *is_beneath = true;
1563c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (*is_beneath)
1564c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drop_bounds.Offset(0, drop_bounds.height() + bounds().height());
1565c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1566c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return drop_bounds;
1567c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1568c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1569c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::UpdateDropIndex(GdkDragContext* context, gint x, gint y) {
1570c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If the UI layout is right-to-left, we need to mirror the mouse
1571c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // coordinates since we calculate the drop index based on the
1572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // original (and therefore non-mirrored) positions of the tabs.
1573c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  x = gtk_util::MirroredXCoordinate(tabstrip_.get(), x);
1574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We don't allow replacing the urls of mini-tabs.
1575c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = GetMiniTabCount(); i < GetTabCount(); ++i) {
1576c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabGtk* tab = GetTabAt(i);
1577c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect bounds = tab->GetNonMirroredBounds(tabstrip_.get());
1578c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const int tab_max_x = bounds.x() + bounds.width();
1579c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const int hot_width = bounds.width() / 3;
1580c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (x < tab_max_x) {
1581c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (x < bounds.x() + hot_width)
1582c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        SetDropIndex(i, true);
1583c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      else if (x >= tab_max_x - hot_width)
1584c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        SetDropIndex(i + 1, true);
1585c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      else
1586c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        SetDropIndex(i, false);
1587c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
1588c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1589c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1590c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1591c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The drop isn't over a tab, add it to the end.
1592c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetDropIndex(GetTabCount(), true);
1593c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1594c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1595c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::SetDropIndex(int index, bool drop_before) {
1596c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool is_beneath;
1597c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect drop_bounds = GetDropBounds(index, drop_before, &is_beneath);
1598c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1599c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!drop_info_.get()) {
1600c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drop_info_.reset(new DropInfo(index, drop_before, !is_beneath));
1601c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
1602c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!GTK_IS_WIDGET(drop_info_->container)) {
1603c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      drop_info_->CreateContainer();
1604c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else if (drop_info_->drop_index == index &&
1605c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch               drop_info_->drop_before == drop_before) {
1606c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
1607c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1608c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1609c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drop_info_->drop_index = index;
1610c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    drop_info_->drop_before = drop_before;
1611c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (is_beneath == drop_info_->point_down) {
1612c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      drop_info_->point_down = !is_beneath;
1613c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      drop_info_->drop_arrow= GetDropArrowImage(drop_info_->point_down);
1614c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1615c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1616c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1617c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_window_move(GTK_WINDOW(drop_info_->container),
1618c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                  drop_bounds.x(), drop_bounds.y());
1619c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_window_resize(GTK_WINDOW(drop_info_->container),
1620c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    drop_bounds.width(), drop_bounds.height());
1621c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1622c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
162321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsenbool TabStripGtk::CompleteDrop(guchar* data, bool is_plain_text) {
1624c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!drop_info_.get())
1625c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
1626c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1627c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const int drop_index = drop_info_->drop_index;
1628c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const bool drop_before = drop_info_->drop_before;
1629c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1630c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Destroy the drop indicator.
1631c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  drop_info_.reset();
1632c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
163321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  GURL url;
163421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (is_plain_text) {
163521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    AutocompleteMatch match;
163621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    model_->profile()->GetAutocompleteClassifier()->Classify(
163772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen        UTF8ToUTF16(reinterpret_cast<char*>(data)), string16(), false,
163821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        &match, NULL);
163921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    url = match.destination_url;
164021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  } else {
164121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    std::string url_string(reinterpret_cast<char*>(data));
164221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    url = GURL(url_string.substr(0, url_string.find_first_of('\n')));
164321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  }
1644c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!url.is_valid())
1645c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
1646c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
16474a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  browser::NavigateParams params(window()->browser(), url,
16484a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                 PageTransition::LINK);
16494a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  params.tabstrip_index = drop_index;
16504a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1651c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (drop_before) {
16524a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    params.disposition = NEW_FOREGROUND_TAB;
1653c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
16544a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    params.disposition = CURRENT_TAB;
16554a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    params.source_contents = model_->GetTabContentsAt(drop_index);
1656c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1657c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
16584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  browser::Navigate(&params);
16594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1660c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
1661c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1662c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1663c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// static
1664c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochGdkPixbuf* TabStripGtk::GetDropArrowImage(bool is_down) {
1665c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return ResourceBundle::GetSharedInstance().GetPixbufNamed(
1666c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      is_down ? IDR_TAB_DROP_DOWN : IDR_TAB_DROP_UP);
1667c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1668c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1669c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// TabStripGtk::DropInfo -------------------------------------------------------
1670c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1671c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabStripGtk::DropInfo::DropInfo(int drop_index, bool drop_before,
1672c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                bool point_down)
1673c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : drop_index(drop_index),
1674c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      drop_before(drop_before),
1675c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      point_down(point_down) {
1676c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CreateContainer();
1677c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  drop_arrow = GetDropArrowImage(point_down);
1678c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1679c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1680c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTabStripGtk::DropInfo::~DropInfo() {
1681c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DestroyContainer();
1682c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1683c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1684c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgboolean TabStripGtk::DropInfo::OnExposeEvent(GtkWidget* widget,
1685c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                              GdkEventExpose* event) {
1686c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (gtk_util::IsScreenComposited()) {
1687c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SetContainerTransparency();
1688c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
1689c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SetContainerShapeMask();
1690c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1691c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1692c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gdk_pixbuf_render_to_drawable(drop_arrow,
1693c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                container->window,
1694c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                0, 0, 0,
1695c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                0, 0,
1696c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                drop_indicator_width,
1697c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                drop_indicator_height,
1698c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                GDK_RGB_DITHER_NONE, 0, 0);
1699c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1700c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return FALSE;
1701c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1702c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1703c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Sets the color map of the container window to allow the window to be
1704c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// transparent.
1705c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::DropInfo::SetContainerColorMap() {
1706c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GdkScreen* screen = gtk_widget_get_screen(container);
1707c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GdkColormap* colormap = gdk_screen_get_rgba_colormap(screen);
1708c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1709c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If rgba is not available, use rgb instead.
1710c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!colormap)
1711c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    colormap = gdk_screen_get_rgb_colormap(screen);
1712c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1713c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_set_colormap(container, colormap);
1714c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1715c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1716c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Sets full transparency for the container window.  This is used if
1717c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// compositing is available for the screen.
1718c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::DropInfo::SetContainerTransparency() {
1719c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_t* cairo_context = gdk_cairo_create(container->window);
1720c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!cairo_context)
1721c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
1722c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1723c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make the background of the dragged tab window fully transparent.  All of
1724c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // the content of the window (child widgets) will be completely opaque.
1725c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1726c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_scale(cairo_context, static_cast<double>(drop_indicator_width),
1727c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch              static_cast<double>(drop_indicator_height));
1728c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_set_source_rgba(cairo_context, 1.0f, 1.0f, 1.0f, 0.0f);
1729c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE);
1730c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_paint(cairo_context);
1731c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_destroy(cairo_context);
1732c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1733c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1734c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Sets the shape mask for the container window to emulate a transparent
1735c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// container window.  This is used if compositing is not available for the
1736c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// screen.
1737c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::DropInfo::SetContainerShapeMask() {
1738c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Create a 1bpp bitmap the size of |container|.
1739c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GdkPixmap* pixmap = gdk_pixmap_new(NULL,
1740c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                     drop_indicator_width,
1741c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                     drop_indicator_height, 1);
1742c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_t* cairo_context = gdk_cairo_create(GDK_DRAWABLE(pixmap));
1743c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1744c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Set the transparency.
1745c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_set_source_rgba(cairo_context, 1, 1, 1, 0);
1746c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1747c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Blit the rendered bitmap into a pixmap.  Any pixel set in the pixmap will
1748c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // be opaque in the container window.
1749c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_set_operator(cairo_context, CAIRO_OPERATOR_SOURCE);
1750c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gdk_cairo_set_source_pixbuf(cairo_context, drop_arrow, 0, 0);
1751c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_paint(cairo_context);
1752c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  cairo_destroy(cairo_context);
1753c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1754c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Set the shape mask.
1755c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gdk_window_shape_combine_mask(container->window, pixmap, 0, 0);
1756c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_object_unref(pixmap);
1757c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1758c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1759c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::DropInfo::CreateContainer() {
1760c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  container = gtk_window_new(GTK_WINDOW_POPUP);
1761c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetContainerColorMap();
1762c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_set_app_paintable(container, TRUE);
1763c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_signal_connect(container, "expose-event",
1764c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnExposeEventThunk), this);
1765c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_add_events(container, GDK_STRUCTURE_MASK);
1766c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_window_move(GTK_WINDOW(container), 0, 0);
1767c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_window_resize(GTK_WINDOW(container),
1768c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    drop_indicator_width, drop_indicator_height);
1769c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_widget_show_all(container);
1770c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1771c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1772c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::DropInfo::DestroyContainer() {
1773c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (GTK_IS_WIDGET(container))
1774c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_widget_destroy(container);
1775c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1776c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1777c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::StopAnimation() {
1778c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (active_animation_.get())
1779c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    active_animation_->Stop();
1780c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1781c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1782c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Called from:
1783c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// - animation tick
1784c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::AnimationLayout(double unselected_width) {
1785c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int tab_height = TabGtk::GetStandardSize().height();
1786c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double tab_x = tab_start_x();
1787c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0; i < GetTabCount(); ++i) {
1788c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabAnimation* animation = active_animation_.get();
1789c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (animation)
1790c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      tab_x += animation->GetGapWidth(i);
1791c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    double tab_width = TabAnimation::GetCurrentTabWidth(this, animation, i);
1792c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    double end_of_tab = tab_x + tab_width;
1793c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int rounded_tab_x = Round(tab_x);
1794c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabGtk* tab = GetTabAt(i);
1795c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gfx::Rect bounds(rounded_tab_x, 0, Round(end_of_tab) - rounded_tab_x,
1796c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                     tab_height);
1797c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    SetTabBounds(tab, bounds);
1798c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    tab_x = end_of_tab + GetTabHOffset(i + 1);
1799c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1800c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  LayoutNewTabButton(tab_x, unselected_width);
1801c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1802c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1803c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::StartInsertTabAnimation(int index) {
1804c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // The TabStrip can now use its entire width to lay out Tabs.
1805c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  available_width_for_tabs_ = -1;
1806c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StopAnimation();
1807c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_animation_.reset(new InsertTabAnimation(this, index));
1808c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_animation_->Start();
1809c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1810c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1811c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::StartRemoveTabAnimation(int index, TabContents* contents) {
1812c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (active_animation_.get()) {
1813c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Some animations (e.g. MoveTabAnimation) cause there to be a Layout when
1814c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // they're completed (which includes canceled). Since |tab_data_| is now
1815c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // inconsistent with TabStripModel, doing this Layout will crash now, so
1816c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // we ask the MoveTabAnimation to skip its Layout (the state will be
1817c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // corrected by the RemoveTabAnimation we're about to initiate).
1818c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    active_animation_->set_layout_on_completion(false);
1819c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    active_animation_->Stop();
1820c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1821c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1822c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_animation_.reset(new RemoveTabAnimation(this, index, contents));
1823c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_animation_->Start();
1824c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1825c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1826c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::StartMoveTabAnimation(int from_index, int to_index) {
1827c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StopAnimation();
1828c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_animation_.reset(new MoveTabAnimation(this, from_index, to_index));
1829c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_animation_->Start();
1830c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1831c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1832c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::StartResizeLayoutAnimation() {
1833c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StopAnimation();
1834c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_animation_.reset(new ResizeLayoutAnimation(this));
1835c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_animation_->Start();
1836c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1837c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1838c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::StartMiniTabAnimation(int index) {
1839c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StopAnimation();
1840c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_animation_.reset(new MiniTabAnimation(this, index));
1841c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_animation_->Start();
1842c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1843c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1844c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::StartMiniMoveTabAnimation(int from_index,
1845c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                            int to_index,
1846c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                            const gfx::Rect& start_bounds) {
1847c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  StopAnimation();
1848c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_animation_.reset(
1849c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      new MiniMoveAnimation(this, from_index, to_index, start_bounds));
1850c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_animation_->Start();
1851c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1852c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1853c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::FinishAnimation(TabStripGtk::TabAnimation* animation,
1854c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  bool layout) {
1855c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  active_animation_.reset(NULL);
1856c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1857c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Reset the animation state of each tab.
1858c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = 0, count = GetTabCount(); i < count; ++i)
1859c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    GetTabAt(i)->set_animating_mini_change(false);
1860c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1861c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (layout)
1862c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Layout();
1863c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1864c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1865c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgboolean TabStripGtk::OnExpose(GtkWidget* widget, GdkEventExpose* event) {
1866c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (gdk_region_empty(event->region))
1867c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return TRUE;
1868c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1869c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If we're only repainting favicons, optimize the paint path and only draw
1870c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // the favicons.
1871c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GdkRectangle* rects;
1872c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gint num_rects;
1873c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gdk_region_get_rectangles(event->region, &rects, &num_rects);
1874c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  qsort(rects, num_rects, sizeof(GdkRectangle), CompareGdkRectangles);
1875c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<int> tabs_to_repaint;
1876c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!IsDragSessionActive() &&
1877ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      CanPaintOnlyFavicons(rects, num_rects, &tabs_to_repaint)) {
1878ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    PaintOnlyFavicons(event, tabs_to_repaint);
1879c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    g_free(rects);
1880c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return TRUE;
1881c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1882c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_free(rects);
1883c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1884c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // TODO(jhawkins): Ideally we'd like to only draw what's needed in the damage
1885c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // rect, but the tab widgets overlap each other, and painting on one widget
1886c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // will cause an expose-event to be sent to the widgets underneath.  The
1887c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // underlying widget does not need to be redrawn as we control the order of
1888c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // expose-events.  Currently we hack it to redraw the entire tabstrip.  We
1889c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // could change the damage rect to just contain the tabs + the new tab button.
1890c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  event->area.x = 0;
1891c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  event->area.y = 0;
1892c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  event->area.width = bounds_.width();
1893c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  event->area.height = bounds_.height();
1894c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gdk_region_union_with_rect(event->region, &event->area);
1895c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1896c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Paint the New Tab button.
1897c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_container_propagate_expose(GTK_CONTAINER(tabstrip_.get()),
1898c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      newtab_button_->widget(), event);
1899c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1900c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Paint the tabs in reverse order, so they stack to the left.
1901c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TabGtk* selected_tab = NULL;
1902c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int tab_count = GetTabCount();
1903c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int i = tab_count - 1; i >= 0; --i) {
1904c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    TabGtk* tab = GetTabAt(i);
1905c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // We must ask the _Tab's_ model, not ourselves, because in some situations
1906c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // the model will be different to this object, e.g. when a Tab is being
1907c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // removed after its TabContents has been destroyed.
1908c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!tab->IsSelected()) {
1909c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      gtk_container_propagate_expose(GTK_CONTAINER(tabstrip_.get()),
1910c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                     tab->widget(), event);
1911c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
1912c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      selected_tab = tab;
1913c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1914c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1915c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1916c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Paint the selected tab last, so it overlaps all the others.
1917c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (selected_tab) {
1918c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_container_propagate_expose(GTK_CONTAINER(tabstrip_.get()),
1919c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   selected_tab->widget(), event);
1920c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1921c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1922c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return TRUE;
1923c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1924c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1925c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::OnSizeAllocate(GtkWidget* widget, GtkAllocation* allocation) {
1926c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect bounds = gfx::Rect(allocation->x, allocation->y,
1927c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      allocation->width, allocation->height);
1928c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1929c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Nothing to do if the bounds are the same.  If we don't catch this, we'll
1930c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // get an infinite loop of size-allocate signals.
1931c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (bounds_ == bounds)
1932c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
1933c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1934c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SetBounds(bounds);
1935c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1936c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // No tabs, nothing to layout.  This happens when a browser window is created
1937c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // and shown before tabs are added (as in a popup window).
1938c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (GetTabCount() == 0)
1939c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
1940c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
19413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // When there is only one tab, Layout() so we don't animate it. With more
19423345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // tabs, do ResizeLayoutTabs(). In RTL(), we will also need to manually
19433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Layout() when ResizeLayoutTabs() is a no-op.
19443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if ((GetTabCount() == 1) || (!ResizeLayoutTabs() && base::i18n::IsRTL()))
1945c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Layout();
1946c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1947c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1948c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgboolean TabStripGtk::OnDragMotion(GtkWidget* widget, GdkDragContext* context,
1949c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   gint x, gint y, guint time) {
1950c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UpdateDropIndex(context, x, y);
1951c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return TRUE;
1952c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1953c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1954c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgboolean TabStripGtk::OnDragDrop(GtkWidget* widget, GdkDragContext* context,
1955c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                 gint x, gint y, guint time) {
1956c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!drop_info_.get())
1957c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return FALSE;
1958c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1959c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GdkAtom target = gtk_drag_dest_find_target(widget, context, NULL);
1960c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (target != GDK_NONE)
1961c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_drag_finish(context, FALSE, FALSE, time);
1962c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  else
1963c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    gtk_drag_get_data(widget, context, target, time);
1964c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1965c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return TRUE;
1966c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1967c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1968c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgboolean TabStripGtk::OnDragLeave(GtkWidget* widget, GdkDragContext* context,
1969c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                  guint time) {
1970c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Destroy the drop indicator.
1971c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  drop_info_->DestroyContainer();
1972c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return FALSE;
1973c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1974c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1975c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochgboolean TabStripGtk::OnDragDataReceived(GtkWidget* widget,
1976c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                         GdkDragContext* context,
1977c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                         gint x, gint y,
1978c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                         GtkSelectionData* data,
1979c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                         guint info, guint time) {
1980c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bool success = false;
1981c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
198272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (info == ui::TEXT_URI_LIST ||
198372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      info == ui::NETSCAPE_URL ||
198472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      info == ui::TEXT_PLAIN) {
198572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    success = CompleteDrop(data->data, info == ui::TEXT_PLAIN);
1986c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1987c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1988c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_drag_finish(context, success, success, time);
1989c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return TRUE;
1990c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1991c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1992c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::OnNewTabClicked(GtkWidget* widget) {
1993c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GdkEvent* event = gtk_get_current_event();
1994c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK_EQ(event->type, GDK_BUTTON_RELEASE);
1995c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int mouse_button = event->button.button;
1996c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gdk_event_free(event);
1997c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1998c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  switch (mouse_button) {
1999c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case 1:
2000c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      model_->delegate()->AddBlankTab(true);
2001c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
2002c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    case 2: {
2003c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // On middle-click, try to parse the PRIMARY selection as a URL and load
2004c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // it instead of creating a blank page.
2005c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      GURL url;
2006c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!gtk_util::URLFromPrimarySelection(model_->profile(), &url))
2007c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        return;
2008c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
20094a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      Browser* browser = window_->browser();
20104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      DCHECK(browser);
20114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      browser->AddSelectedTabWithURL(url, PageTransition::TYPED);
2012c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
2013c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
2014c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    default:
2015c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      NOTREACHED() << "Got click on new tab button with unhandled mouse "
2016c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   << "button " << mouse_button;
2017c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
2018c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
2019c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
2020c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid TabStripGtk::SetTabBounds(TabGtk* tab, const gfx::Rect& bounds) {
2021c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gfx::Rect bds = bounds;
2022c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  bds.set_x(gtk_util::MirroredLeftPointForRect(tabstrip_.get(), bounds));
2023c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  tab->SetBounds(bds);
2024c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_fixed_move(GTK_FIXED(tabstrip_.get()), tab->widget(),
2025c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                 bds.x(), bds.y());
2026c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
2027c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
2028ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool TabStripGtk::CanPaintOnlyFavicons(const GdkRectangle* rects,
2029c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    int num_rects, std::vector<int>* tabs_to_paint) {
2030c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // |rects| are sorted so we just need to scan from left to right and compare
2031c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // it to the tab favicon positions from left to right.
2032c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int t = 0;
2033c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (int r = 0; r < num_rects; ++r) {
2034c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    while (t < GetTabCount()) {
2035c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      TabGtk* tab = GetTabAt(t);
2036ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (GdkRectMatchesTabFaviconBounds(rects[r], tab) &&
2037513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch          tab->ShouldShowIcon()) {
2038c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        tabs_to_paint->push_back(t);
2039c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        ++t;
2040c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        break;
2041c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
2042c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      ++t;
2043c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
2044c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
2045c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return static_cast<int>(tabs_to_paint->size()) == num_rects;
2046c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
2047c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
2048ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid TabStripGtk::PaintOnlyFavicons(GdkEventExpose* event,
2049c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                    const std::vector<int>& tabs_to_paint) {
2050c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < tabs_to_paint.size(); ++i)
2051ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    GetTabAt(tabs_to_paint[i])->PaintFaviconArea(event);
2052c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
2053c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
2054c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochCustomDrawButton* TabStripGtk::MakeNewTabButton() {
2055c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CustomDrawButton* button = new CustomDrawButton(IDR_NEWTAB_BUTTON,
2056c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      IDR_NEWTAB_BUTTON_P, IDR_NEWTAB_BUTTON_H, 0);
2057c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
2058c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Let the middle mouse button initiate clicks as well.
2059c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_util::SetButtonTriggersNavigation(button->widget());
2060c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  g_signal_connect(button->widget(), "clicked",
2061c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   G_CALLBACK(OnNewTabClickedThunk), this);
2062c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  GTK_WIDGET_UNSET_FLAGS(button->widget(), GTK_CAN_FOCUS);
2063c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  gtk_fixed_put(GTK_FIXED(tabstrip_.get()), button->widget(), 0, 0);
2064c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
2065c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return button;
2066c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
2067