• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
model()48   TabStripModel* model() const { return model_; }
49 
window()50   BrowserWindowGtk* window() const { return window_; }
51 
widget()52   GtkWidget* widget() const { return tabstrip_.get(); }
53 
54   // Returns true if there is an active drag session.
IsDragSessionActive()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.
bounds()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