Home | History | Annotate | Download | only in tabs
      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 
     24 class BrowserWindowGtk;
     25 class CustomDrawButton;
     26 class DraggedTabControllerGtk;
     27 class GtkThemeService;
     28 
     29 class 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