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_BOOKMARKS_BOOKMARK_BAR_GTK_H_
6#define CHROME_BROWSER_UI_GTK_BOOKMARKS_BOOKMARK_BAR_GTK_H_
7#pragma once
8
9#include <gtk/gtk.h>
10
11#include <vector>
12
13#include "base/gtest_prod_util.h"
14#include "base/memory/scoped_ptr.h"
15#include "chrome/browser/bookmarks/bookmark_context_menu_controller.h"
16#include "chrome/browser/bookmarks/bookmark_model_observer.h"
17#include "chrome/browser/prefs/pref_member.h"
18#include "chrome/browser/sync/profile_sync_service.h"
19#include "chrome/browser/ui/gtk/bookmarks/bookmark_bar_instructions_gtk.h"
20#include "chrome/browser/ui/gtk/menu_bar_helper.h"
21#include "chrome/browser/ui/gtk/owned_widget_gtk.h"
22#include "chrome/browser/ui/gtk/view_id_util.h"
23#include "content/common/notification_observer.h"
24#include "content/common/notification_registrar.h"
25#include "ui/base/animation/animation.h"
26#include "ui/base/animation/animation_delegate.h"
27#include "ui/base/animation/slide_animation.h"
28#include "ui/base/gtk/gtk_signal.h"
29#include "ui/gfx/point.h"
30#include "ui/gfx/size.h"
31
32class BookmarkMenuController;
33class Browser;
34class BrowserWindowGtk;
35class CustomContainerButton;
36class GtkThemeService;
37class MenuGtk;
38class PageNavigator;
39class Profile;
40class TabstripOriginProvider;
41
42class BookmarkBarGtk : public ui::AnimationDelegate,
43                       public ProfileSyncServiceObserver,
44                       public BookmarkModelObserver,
45                       public MenuBarHelper::Delegate,
46                       public NotificationObserver,
47                       public BookmarkBarInstructionsGtk::Delegate,
48                       public BookmarkContextMenuControllerDelegate {
49 public:
50  BookmarkBarGtk(BrowserWindowGtk* window,
51                 Profile* profile,
52                 Browser* browser,
53                 TabstripOriginProvider* tabstrip_origin_provider);
54  virtual ~BookmarkBarGtk();
55
56  // Resets the profile. This removes any buttons for the current profile and
57  // recreates the models.
58  void SetProfile(Profile* profile);
59
60  // Returns the current profile.
61  Profile* GetProfile() { return profile_; }
62
63  // Returns the current browser.
64  Browser* browser() const { return browser_; }
65
66  // Returns the top level widget.
67  GtkWidget* widget() const { return event_box_.get(); }
68
69  // Sets the PageNavigator that is used when the user selects an entry on
70  // the bookmark bar.
71  void SetPageNavigator(PageNavigator* navigator);
72
73  // Create the contents of the bookmark bar.
74  void Init(Profile* profile);
75
76  // Whether the current page is the New Tag Page (which requires different
77  // rendering).
78  bool OnNewTabPage();
79
80  // Change the visibility of the bookmarks bar. (Starts out hidden, per GTK's
81  // default behaviour). There are three visiblity states:
82  //
83  //   Showing    - bookmark bar is fully visible.
84  //   Hidden     - bookmark bar is hidden except for a few pixels that give
85  //                extra padding to the bottom of the toolbar. Buttons are not
86  //                clickable.
87  //   Fullscreen - bookmark bar is fully hidden.
88  void Show(bool animate);
89  void Hide(bool animate);
90  void EnterFullscreen();
91
92  // Get the current height of the bookmark bar.
93  int GetHeight();
94
95  // Returns true if the bookmark bar is showing an animation.
96  bool IsAnimating();
97
98  // Returns true if the bookmarks bar preference is set to 'always show'.
99  bool IsAlwaysShown();
100
101  // ui::AnimationDelegate implementation --------------------------------------
102  virtual void AnimationProgressed(const ui::Animation* animation);
103  virtual void AnimationEnded(const ui::Animation* animation);
104
105  // MenuBarHelper::Delegate implementation ------------------------------------
106  virtual void PopupForButton(GtkWidget* button);
107  virtual void PopupForButtonNextTo(GtkWidget* button,
108                                    GtkMenuDirectionType dir);
109
110  // The NTP needs to have access to this.
111  static const int kBookmarkBarNTPHeight;
112
113  // BookmarkContextMenuController::Delegate implementation --------------------
114  virtual void CloseMenu();
115
116  const ui::Animation* animation() { return &slide_animation_; }
117
118 private:
119  FRIEND_TEST_ALL_PREFIXES(BookmarkBarGtkUnittest, DisplaysHelpMessageOnEmpty);
120  FRIEND_TEST_ALL_PREFIXES(BookmarkBarGtkUnittest,
121                           HidesHelpMessageWithBookmark);
122  FRIEND_TEST_ALL_PREFIXES(BookmarkBarGtkUnittest, BuildsButtons);
123
124  // Helper function which generates GtkToolItems for |bookmark_toolbar_|.
125  void CreateAllBookmarkButtons();
126
127  // Sets the visibility of the instructional text based on whether there are
128  // any bookmarks in the bookmark bar node.
129  void SetInstructionState();
130
131  // Sets the visibility of the overflow chevron.
132  void SetChevronState();
133
134  // Helper function which destroys all the bookmark buttons in the GtkToolbar.
135  void RemoveAllBookmarkButtons();
136
137  // Returns the number of buttons corresponding to starred urls/folders. This
138  // is equivalent to the number of children the bookmark bar node from the
139  // bookmark bar model has.
140  int GetBookmarkButtonCount();
141
142  // Set the appearance of the overflow button appropriately (either chromium
143  // style or GTK style).
144  void SetOverflowButtonAppearance();
145
146  // Returns the index of the first bookmark that is not visible on the bar.
147  // Returns -1 if they are all visible.
148  // |extra_space| is how much extra space to give the toolbar during the
149  // calculation (for the purposes of determining if ditching the chevron
150  // would be a good idea).
151  // If non-NULL, |showing_folders| will be packed with all the folders that are
152  // showing on the bar.
153  int GetFirstHiddenBookmark(int extra_space,
154                             std::vector<GtkWidget*>* showing_folders);
155
156  // Returns true if the bookmark bar should be floating on the page (for
157  // NTP).
158  bool ShouldBeFloating();
159  // Update the floating state (either enable or disable it, or do nothing).
160  void UpdateFloatingState();
161
162  // Turns on or off the app_paintable flag on |event_box_|, depending on our
163  // state.
164  void UpdateEventBoxPaintability();
165
166  // Queue a paint on the event box.
167  void PaintEventBox();
168
169  // Finds the size of the current tab contents, if it exists and sets |size|
170  // to the correct value. Returns false if there isn't a TabContents, a
171  // condition that can happen during testing.
172  bool GetTabContentsSize(gfx::Size* size);
173
174  // Connects to the "size-allocate" signal on the given widget, and causes it
175  // to throb after allocation. This is called when a new item is added to the
176  // bar. We can't call StartThrobbing directly because we don't know if it's
177  // visible or not until after the widget is allocated.
178  void StartThrobbingAfterAllocation(GtkWidget* item);
179
180  // Used by StartThrobbingAfterAllocation.
181  CHROMEGTK_CALLBACK_1(BookmarkBarGtk, void, OnItemAllocate, GtkAllocation*);
182
183  // Makes the appropriate widget on the bookmark bar stop throbbing
184  // (a folder, the overflow chevron, or nothing).
185  void StartThrobbing(const BookmarkNode* node);
186
187  // Set |throbbing_widget_| to |widget|. Also makes sure that
188  // |throbbing_widget_| doesn't become stale.
189  void SetThrobbingWidget(GtkWidget* widget);
190
191  // An item has been dragged over the toolbar, update the drag context
192  // and toolbar UI appropriately.
193  gboolean ItemDraggedOverToolbar(
194      GdkDragContext* context, int index, guint time);
195
196  // When dragging in the middle of a folder, assume the user wants to drop
197  // on the folder. Towards the edges, assume the user wants to drop on the
198  // toolbar. This makes it possible to drop between two folders. This function
199  // returns the index on the toolbar the drag should target, or -1 if the
200  // drag should hit the folder.
201  int GetToolbarIndexForDragOverFolder(GtkWidget* button, gint x);
202
203  void ClearToolbarDropHighlighting();
204
205  // Overridden from BookmarkModelObserver:
206
207  // Invoked when the bookmark model has finished loading. Creates a button
208  // for each of the children of the root node from the model.
209  virtual void Loaded(BookmarkModel* model);
210
211  // Invoked when the model is being deleted.
212  virtual void BookmarkModelBeingDeleted(BookmarkModel* model);
213
214  // Invoked when a node has moved.
215  virtual void BookmarkNodeMoved(BookmarkModel* model,
216                                 const BookmarkNode* old_parent,
217                                 int old_index,
218                                 const BookmarkNode* new_parent,
219                                 int new_index);
220  virtual void BookmarkNodeAdded(BookmarkModel* model,
221                                 const BookmarkNode* parent,
222                                 int index);
223  virtual void BookmarkNodeRemoved(BookmarkModel* model,
224                                   const BookmarkNode* parent,
225                                   int old_index,
226                                   const BookmarkNode* node);
227  virtual void BookmarkNodeChanged(BookmarkModel* model,
228                                   const BookmarkNode* node);
229  // Invoked when a favicon has finished loading.
230  virtual void BookmarkNodeFaviconLoaded(BookmarkModel* model,
231                                         const BookmarkNode* node);
232  virtual void BookmarkNodeChildrenReordered(BookmarkModel* model,
233                                             const BookmarkNode* node);
234
235  // Overridden from NotificationObserver:
236  virtual void Observe(NotificationType type,
237                       const NotificationSource& source,
238                       const NotificationDetails& details);
239
240  GtkWidget* CreateBookmarkButton(const BookmarkNode* node);
241  GtkToolItem* CreateBookmarkToolItem(const BookmarkNode* node);
242
243  void ConnectFolderButtonEvents(GtkWidget* widget, bool is_tool_item);
244
245  // Finds the BookmarkNode from the model associated with |button|.
246  const BookmarkNode* GetNodeForToolButton(GtkWidget* button);
247
248  // Creates and displays a popup menu for BookmarkNode |node|.
249  void PopupMenuForNode(GtkWidget* sender, const BookmarkNode* node,
250                        GdkEventButton* event);
251
252  // GtkButton callbacks.
253  CHROMEGTK_CALLBACK_1(BookmarkBarGtk, gboolean, OnButtonPressed,
254                       GdkEventButton*);
255  CHROMEGTK_CALLBACK_1(BookmarkBarGtk, gboolean, OnSyncErrorButtonPressed,
256                       GdkEventButton*);
257  CHROMEGTK_CALLBACK_0(BookmarkBarGtk, void, OnClicked);
258  CHROMEGTK_CALLBACK_1(BookmarkBarGtk, void, OnButtonDragBegin,
259                       GdkDragContext*);
260  CHROMEGTK_CALLBACK_1(BookmarkBarGtk, void, OnButtonDragEnd, GdkDragContext*);
261  CHROMEGTK_CALLBACK_4(BookmarkBarGtk, void, OnButtonDragGet,
262                       GdkDragContext*, GtkSelectionData*, guint, guint);
263
264  // GtkButton callbacks for folder buttons.
265  CHROMEGTK_CALLBACK_0(BookmarkBarGtk, void, OnFolderClicked);
266
267  // GtkToolbar callbacks.
268  CHROMEGTK_CALLBACK_4(BookmarkBarGtk, gboolean, OnToolbarDragMotion,
269                       GdkDragContext*, gint, gint, guint);
270  CHROMEGTK_CALLBACK_1(BookmarkBarGtk, void, OnToolbarSizeAllocate,
271                       GtkAllocation*);
272
273  // Used for both folder buttons and the toolbar.
274  CHROMEGTK_CALLBACK_6(BookmarkBarGtk, void, OnDragReceived,
275                       GdkDragContext*, gint, gint, GtkSelectionData*,
276                       guint, guint);
277  CHROMEGTK_CALLBACK_2(BookmarkBarGtk, void, OnDragLeave,
278                       GdkDragContext*, guint);
279
280  // Used for folder buttons.
281  CHROMEGTK_CALLBACK_4(BookmarkBarGtk, gboolean, OnFolderDragMotion,
282                       GdkDragContext*, gint, gint, guint);
283
284  // GtkEventBox callbacks.
285  CHROMEGTK_CALLBACK_1(BookmarkBarGtk, gboolean, OnEventBoxExpose,
286                       GdkEventExpose*);
287  CHROMEGTK_CALLBACK_0(BookmarkBarGtk, void, OnEventBoxDestroy);
288
289  // Callbacks on our parent widget.
290  CHROMEGTK_CALLBACK_1(BookmarkBarGtk, void, OnParentSizeAllocate,
291                       GtkAllocation*);
292
293  // |throbbing_widget_| callback.
294  CHROMEGTK_CALLBACK_0(BookmarkBarGtk, void, OnThrobbingWidgetDestroy);
295
296  // ProfileSyncServiceObserver method.
297  virtual void OnStateChanged();
298
299  // Overriden from BookmarkBarInstructionsGtk::Delegate.
300  virtual void ShowImportDialog();
301
302  // Updates the drag&drop state when |edit_bookmarks_enabled_| changes.
303  void OnEditBookmarksEnabledChanged();
304
305  Profile* profile_;
306
307  // Used for opening urls.
308  PageNavigator* page_navigator_;
309
310  Browser* browser_;
311  BrowserWindowGtk* window_;
312
313  // Provides us with the offset into the background theme image.
314  TabstripOriginProvider* tabstrip_origin_provider_;
315
316  // Model providing details as to the starred entries/folders that should be
317  // shown. This is owned by the Profile.
318  BookmarkModel* model_;
319
320  // Contains |bookmark_hbox_|. Event box exists to prevent leakage of
321  // background color from the toplevel application window's GDK window.
322  OwnedWidgetGtk event_box_;
323
324  // Used to float the bookmark bar when on the NTP.
325  GtkWidget* ntp_padding_box_;
326
327  // Used to paint the background of the bookmark bar when in floating mode.
328  GtkWidget* paint_box_;
329
330  // Used to position all children.
331  GtkWidget* bookmark_hbox_;
332
333  // Alignment widget that is visible if there are no bookmarks on
334  // the bookmar bar.
335  GtkWidget* instructions_;
336
337  // BookmarkBarInstructionsGtk that holds the label and the link for importing
338  // bookmarks when there are no bookmarks on the bookmark bar.
339  scoped_ptr<BookmarkBarInstructionsGtk> instructions_gtk_;
340
341  // GtkToolbar which contains all the bookmark buttons.
342  OwnedWidgetGtk bookmark_toolbar_;
343
344  // The button that shows extra bookmarks that don't fit on the bookmark
345  // bar.
346  GtkWidget* overflow_button_;
347
348  // The other bookmarks button.
349  GtkWidget* other_bookmarks_button_;
350
351  // The sync error button.
352  GtkWidget* sync_error_button_;
353
354  // A pointer to the ProfileSyncService instance if one exists.
355  ProfileSyncService* sync_service_;
356
357  // The BookmarkNode from the model being dragged. NULL when we aren't
358  // dragging.
359  const BookmarkNode* dragged_node_;
360
361  // The visual representation that follows the cursor during drags.
362  GtkWidget* drag_icon_;
363
364  // We create a GtkToolbarItem from |dragged_node_| ;or display.
365  GtkToolItem* toolbar_drop_item_;
366
367  // Theme provider for building buttons.
368  GtkThemeService* theme_service_;
369
370  // Whether we should show the instructional text in the bookmark bar.
371  bool show_instructions_;
372
373  MenuBarHelper menu_bar_helper_;
374
375  // The last displayed right click menu, or NULL if no menus have been
376  // displayed yet.
377  // The controller.
378  scoped_ptr<BookmarkContextMenuController> current_context_menu_controller_;
379  // The view.
380  scoped_ptr<MenuGtk> current_context_menu_;
381
382  // The last displayed left click menu, or NULL if no menus have been
383  // displayed yet.
384  scoped_ptr<BookmarkMenuController> current_menu_;
385
386  ui::SlideAnimation slide_animation_;
387
388  // Whether we are currently configured as floating (detached from the
389  // toolbar). This reflects our actual state, and can be out of sync with
390  // what ShouldShowFloating() returns.
391  bool floating_;
392
393  // Used to optimize out |bookmark_toolbar_| size-allocate events we don't
394  // need to respond to.
395  int last_allocation_width_;
396
397  NotificationRegistrar registrar_;
398
399  // The size of the tab contents last time we forced a paint. We keep track
400  // of this so we don't force too many paints.
401  gfx::Size last_tab_contents_size_;
402
403  // The last coordinates recorded by OnButtonPress; used to line up the
404  // drag icon during bookmark drags.
405  gfx::Point last_pressed_coordinates_;
406
407  // The currently throbbing widget. This is NULL if no widget is throbbing.
408  // We track it because we only want to allow one widget to throb at a time.
409  GtkWidget* throbbing_widget_;
410
411  // Tracks whether bookmarks can be modified.
412  BooleanPrefMember edit_bookmarks_enabled_;
413
414  ScopedRunnableMethodFactory<BookmarkBarGtk> method_factory_;
415};
416
417#endif  // CHROME_BROWSER_UI_GTK_BOOKMARKS_BOOKMARK_BAR_GTK_H_
418