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