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_VIEWS_BOOKMARKS_BOOKMARK_BAR_VIEW_H_
6#define CHROME_BROWSER_UI_VIEWS_BOOKMARKS_BOOKMARK_BAR_VIEW_H_
7#pragma once
8
9#include <set>
10
11#include "base/compiler_specific.h"
12#include "chrome/browser/bookmarks/bookmark_model_observer.h"
13#include "chrome/browser/bookmarks/bookmark_node_data.h"
14#include "chrome/browser/sync/profile_sync_service.h"
15#include "chrome/browser/ui/views/bookmarks/bookmark_bar_instructions_view.h"
16#include "chrome/browser/ui/views/bookmarks/bookmark_menu_controller_views.h"
17#include "chrome/browser/ui/views/detachable_toolbar_view.h"
18#include "content/common/notification_registrar.h"
19#include "ui/base/animation/animation_delegate.h"
20#include "views/controls/button/button.h"
21#include "views/controls/menu/view_menu_delegate.h"
22
23class Browser;
24class PageNavigator;
25class PrefService;
26
27namespace ui {
28class SlideAnimation;
29}
30
31namespace views {
32class CustomButton;
33class MenuButton;
34class MenuItemView;
35class TextButton;
36}
37
38// BookmarkBarView renders the BookmarkModel.  Each starred entry on the
39// BookmarkBar is rendered as a MenuButton. An additional MenuButton aligned to
40// the right allows the user to quickly see recently starred entries.
41//
42// BookmarkBarView shows the bookmarks from a specific Profile. BookmarkBarView
43// waits until the HistoryService for the profile has been loaded before
44// creating the BookmarkModel.
45class BookmarkBarView : public DetachableToolbarView,
46                        public ProfileSyncServiceObserver,
47                        public BookmarkModelObserver,
48                        public views::ViewMenuDelegate,
49                        public views::ButtonListener,
50                        public NotificationObserver,
51                        public views::ContextMenuController,
52                        public views::DragController,
53                        public ui::AnimationDelegate,
54                        public BookmarkMenuController::Observer,
55                        public BookmarkBarInstructionsView::Delegate {
56  friend class ShowFolderMenuTask;
57
58 public:
59  // Constants used in Browser View, as well as here.
60  // How inset the bookmarks bar is when displayed on the new tab page.
61  static const int kNewtabHorizontalPadding;
62  static const int kNewtabVerticalPadding;
63
64  // Maximum size of buttons on the bookmark bar.
65  static const int kMaxButtonWidth;
66
67  // Interface implemented by controllers/views that need to be notified any
68  // time the model changes, typically to cancel an operation that is showing
69  // data from the model such as a menu. This isn't intended as a general
70  // way to be notified of changes, rather for cases where a controller/view is
71  // showing data from the model in a modal like setting and needs to cleanly
72  // exit the modal loop if the model changes out from under it.
73  //
74  // A controller/view that needs this notification should install itself as the
75  // ModelChangeListener via the SetModelChangedListener method when shown and
76  // reset the ModelChangeListener of the BookmarkBarView when it closes by way
77  // of either the SetModelChangedListener method or the
78  // ClearModelChangedListenerIfEquals method.
79  class ModelChangedListener {
80   public:
81    virtual ~ModelChangedListener() {}
82
83    // Invoked when the model changes. Should cancel the edit and close any
84    // dialogs.
85    virtual void ModelChanged() = 0;
86  };
87
88  static const int kNewtabBarHeight;
89
90  BookmarkBarView(Profile* profile, Browser* browser);
91  virtual ~BookmarkBarView();
92
93  // Resets the profile. This removes any buttons for the current profile and
94  // recreates the models.
95  void SetProfile(Profile* profile);
96
97  // Returns the current profile.
98  Profile* GetProfile() { return profile_; }
99
100  // Returns the current browser.
101  Browser* browser() const { return browser_; }
102
103  // Sets the PageNavigator that is used when the user selects an entry on
104  // the bookmark bar.
105  void SetPageNavigator(PageNavigator* navigator);
106
107  // Sets whether the containing browser is showing an infobar.  This affects
108  // layout during animation.
109  void set_infobar_visible(bool infobar_visible) {
110    infobar_visible_ = infobar_visible;
111  }
112
113  virtual bool IsOnTop() const;
114
115  // DetachableToolbarView methods:
116  virtual bool IsDetached() const OVERRIDE;
117  virtual double GetAnimationValue() const OVERRIDE;
118  virtual int GetToolbarOverlap() const OVERRIDE;
119
120  // View methods:
121  virtual gfx::Size GetPreferredSize() OVERRIDE;
122  virtual gfx::Size GetMinimumSize() OVERRIDE;
123  virtual void Layout() OVERRIDE;
124  virtual void ViewHierarchyChanged(bool is_add, View* parent, View* child)
125      OVERRIDE;
126  virtual void PaintChildren(gfx::Canvas* canvas) OVERRIDE;
127  virtual bool GetDropFormats(
128      int* formats,
129      std::set<ui::OSExchangeData::CustomFormat>* custom_formats) OVERRIDE;
130  virtual bool AreDropTypesRequired() OVERRIDE;
131  virtual bool CanDrop(const ui::OSExchangeData& data) OVERRIDE;
132  virtual void OnDragEntered(const views::DropTargetEvent& event) OVERRIDE;
133  virtual int OnDragUpdated(const views::DropTargetEvent& event) OVERRIDE;
134  virtual void OnDragExited() OVERRIDE;
135  virtual int OnPerformDrop(const views::DropTargetEvent& event) OVERRIDE;
136  virtual void ShowContextMenu(const gfx::Point& p, bool is_mouse_gesture)
137      OVERRIDE;
138
139  // AccessiblePaneView methods:
140  virtual void GetAccessibleState(ui::AccessibleViewState* state) OVERRIDE;
141
142  // ProfileSyncServiceObserver method.
143  virtual void OnStateChanged();
144
145  // Called when fullscreen mode toggles on or off; this affects our layout.
146  void OnFullscreenToggled(bool fullscreen);
147
148  // Sets the model change listener to listener.
149  void SetModelChangedListener(ModelChangedListener* listener) {
150    model_changed_listener_ = listener;
151  }
152
153  // If the ModelChangedListener is listener, ModelChangeListener is set to
154  // NULL.
155  void ClearModelChangedListenerIfEquals(ModelChangedListener* listener) {
156    if (model_changed_listener_ == listener)
157      model_changed_listener_ = NULL;
158  }
159
160  // Returns the model change listener.
161  ModelChangedListener* GetModelChangedListener() {
162    return model_changed_listener_;
163  }
164
165  // Returns the page navigator.
166  PageNavigator* GetPageNavigator() { return page_navigator_; }
167
168  // Returns the model.
169  BookmarkModel* GetModel() { return model_; }
170
171  // Returns true if the bookmarks bar preference is set to 'always show'.
172  bool IsAlwaysShown() const;
173
174  // True if we're on a page where the bookmarks bar is always visible.
175  bool OnNewTabPage() const;
176
177  // How much we want the bookmark bar to overlap the toolbar.  If |return_max|
178  // is true, we return the maximum overlap rather than the current overlap.
179  int GetToolbarOverlap(bool return_max) const;
180
181  // Whether or not we are animating.
182  bool is_animating();
183
184  // SlideAnimationDelegate implementation.
185  virtual void AnimationProgressed(const ui::Animation* animation);
186  virtual void AnimationEnded(const ui::Animation* animation);
187
188  // BookmarkMenuController::Observer
189  virtual void BookmarkMenuDeleted(BookmarkMenuController* controller);
190
191  // Returns the button at the specified index.
192  views::TextButton* GetBookmarkButton(int index);
193
194  // Returns the button responsible for showing bookmarks in the other bookmark
195  // folder.
196  views::MenuButton* other_bookmarked_button() const {
197    return other_bookmarked_button_;
198  }
199
200  // Returns the active MenuItemView, or NULL if a menu isn't showing.
201  views::MenuItemView* GetMenu();
202
203  // Returns the drop MenuItemView, or NULL if a menu isn't showing.
204  views::MenuItemView* GetDropMenu();
205
206  // Returns the context menu, or null if one isn't showing.
207  views::MenuItemView* GetContextMenu();
208
209  // Returns the button used when not all the items on the bookmark bar fit.
210  views::MenuButton* overflow_button() const { return overflow_button_; }
211
212  // If |loc| is over a bookmark button the node is returned corresponding
213  // to the button and |start_index| is set to 0. If a overflow button is
214  // showing and |loc| is over the overflow button, the bookmark bar node is
215  // returned and |start_index| is set to the index of the first node
216  // contained in the overflow menu.
217  const BookmarkNode* GetNodeForButtonAt(const gfx::Point& loc,
218                                         int* start_index);
219
220  // Returns the MenuButton for node.
221  views::MenuButton* GetMenuButtonForNode(const BookmarkNode* node);
222
223  // Returns the position to anchor the menu for |button| at, the index of the
224  // first child of the node to build the menu from.
225  void GetAnchorPositionAndStartIndexForButton(
226      views::MenuButton* button,
227      views::MenuItemView::AnchorPosition* anchor,
228      int* start_index);
229
230  // BookmarkBarInstructionsView::Delegate.
231  virtual void ShowImportDialog();
232
233  // If a button is currently throbbing, it is stopped. If immediate is true
234  // the throb stops immediately, otherwise it stops after a couple more
235  // throbs.
236  void StopThrobbing(bool immediate);
237
238  // Returns the number of buttons corresponding to starred urls/folders. This
239  // is equivalent to the number of children the bookmark bar node from the
240  // bookmark bar model has.
241  int GetBookmarkButtonCount();
242
243  // Returns the tooltip text for the specified url and title. The returned
244  // text is clipped to fit within the bounds of the monitor.
245  //
246  // Note that we adjust the direction of both the URL and the title based on
247  // the locale so that pure LTR strings are displayed properly in RTL locales.
248  static std::wstring CreateToolTipForURLAndTitle(
249      const gfx::Point& screen_loc,
250      const GURL& url,
251      const std::wstring& title,
252      Profile* profile);
253
254  // If true we're running tests. This short circuits a couple of animations.
255  static bool testing_;
256
257 private:
258  class ButtonSeparatorView;
259  struct DropInfo;
260
261  // Task that invokes ShowDropFolderForNode when run. ShowFolderDropMenuTask
262  // deletes itself once run.
263  class ShowFolderDropMenuTask : public Task {
264   public:
265    ShowFolderDropMenuTask(BookmarkBarView* view, const BookmarkNode* node)
266      : view_(view),
267        node_(node) {
268    }
269
270    void Cancel() {
271      view_->show_folder_drop_menu_task_ = NULL;
272      view_ = NULL;
273    }
274
275    virtual void Run() {
276      if (view_) {
277        view_->show_folder_drop_menu_task_ = NULL;
278        view_->ShowDropFolderForNode(node_);
279      }
280      // MessageLoop deletes us.
281    }
282
283   private:
284    BookmarkBarView* view_;
285    const BookmarkNode* node_;
286
287    DISALLOW_COPY_AND_ASSIGN(ShowFolderDropMenuTask);
288  };
289
290  // Creates recent bookmark button and when visible button as well as
291  // calculating the preferred height.
292  void Init();
293
294  // Creates the button showing the other bookmarked items.
295  views::MenuButton* CreateOtherBookmarkedButton();
296
297  // Creates the button used when not all bookmark buttons fit.
298  views::MenuButton* CreateOverflowButton();
299
300  // Invoked when the bookmark bar model has finished loading. Creates a button
301  // for each of the children of the root node from the model.
302  virtual void Loaded(BookmarkModel* model);
303
304  // Invoked when the model is being deleted.
305  virtual void BookmarkModelBeingDeleted(BookmarkModel* model);
306
307  // Invokes added followed by removed.
308  virtual void BookmarkNodeMoved(BookmarkModel* model,
309                                 const BookmarkNode* old_parent,
310                                 int old_index,
311                                 const BookmarkNode* new_parent,
312                                 int new_index);
313
314  // Notifies ModelChangeListener of change.
315  // If the node was added to the root node, a button is created and added to
316  // this bookmark bar view.
317  virtual void BookmarkNodeAdded(BookmarkModel* model,
318                                 const BookmarkNode* parent,
319                                 int index);
320
321  // Implementation for BookmarkNodeAddedImpl.
322  void BookmarkNodeAddedImpl(BookmarkModel* model,
323                             const BookmarkNode* parent,
324                             int index);
325
326  // Notifies ModelChangeListener of change.
327  // If the node was a child of the root node, the button corresponding to it
328  // is removed.
329  virtual void BookmarkNodeRemoved(BookmarkModel* model,
330                                   const BookmarkNode* parent,
331                                   int old_index,
332                                   const BookmarkNode* node);
333
334  // Implementation for BookmarkNodeRemoved.
335  void BookmarkNodeRemovedImpl(BookmarkModel* model,
336                               const BookmarkNode* parent,
337                               int index);
338
339  // Notifies ModelChangedListener and invokes BookmarkNodeChangedImpl.
340  virtual void BookmarkNodeChanged(BookmarkModel* model,
341                                   const BookmarkNode* node);
342
343  // If the node is a child of the root node, the button is updated
344  // appropriately.
345  void BookmarkNodeChangedImpl(BookmarkModel* model, const BookmarkNode* node);
346
347  virtual void BookmarkNodeChildrenReordered(BookmarkModel* model,
348                                             const BookmarkNode* node);
349
350  // Invoked when the favicon is available. If the node is a child of the
351  // root node, the appropriate button is updated. If a menu is showing, the
352  // call is forwarded to the menu to allow for it to update the icon.
353  virtual void BookmarkNodeFaviconLoaded(BookmarkModel* model,
354                                         const BookmarkNode* node);
355
356  // DragController method. Determines the node representing sender and invokes
357  // WriteDragData to write the actual data.
358  virtual void WriteDragDataForView(views::View* sender,
359                                    const gfx::Point& press_pt,
360                                    ui::OSExchangeData* data) OVERRIDE;
361
362  virtual int GetDragOperationsForView(views::View* sender,
363                                       const gfx::Point& p) OVERRIDE;
364
365  virtual bool CanStartDragForView(views::View* sender,
366                                   const gfx::Point& press_pt,
367                                   const gfx::Point& p) OVERRIDE;
368
369  // Writes a BookmarkNodeData for node to data.
370  void WriteBookmarkDragData(const BookmarkNode* node,
371                             ui::OSExchangeData* data);
372
373  // ViewMenuDelegate method. Ends up creating a BookmarkMenuController to
374  // show the menu.
375  virtual void RunMenu(views::View* view, const gfx::Point& pt);
376
377  // Invoked when a star entry corresponding to a URL on the bookmark bar is
378  // pressed. Forwards to the PageNavigator to open the URL.
379  virtual void ButtonPressed(views::Button* sender, const views::Event& event);
380
381  // Invoked for this View, one of the buttons or the 'other' button. Shows the
382  // appropriate context menu.
383  virtual void ShowContextMenuForView(views::View* source,
384                                      const gfx::Point& p,
385                                      bool is_mouse_gesture);
386
387  // Creates the button for rendering the specified bookmark node.
388  views::View* CreateBookmarkButton(const BookmarkNode* node);
389
390  // COnfigures the button from the specified node. This sets the text,
391  // and icon.
392  void ConfigureButton(const BookmarkNode* node, views::TextButton* button);
393
394  // Used when showing the menu allowing the user to choose when the bar is
395  // visible. Return value corresponds to the users preference for when the
396  // bar is visible.
397  virtual bool IsItemChecked(int id) const;
398
399  // Used when showing the menu allowing the user to choose when the bar is
400  // visible. Updates the preferences to match the users choice as appropriate.
401  virtual void ExecuteCommand(int id);
402
403  // NotificationService method.
404  virtual void Observe(NotificationType type,
405                       const NotificationSource& source,
406                       const NotificationDetails& details);
407
408  // Overridden from views::View.
409  virtual void OnThemeChanged();
410
411  // If the ModelChangedListener is non-null, ModelChanged is invoked on it.
412  void NotifyModelChanged();
413
414  // Shows the menu used during drag and drop for the specified node.
415  void ShowDropFolderForNode(const BookmarkNode* node);
416
417  // Cancels the timer used to show a drop menu.
418  void StopShowFolderDropMenuTimer();
419
420  // Stars the timer used to show a drop menu for node.
421  void StartShowFolderDropMenuTimer(const BookmarkNode* node);
422
423  // Returns the drop operation and index for the drop based on the event
424  // and data. Returns ui::DragDropTypes::DRAG_NONE if not a valid location.
425  int CalculateDropOperation(const views::DropTargetEvent& event,
426                             const BookmarkNodeData& data,
427                             int* index,
428                             bool* drop_on,
429                             bool* is_over_overflow,
430                             bool* is_over_other);
431
432  // Returns the index of the first hidden bookmark button. If all buttons are
433  // visible, this returns GetBookmarkButtonCount().
434  int GetFirstHiddenNodeIndex();
435
436  // This determines which view should throb and starts it
437  // throbbing (e.g when the bookmark bubble is showing).
438  // If |overflow_only| is true, start throbbing only if |node| is hidden in
439  // the overflow menu.
440  void StartThrobbing(const BookmarkNode* node, bool overflow_only);
441
442  // Returns the view to throb when a node is removed. |parent| is the parent of
443  // the node that was removed, and |old_index| the index of the node that was
444  // removed.
445  views::CustomButton* DetermineViewToThrobFromRemove(
446      const BookmarkNode* parent,
447      int old_index);
448
449  // Updates the colors for all the child objects in the bookmarks bar.
450  void UpdateColors();
451
452  // Updates the visibility of |other_bookmarked_button_| and
453  // |bookmarks_separator_view_|.
454  void UpdateOtherBookmarksVisibility();
455
456  // This method computes the bounds for the bookmark bar items. If
457  // |compute_bounds_only| = TRUE, the bounds for the items are just computed,
458  // but are not set. This mode is used by GetPreferredSize() to obtain the
459  // desired bounds. If |compute_bounds_only| = FALSE, the bounds are set.
460  gfx::Size LayoutItems(bool compute_bounds_only);
461
462  // Creates the sync error button and adds it as a child view.
463  views::TextButton* CreateSyncErrorButton();
464
465  NotificationRegistrar registrar_;
466
467  Profile* profile_;
468
469  // Used for opening urls.
470  PageNavigator* page_navigator_;
471
472  // Model providing details as to the starred entries/folders that should be
473  // shown. This is owned by the Profile.
474  BookmarkModel* model_;
475
476  // Used to manage showing a Menu, either for the most recently bookmarked
477  // entries, or for the starred folder.
478  BookmarkMenuController* bookmark_menu_;
479
480  // Used when showing a menu for drag and drop. That is, if the user drags
481  // over a folder this becomes non-null and manages the menu showing the
482  // contents of the node.
483  BookmarkMenuController* bookmark_drop_menu_;
484
485  // Shows the other bookmark entries.
486  views::MenuButton* other_bookmarked_button_;
487
488  // ModelChangeListener.
489  ModelChangedListener* model_changed_listener_;
490
491  // Task used to delay showing of the drop menu.
492  ShowFolderDropMenuTask* show_folder_drop_menu_task_;
493
494  // Used to track drops on the bookmark bar view.
495  scoped_ptr<DropInfo> drop_info_;
496
497  // The sync re-login indicator which appears when the user needs to re-enter
498  // credentials in order to continue syncing.
499  views::TextButton* sync_error_button_;
500
501  // A pointer to the ProfileSyncService instance if one exists.
502  ProfileSyncService* sync_service_;
503
504  // Visible if not all the bookmark buttons fit.
505  views::MenuButton* overflow_button_;
506
507  // BookmarkBarInstructionsView that is visible if there are no bookmarks on
508  // the bookmark bar.
509  views::View* instructions_;
510
511  ButtonSeparatorView* bookmarks_separator_view_;
512
513  // Owning browser. This is NULL during testing.
514  Browser* browser_;
515
516  // True if the owning browser is showing an infobar.
517  bool infobar_visible_;
518
519  // Animation controlling showing and hiding of the bar.
520  scoped_ptr<ui::SlideAnimation> size_animation_;
521
522  // If the bookmark bubble is showing, this is the visible ancestor of the URL.
523  // The visible ancestor is either the other_bookmarked_button_,
524  // overflow_button_ or a button on the bar.
525  views::CustomButton* throbbing_view_;
526
527  // Background for extension toolstrips.
528  SkBitmap toolstrip_background_;
529
530  DISALLOW_COPY_AND_ASSIGN(BookmarkBarView);
531};
532
533#endif  // CHROME_BROWSER_UI_VIEWS_BOOKMARKS_BOOKMARK_BAR_VIEW_H_
534