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#ifndef CHROME_BROWSER_UI_GTK_TABS_TAB_STRIP_GTK_H_
6#define CHROME_BROWSER_UI_GTK_TABS_TAB_STRIP_GTK_H_
7#pragma once
8
9#include <gtk/gtk.h>
10#include <vector>
11
12#include "base/basictypes.h"
13#include "base/message_loop.h"
14#include "base/task.h"
15#include "chrome/browser/tabs/tab_strip_model.h"
16#include "chrome/browser/ui/gtk/owned_widget_gtk.h"
17#include "chrome/browser/ui/gtk/tabs/tab_gtk.h"
18#include "chrome/browser/ui/gtk/tabstrip_origin_provider.h"
19#include "chrome/browser/ui/gtk/view_id_util.h"
20#include "content/common/notification_observer.h"
21#include "ui/base/gtk/gtk_signal.h"
22#include "ui/gfx/rect.h"
23
24class BrowserWindowGtk;
25class CustomDrawButton;
26class DraggedTabControllerGtk;
27class GtkThemeService;
28
29class TabStripGtk : public TabStripModelObserver,
30                    public TabGtk::TabDelegate,
31                    public MessageLoopForUI::Observer,
32                    public NotificationObserver,
33                    public TabstripOriginProvider,
34                    public ViewIDUtil::Delegate {
35 public:
36  class TabAnimation;
37
38  TabStripGtk(TabStripModel* model, BrowserWindowGtk* window);
39  virtual ~TabStripGtk();
40
41  // Initialize and load the TabStrip into a container.
42  // TODO(tc): Pass in theme provider so we can properly theme the tabs.
43  void Init();
44
45  void Show();
46  void Hide();
47
48  TabStripModel* model() const { return model_; }
49
50  BrowserWindowGtk* window() const { return window_; }
51
52  GtkWidget* widget() const { return tabstrip_.get(); }
53
54  // Returns true if there is an active drag session.
55  bool IsDragSessionActive() const { return drag_controller_.get() != NULL; }
56
57  // Returns true if a tab is being dragged into this tabstrip.
58  bool IsActiveDropTarget() const;
59
60  // Sets the bounds of the tabs.
61  void Layout();
62
63  // Queues a draw for the tabstrip widget.
64  void SchedulePaint();
65
66  // Sets the bounds of the tabstrip.
67  void SetBounds(const gfx::Rect& bounds);
68
69  // Returns the bounds of the tabstrip.
70  const gfx::Rect& bounds() const { return bounds_; }
71
72  // Updates loading animations for the TabStrip.
73  void UpdateLoadingAnimations();
74
75  // Return true if this tab strip is compatible with the provided tab strip.
76  // Compatible tab strips can transfer tabs during drag and drop.
77  bool IsCompatibleWith(TabStripGtk* other);
78
79  // Returns true if Tabs in this TabStrip are currently changing size or
80  // position.
81  bool IsAnimating() const;
82
83  // Destroys the active drag controller.
84  void DestroyDragController();
85
86  // Removes the drag source tab from this tabstrip, and deletes it.
87  void DestroyDraggedSourceTab(TabGtk* tab);
88
89  // Retrieve the ideal bounds for the Tab at the specified index.
90  gfx::Rect GetIdealBounds(int index);
91
92  // Sets the vertical offset that each tab will use to offset against the
93  // background image. Passed in from the titlebar and based on the size of the
94  // alignment that sits above the tabstrip.
95  void SetVerticalOffset(int offset);
96
97  // TabstripOriginProvider implementation -------------------------------------
98  virtual gfx::Point GetTabStripOriginForWidget(GtkWidget* widget);
99
100  // ViewIDUtil::Delegate implementation ---------------------------------------
101  virtual GtkWidget* GetWidgetForViewID(ViewID id);
102
103 protected:
104  // TabStripModelObserver implementation:
105  virtual void TabInsertedAt(TabContentsWrapper* contents,
106                             int index,
107                             bool foreground);
108  virtual void TabDetachedAt(TabContentsWrapper* contents, int index);
109  virtual void TabSelectedAt(TabContentsWrapper* old_contents,
110                             TabContentsWrapper* contents,
111                             int index,
112                             bool user_gesture);
113  virtual void TabMoved(TabContentsWrapper* contents,
114                        int from_index,
115                        int to_index);
116  virtual void TabChangedAt(TabContentsWrapper* contents, int index,
117                            TabChangeType change_type);
118  virtual void TabReplacedAt(TabStripModel* tab_strip_model,
119                             TabContentsWrapper* old_contents,
120                             TabContentsWrapper* new_contents,
121                             int index);
122  virtual void TabMiniStateChanged(TabContentsWrapper* contents, int index);
123  virtual void TabBlockedStateChanged(TabContentsWrapper* contents,
124                                      int index);
125
126  // TabGtk::TabDelegate implementation:
127  virtual bool IsTabSelected(const TabGtk* tab) const;
128  virtual bool IsTabPinned(const TabGtk* tab) const;
129  virtual bool IsTabDetached(const TabGtk* tab) const;
130  virtual void SelectTab(TabGtk* tab);
131  virtual void CloseTab(TabGtk* tab);
132  virtual bool IsCommandEnabledForTab(
133      TabStripModel::ContextMenuCommand command_id, const TabGtk* tab) const;
134  virtual void ExecuteCommandForTab(
135      TabStripModel::ContextMenuCommand command_id, TabGtk* tab);
136  virtual void StartHighlightTabsForCommand(
137      TabStripModel::ContextMenuCommand command_id, TabGtk* tab);
138  virtual void StopHighlightTabsForCommand(
139      TabStripModel::ContextMenuCommand command_id, TabGtk* tab);
140  virtual void StopAllHighlighting();
141  virtual void MaybeStartDrag(TabGtk* tab, const gfx::Point& point);
142  virtual void ContinueDrag(GdkDragContext* context);
143  virtual bool EndDrag(bool canceled);
144  virtual bool HasAvailableDragActions() const;
145  virtual ui::ThemeProvider* GetThemeProvider();
146
147  // MessageLoop::Observer implementation:
148  virtual void WillProcessEvent(GdkEvent* event);
149  virtual void DidProcessEvent(GdkEvent* event);
150
151  // Overridden from NotificationObserver:
152  virtual void Observe(NotificationType type,
153                       const NotificationSource& source,
154                       const NotificationDetails& details);
155
156  // Horizontal gap between mini-tabs and normal tabs.
157  static const int mini_to_non_mini_gap_;
158
159 private:
160  friend class BrowserWindowGtk;
161  friend class DraggedTabControllerGtk;
162  friend class InsertTabAnimation;
163  friend class MiniMoveAnimation;
164  friend class MiniTabAnimation;
165  friend class MoveTabAnimation;
166  friend class RemoveTabAnimation;
167  friend class ResizeLayoutAnimation;
168  friend class TabAnimation;
169
170  struct TabData {
171    TabGtk* tab;
172    gfx::Rect ideal_bounds;
173  };
174
175  // Used during a drop session of a url. Tracks the position of the drop as
176  // well as a window used to highlight where the drop occurs.
177  class DropInfo {
178   public:
179    DropInfo(int index, bool drop_before, bool point_down);
180    virtual ~DropInfo();
181
182    // TODO(jhawkins): Factor out this code into a TransparentContainer class.
183
184    // expose-event handler that redraws the drop indicator.
185    CHROMEGTK_CALLBACK_1(DropInfo, gboolean, OnExposeEvent, GdkEventExpose*);
186
187    // Sets the color map of the container window to allow the window to be
188    // transparent.
189    void SetContainerColorMap();
190
191    // Sets full transparency for the container window.  This is used if
192    // compositing is available for the screen.
193    void SetContainerTransparency();
194
195    // Sets the shape mask for the container window to emulate a transparent
196    // container window.  This is used if compositing is not available for the
197    // screen.
198    void SetContainerShapeMask();
199
200    // Creates the container widget.
201    void CreateContainer();
202
203    // Destroys the container widget.
204    void DestroyContainer();
205
206    // Index of the tab to drop on. If drop_before is true, the drop should
207    // occur between the tab at drop_index - 1 and drop_index.
208    // WARNING: if drop_before is true it is possible this will == tab_count,
209    // which indicates the drop should create a new tab at the end of the tabs.
210    int drop_index;
211    bool drop_before;
212
213    // Direction the arrow should point in. If true, the arrow is displayed
214    // above the tab and points down. If false, the arrow is displayed beneath
215    // the tab and points up.
216    bool point_down;
217
218    // Transparent container window used to render the drop indicator over the
219    // tabstrip and toolbar.
220    GtkWidget* container;
221
222    // The drop indicator image.
223    GdkPixbuf* drop_arrow;
224
225   private:
226    DISALLOW_COPY_AND_ASSIGN(DropInfo);
227  };
228
229  // expose-event handler that redraws the tabstrip
230  CHROMEGTK_CALLBACK_1(TabStripGtk, gboolean, OnExpose, GdkEventExpose*);
231
232  // size-allocate handler that gets the new bounds of the tabstrip.
233  CHROMEGTK_CALLBACK_1(TabStripGtk, void, OnSizeAllocate, GtkAllocation*);
234
235  // drag-motion handler that is signaled when the user performs a drag in the
236  // tabstrip bounds.
237  CHROMEGTK_CALLBACK_4(TabStripGtk, gboolean, OnDragMotion, GdkDragContext*,
238                       gint, gint, guint);
239
240  // drag-drop handler that is notified when the user finishes a drag.
241  CHROMEGTK_CALLBACK_4(TabStripGtk, gboolean, OnDragDrop, GdkDragContext*,
242                       gint, gint, guint);
243
244  // drag-leave handler that is signaled when the mouse leaves the tabstrip
245  // during a drag.
246  CHROMEGTK_CALLBACK_2(TabStripGtk, gboolean, OnDragLeave, GdkDragContext*,
247                       guint);
248
249  // drag-data-received handler that receives the data associated with the drag.
250  CHROMEGTK_CALLBACK_6(TabStripGtk, gboolean, OnDragDataReceived,
251                       GdkDragContext*, gint, gint, GtkSelectionData*,
252                       guint, guint);
253
254  // Handles the clicked signal from the new tab button.
255  CHROMEGTK_CALLBACK_0(TabStripGtk, void, OnNewTabClicked);
256
257  // Sets the bounds of the tab and moves the tab widget to those bounds.
258  void SetTabBounds(TabGtk* tab, const gfx::Rect& bounds);
259
260  // Returns true if |rects| are all areas that match up with tab favicons.
261  // |rects| must be sorted from left to right.  |tabs_to_paint| are the tab
262  // positions that match the rects.
263  bool CanPaintOnlyFavicons(const GdkRectangle* rects,
264                            int num_rects,
265                            std::vector<int>* tabs_to_paint);
266
267  // Paints the tab favicon areas for tabs in |tabs_to_paint|.
268  void PaintOnlyFavicons(GdkEventExpose* event,
269                         const std::vector<int>& tabs_to_paint);
270
271  // Initializes the new tab button.
272  CustomDrawButton* MakeNewTabButton();
273
274  // Gets the number of Tabs in the collection.
275  int GetTabCount() const;
276
277  // Returns the number of mini-tabs.
278  int GetMiniTabCount() const;
279
280  // Retrieves the Tab at the specified index. Take care in using this, you may
281  // need to use GetTabAtAdjustForAnimation.
282  TabGtk* GetTabAt(int index) const;
283
284  // Returns the tab at the specified index. If a remove animation is on going
285  // and the index is >= the index of the tab being removed, the index is
286  // incremented. While a remove operation is on going the indices of the model
287  // do not line up with the indices of the view. This method adjusts the index
288  // accordingly.
289  //
290  // Use this instead of GetTabAt if the index comes from the model.
291  TabGtk* GetTabAtAdjustForAnimation(int index) const;
292
293  // Returns the exact (unrounded) current width of each tab.
294  void GetCurrentTabWidths(double* unselected_width,
295                           double* selected_width) const;
296
297  // Returns the exact (unrounded) desired width of each tab, based on the
298  // desired strip width and number of tabs.  If
299  // |width_of_tabs_for_mouse_close_| is nonnegative we use that value in
300  // calculating the desired strip width; otherwise we use the current width.
301  // |mini_tab_count| gives the number of mini-tabs, and |tab_count| the
302  // number of mini and non-mini-tabs.
303  void GetDesiredTabWidths(int tab_count,
304                           int mini_tab_count,
305                           double* unselected_width,
306                           double* selected_width) const;
307
308  // Returns the horizontal offset before the tab at |tab_index|.
309  int GetTabHOffset(int tab_index);
310
311  // Returns the x-coordinate tabs start from.
312  int tab_start_x() const;
313
314  // Perform an animated resize-relayout of the TabStrip immediately. The
315  // value returned indicates whether a resize actually took place.
316  bool ResizeLayoutTabs();
317
318  // Returns whether or not the cursor is currently in the "tab strip zone"
319  // which is defined as the region above the TabStrip and a bit below it.
320  bool IsCursorInTabStripZone() const;
321
322  // Ensure that the message loop observer used for event spying is added and
323  // removed appropriately so we can tell when to resize layout the tab strip.
324  void AddMessageLoopObserver();
325  void RemoveMessageLoopObserver();
326
327  // Calculates the available width for tabs, assuming a Tab is to be closed.
328  int GetAvailableWidthForTabs(TabGtk* last_tab) const;
329
330  // Finds the index of the TabContents corresponding to |tab| in our
331  // associated TabStripModel, or -1 if there is none (e.g. the specified |tab|
332  // is being animated closed).
333  int GetIndexOfTab(const TabGtk* tab) const;
334
335  // Cleans up the tab from the TabStrip at the specified |index|.
336  void RemoveTabAt(int index);
337
338  // Called from the message loop observer when a mouse movement has occurred
339  // anywhere over our containing window.
340  void HandleGlobalMouseMoveEvent();
341
342  // Generates the ideal bounds of the TabStrip when all Tabs have finished
343  // animating to their desired position/bounds. This is used by the standard
344  // Layout method and other callers like the DraggedTabController that need
345  // stable representations of Tab positions.
346  void GenerateIdealBounds();
347
348  // Lays out the New Tab button, assuming the right edge of the last Tab on
349  // the TabStrip at |last_tab_right|.  |unselected_width| is the width of
350  // unselected tabs at the moment this function is called.  The value changes
351  // during animations, so we can't use current_unselected_width_.
352  void LayoutNewTabButton(double last_tab_right, double unselected_width);
353
354  // -- Link Drag & Drop ------------------------------------------------------
355
356  // Returns the bounds to render the drop at, in screen coordinates. Sets
357  // |is_beneath| to indicate whether the arrow is beneath the tab, or above
358  // it.
359  gfx::Rect GetDropBounds(int drop_index, bool drop_before, bool* is_beneath);
360
361  // Updates the location of the drop based on the event.
362  void UpdateDropIndex(GdkDragContext* context, gint x, gint y);
363
364  // Sets the location of the drop, repainting as necessary.
365  void SetDropIndex(int index, bool drop_before);
366
367  // Determines whether the data is acceptable by the tabstrip and opens a new
368  // tab with the data as URL if it is.  Returns true if the drop was
369  // successful.
370  bool CompleteDrop(guchar* data, bool is_plain_text);
371
372  // Returns the image to use for indicating a drop on a tab. If is_down is
373  // true, this returns an arrow pointing down.
374  static GdkPixbuf* GetDropArrowImage(bool is_down);
375
376  // -- Animations -------------------------------------------------------------
377
378  // Stops the current animation.
379  void StopAnimation();
380
381  // A generic Layout method for various classes of TabStrip animations,
382  // including Insert, Remove and Resize Layout cases.
383  void AnimationLayout(double unselected_width);
384
385  // Starts various types of TabStrip animations.
386  void StartInsertTabAnimation(int index);
387  void StartRemoveTabAnimation(int index, TabContents* contents);
388  void StartMoveTabAnimation(int from_index, int to_index);
389  void StartMiniTabAnimation(int index);
390  void StartMiniMoveTabAnimation(int from_index,
391                                 int to_index,
392                                 const gfx::Rect& start_bounds);
393  void StartResizeLayoutAnimation();
394
395  // Notifies the TabStrip that the specified TabAnimation has completed.
396  // Optionally a full Layout will be performed, specified by |layout|.
397  void FinishAnimation(TabAnimation* animation, bool layout);
398
399  NotificationRegistrar registrar_;
400
401  // The Tabs we contain, and their last generated "good" bounds.
402  std::vector<TabData> tab_data_;
403
404  // The current widths of various types of tabs.  We save these so that, as
405  // users close tabs while we're holding them at the same size, we can lay out
406  // tabs exactly and eliminate the "pixel jitter" we'd get from just leaving
407  // them all at their existing, rounded widths.
408  double current_unselected_width_;
409  double current_selected_width_;
410
411  // If this value is nonnegative, it is used in GetDesiredTabWidths() to
412  // calculate how much space in the tab strip to use for tabs.  Most of the
413  // time this will be -1, but while we're handling closing a tab via the mouse,
414  // we'll set this to the edge of the last tab before closing, so that if we
415  // are closing the last tab and need to resize immediately, we'll resize only
416  // back to this width, thus once again placing the last tab under the mouse
417  // cursor.
418  int available_width_for_tabs_;
419
420  // True if a resize layout animation should be run a short delay after the
421  // mouse exits the TabStrip.
422  bool needs_resize_layout_;
423
424  // The GtkFixed widget.
425  OwnedWidgetGtk tabstrip_;
426
427  // The bounds of the tabstrip.
428  gfx::Rect bounds_;
429
430  // The amount to offset tab backgrounds when we are using an autogenerated
431  // tab background image.
432  int tab_vertical_offset_;
433
434  // Our model.
435  TabStripModel* model_;
436
437  // The BrowserWindowGtk containing this tab strip.
438  BrowserWindowGtk* window_;
439
440  // Theme resources.
441  GtkThemeService* theme_service_;
442
443  // The currently running animation.
444  scoped_ptr<TabAnimation> active_animation_;
445
446  // The New Tab button.
447  scoped_ptr<CustomDrawButton> newtab_button_;
448
449  // Valid for the lifetime of a drag over us.
450  scoped_ptr<DropInfo> drop_info_;
451
452  // The controller for a drag initiated from a Tab. Valid for the lifetime of
453  // the drag session.
454  scoped_ptr<DraggedTabControllerGtk> drag_controller_;
455
456  // A factory that is used to construct a delayed callback to the
457  // ResizeLayoutTabsNow method.
458  ScopedRunnableMethodFactory<TabStripGtk> resize_layout_factory_;
459
460  // True if the tabstrip has already been added as a MessageLoop observer.
461  bool added_as_message_loop_observer_;
462
463  DISALLOW_COPY_AND_ASSIGN(TabStripGtk);
464};
465
466#endif  // CHROME_BROWSER_UI_GTK_TABS_TAB_STRIP_GTK_H_
467