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