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_VIEWS_BOOKMARKS_BOOKMARK_BAR_VIEW_H_
6 #define CHROME_BROWSER_UI_VIEWS_BOOKMARKS_BOOKMARK_BAR_VIEW_H_
7 
8 #include <memory>
9 #include <set>
10 
11 #include "base/compiler_specific.h"
12 #include "base/macros.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/observer_list.h"
15 #include "chrome/browser/ui/bookmarks/bookmark_bar.h"
16 #include "chrome/browser/ui/bookmarks/bookmark_bubble_observer.h"
17 #include "chrome/browser/ui/bookmarks/bookmark_stats.h"
18 #include "chrome/browser/ui/views/bookmarks/bookmark_menu_controller_observer.h"
19 #include "components/bookmarks/browser/bookmark_model_observer.h"
20 #include "components/bookmarks/browser/bookmark_node_data.h"
21 #include "components/prefs/pref_change_registrar.h"
22 #include "ui/gfx/animation/slide_animation.h"
23 #include "ui/views/accessible_pane_view.h"
24 #include "ui/views/animation/animation_delegate_views.h"
25 #include "ui/views/context_menu_controller.h"
26 #include "ui/views/controls/menu/menu_types.h"
27 #include "ui/views/drag_controller.h"
28 
29 class BookmarkBarViewObserver;
30 class BookmarkBarViewTestHelper;
31 class BookmarkContextMenu;
32 class Browser;
33 class BrowserView;
34 class Profile;
35 class ReadLaterButton;
36 
37 namespace bookmarks {
38 class BookmarkModel;
39 class ManagedBookmarkService;
40 }  // namespace bookmarks
41 
42 namespace content {
43 class PageNavigator;
44 }
45 
46 namespace gfx {
47 class FontList;
48 }
49 
50 namespace views {
51 class Button;
52 class MenuButton;
53 class MenuItemView;
54 class LabelButton;
55 }  // namespace views
56 
57 // BookmarkBarView renders the BookmarkModel.  Each starred entry on the
58 // BookmarkBar is rendered as a MenuButton. An additional MenuButton aligned to
59 // the right allows the user to quickly see recently starred entries.
60 //
61 // BookmarkBarView shows the bookmarks from a specific Profile. BookmarkBarView
62 // waits until the HistoryService for the profile has been loaded before
63 // creating the BookmarkModel.
64 class BookmarkBarView : public views::AccessiblePaneView,
65                         public bookmarks::BookmarkModelObserver,
66                         public views::ContextMenuController,
67                         public views::DragController,
68                         public views::AnimationDelegateViews,
69                         public BookmarkMenuControllerObserver,
70                         public bookmarks::BookmarkBubbleObserver {
71  public:
72   // The internal view class name.
73   static const char kViewClassName[];
74 
75   // |browser_view| can be NULL during tests.
76   BookmarkBarView(Browser* browser, BrowserView* browser_view);
77   ~BookmarkBarView() override;
78 
79   static void DisableAnimationsForTesting(bool disabled);
80 
81   // Returns the current browser.
browser()82   Browser* browser() const { return browser_; }
83 
84   void AddObserver(BookmarkBarViewObserver* observer);
85   void RemoveObserver(BookmarkBarViewObserver* observer);
86 
87   // Sets the PageNavigator that is used when the user selects an entry on
88   // the bookmark bar.
89   void SetPageNavigator(content::PageNavigator* navigator);
90 
91   // Sets whether the containing browser is showing an infobar.  This affects
92   // layout during animation.
93   void SetInfoBarVisible(bool infobar_visible);
94 
95   // Changes the state of the bookmark bar.
96   void SetBookmarkBarState(BookmarkBar::State state,
97                            BookmarkBar::AnimateChangeType animate_type);
98 
99   // If |loc| is over a bookmark button the node is returned corresponding to
100   // the button and |model_start_index| is set to 0. If a overflow button is
101   // showing and |loc| is over the overflow button, the bookmark bar node is
102   // returned and |model_start_index| is set to the index of the first node
103   // contained in the overflow menu.
104   const bookmarks::BookmarkNode* GetNodeForButtonAtModelIndex(
105       const gfx::Point& loc,
106       size_t* model_start_index);
107 
108   // Returns the MenuButton for node.
109   views::MenuButton* GetMenuButtonForNode(const bookmarks::BookmarkNode* node);
110 
111   // Returns the position to anchor the menu for |button| at.
112   void GetAnchorPositionForButton(views::MenuButton* button,
113                                   views::MenuAnchorPosition* anchor);
114 
115   // Returns the button responsible for showing bookmarks in the
116   // "Other Bookmarks" folder.
other_bookmarks_button()117   views::MenuButton* other_bookmarks_button() const {
118     return other_bookmarks_button_;
119   }
120 
121   // Returns the button used when not all the items on the bookmark bar fit.
overflow_button()122   views::MenuButton* overflow_button() const { return overflow_button_; }
123 
read_later_button()124   ReadLaterButton* read_later_button() const { return read_later_button_; }
125 
size_animation()126   const gfx::Animation& size_animation() { return size_animation_; }
127 
128   // Returns the active MenuItemView, or NULL if a menu isn't showing.
129   views::MenuItemView* GetMenu();
130 
131   // Returns the context menu, or null if one isn't showing.
132   views::MenuItemView* GetContextMenu();
133 
134   // Returns the drop MenuItemView, or NULL if a menu isn't showing.
135   views::MenuItemView* GetDropMenu();
136 
137   // If a button is currently throbbing, it is stopped. If immediate is true
138   // the throb stops immediately, otherwise it stops after a couple more
139   // throbs.
140   void StopThrobbing(bool immediate);
141 
142   // Returns the tooltip text for the specified url and title. The returned
143   // text is clipped to fit |max_tooltip_width|.
144   //
145   // Note that we adjust the direction of both the URL and the title based on
146   // the locale so that pure LTR strings are displayed properly in RTL locales.
147   static base::string16 CreateToolTipForURLAndTitle(
148       int max_tooltip_width,
149       const gfx::FontList& font_list,
150       const GURL& url,
151       const base::string16& title);
152 
153   // views::View:
154   gfx::Size CalculatePreferredSize() const override;
155   gfx::Size GetMinimumSize() const override;
156   void Layout() override;
157   void ViewHierarchyChanged(
158       const views::ViewHierarchyChangedDetails& details) override;
159   void PaintChildren(const views::PaintInfo& paint_info) override;
160   bool GetDropFormats(int* formats,
161                       std::set<ui::ClipboardFormatType>* format_types) override;
162   bool AreDropTypesRequired() override;
163   bool CanDrop(const ui::OSExchangeData& data) override;
164   void OnDragEntered(const ui::DropTargetEvent& event) override;
165   int OnDragUpdated(const ui::DropTargetEvent& event) override;
166   void OnDragExited() override;
167   int OnPerformDrop(const ui::DropTargetEvent& event) override;
168   void OnThemeChanged() override;
169   const char* GetClassName() const override;
170   void VisibilityChanged(View* starting_from, bool is_visible) override;
171 
172   // AccessiblePaneView:
173   void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
174 
175   // views::AnimationDelegateViews:
176   void AnimationProgressed(const gfx::Animation* animation) override;
177   void AnimationEnded(const gfx::Animation* animation) override;
178 
179   // BookmarkMenuControllerObserver:
180   void BookmarkMenuControllerDeleted(
181       BookmarkMenuController* controller) override;
182 
183   // bookmarks::BookmarkBubbleObserver:
184   void OnBookmarkBubbleShown(const bookmarks::BookmarkNode* node) override;
185   void OnBookmarkBubbleHidden() override;
186 
187   // bookmarks::BookmarkModelObserver:
188   void BookmarkModelLoaded(bookmarks::BookmarkModel* model,
189                            bool ids_reassigned) override;
190   void BookmarkModelBeingDeleted(bookmarks::BookmarkModel* model) override;
191   void BookmarkNodeMoved(bookmarks::BookmarkModel* model,
192                          const bookmarks::BookmarkNode* old_parent,
193                          size_t old_index,
194                          const bookmarks::BookmarkNode* new_parent,
195                          size_t new_index) override;
196   void BookmarkNodeAdded(bookmarks::BookmarkModel* model,
197                          const bookmarks::BookmarkNode* parent,
198                          size_t index) override;
199   void BookmarkNodeRemoved(bookmarks::BookmarkModel* model,
200                            const bookmarks::BookmarkNode* parent,
201                            size_t old_index,
202                            const bookmarks::BookmarkNode* node,
203                            const std::set<GURL>& removed_urls) override;
204   void BookmarkAllUserNodesRemoved(bookmarks::BookmarkModel* model,
205                                    const std::set<GURL>& removed_urls) override;
206   void BookmarkNodeChanged(bookmarks::BookmarkModel* model,
207                            const bookmarks::BookmarkNode* node) override;
208   void BookmarkNodeChildrenReordered(
209       bookmarks::BookmarkModel* model,
210       const bookmarks::BookmarkNode* node) override;
211   void BookmarkNodeFaviconChanged(bookmarks::BookmarkModel* model,
212                                   const bookmarks::BookmarkNode* node) override;
213 
214   // views::DragController:
215   void WriteDragDataForView(views::View* sender,
216                             const gfx::Point& press_pt,
217                             ui::OSExchangeData* data) override;
218   int GetDragOperationsForView(views::View* sender,
219                                const gfx::Point& p) override;
220   bool CanStartDragForView(views::View* sender,
221                            const gfx::Point& press_pt,
222                            const gfx::Point& p) override;
223 
224   // views::ContextMenuController:
225   void ShowContextMenuForViewImpl(views::View* source,
226                                   const gfx::Point& point,
227                                   ui::MenuSourceType source_type) override;
228 
229  private:
230   class ButtonSeparatorView;
231   struct DropInfo;
232   struct DropLocation;
233 
234   friend class BookmarkBarViewTestHelper;
235   friend class BookmarkBarViewEventTestBase;
236 
237   // Used to identify what the user is dropping onto.
238   enum DropButtonType { DROP_BOOKMARK, DROP_OTHER_FOLDER, DROP_OVERFLOW };
239 
240   // Creates recent bookmark button and when visible button as well as
241   // calculating the preferred height.
242   void Init();
243 
244   void AppsPageShortcutPressed(const ui::Event& event);
245   void OnButtonPressed(const bookmarks::BookmarkNode* node,
246                        const ui::Event& event);
247   void OnMenuButtonPressed(const bookmarks::BookmarkNode* node,
248                            const ui::Event& event);
249 
250   // NOTE: unless otherwise stated all methods that take an index are in terms
251   // of the bookmark bar view. Typically the view index and model index are the
252   // same, but they may differ during animations or drag and drop.
253   //
254   // It's easy to get the mapping wrong. For this reason all these methods are
255   // private.
256 
257   // Returns the index of the first hidden bookmark button. If all buttons are
258   // visible, this returns GetBookmarkButtonCount().
259   size_t GetFirstHiddenNodeIndex();
260 
261   // Creates the button showing the "Other Bookmarks" folder.
262   std::unique_ptr<views::MenuButton> CreateOtherBookmarksButton();
263 
264   // Creates the button showing the "Managed Bookmarks" folder.
265   std::unique_ptr<views::MenuButton> CreateManagedBookmarksButton();
266 
267   // Creates the button used when not all bookmark buttons fit.
268   std::unique_ptr<views::MenuButton> CreateOverflowButton();
269 
270   // Creates the button for rendering the specified bookmark node.
271   std::unique_ptr<views::View> CreateBookmarkButton(
272       const bookmarks::BookmarkNode* node);
273 
274   // Creates the button for rendering the apps page shortcut.
275   std::unique_ptr<views::LabelButton> CreateAppsPageShortcutButton();
276 
277   // Configures the button from the specified node. This sets the text,
278   // and icon.
279   void ConfigureButton(const bookmarks::BookmarkNode* node,
280                        views::LabelButton* button);
281 
282   // Implementation for BookmarkNodeAddedImpl. Returns true if LayoutAndPaint()
283   // is required.
284   bool BookmarkNodeAddedImpl(bookmarks::BookmarkModel* model,
285                              const bookmarks::BookmarkNode* parent,
286                              size_t index);
287 
288   // Implementation for BookmarkNodeRemoved. Returns true if LayoutAndPaint() is
289   // required.
290   bool BookmarkNodeRemovedImpl(bookmarks::BookmarkModel* model,
291                                const bookmarks::BookmarkNode* parent,
292                                size_t index);
293 
294   // If the node is a child of the root node, the button is updated
295   // appropriately.
296   void BookmarkNodeChangedImpl(bookmarks::BookmarkModel* model,
297                                const bookmarks::BookmarkNode* node);
298 
299   // Shows the menu used during drag and drop for the specified node.
300   void ShowDropFolderForNode(const bookmarks::BookmarkNode* node);
301 
302   // Cancels the timer used to show a drop menu.
303   void StopShowFolderDropMenuTimer();
304 
305   // Stars the timer used to show a drop menu for node.
306   void StartShowFolderDropMenuTimer(const bookmarks::BookmarkNode* node);
307 
308   // Calculates the location for the drop in |location|.
309   void CalculateDropLocation(const ui::DropTargetEvent& event,
310                              const bookmarks::BookmarkNodeData& data,
311                              DropLocation* location);
312 
313   // Returns the node corresponding to |sender|, which is one of the
314   // |bookmark_buttons_|.
315   const bookmarks::BookmarkNode* GetNodeForSender(View* sender) const;
316 
317   // Writes a BookmarkNodeData for node to data.
318   void WriteBookmarkDragData(const bookmarks::BookmarkNode* node,
319                              ui::OSExchangeData* data);
320 
321   // This determines which view should throb and starts it
322   // throbbing (e.g when the bookmark bubble is showing).
323   // If |overflow_only| is true, start throbbing only if |node| is hidden in
324   // the overflow menu.
325   void StartThrobbing(const bookmarks::BookmarkNode* node, bool overflow_only);
326 
327   // Returns the view to throb when a node is removed. |parent| is the parent of
328   // the node that was removed, and |old_index| the index of the node that was
329   // removed.
330   views::Button* DetermineViewToThrobFromRemove(
331       const bookmarks::BookmarkNode* parent,
332       size_t old_index);
333 
334   // Sets/updates the colors and icons for all the child objects in the
335   // bookmarks bar.
336   void UpdateAppearanceForTheme();
337 
338   // Updates the visibility of |other_bookmarks_button_| and
339   // |managed_bookmarks_button_|. Also shows or hides the separator if required.
340   // Returns true if something changed and a LayoutAndPaint() is needed.
341   bool UpdateOtherAndManagedButtonsVisibility();
342 
343   // Updates the visibility of |bookmarks_separator_view_|.
344   void UpdateBookmarksSeparatorVisibility();
345 
346   // Updates the visibility of the apps shortcut based on the pref value.
347   void OnAppsPageShortcutVisibilityPrefChanged();
348 
349   void OnShowManagedBookmarksPrefChanged();
350 
LayoutAndPaint()351   void LayoutAndPaint() {
352     Layout();
353     SchedulePaint();
354   }
355 
356   // Inserts |button| in logical position |index| in the bar, maintaining
357   // correct focus traversal order.
358   void InsertBookmarkButtonAtIndex(std::unique_ptr<views::View> button,
359                                    size_t index);
360 
361   // Returns the model index for the bookmark associated with |button|,
362   // or size_t{-1} if |button| is not a bookmark button from this bar.
363   size_t GetIndexForButton(views::View* button);
364 
365   // Needed to react to kShowAppsShortcutInBookmarkBar changes.
366   PrefChangeRegistrar profile_pref_registrar_;
367 
368   // Used for opening urls.
369   content::PageNavigator* page_navigator_ = nullptr;
370 
371   // BookmarkModel that owns the entries and folders that are shown in this
372   // view. This is owned by the Profile.
373   bookmarks::BookmarkModel* model_ = nullptr;
374 
375   // ManagedBookmarkService. This is owned by the Profile.
376   bookmarks::ManagedBookmarkService* managed_ = nullptr;
377 
378   // Used to manage showing a Menu, either for the most recently bookmarked
379   // entries, or for the starred folder.
380   BookmarkMenuController* bookmark_menu_ = nullptr;
381 
382   // Used when showing a menu for drag and drop. That is, if the user drags
383   // over a folder this becomes non-null and manages the menu showing the
384   // contents of the node.
385   BookmarkMenuController* bookmark_drop_menu_ = nullptr;
386 
387   // If non-NULL we're showing a context menu for one of the items on the
388   // bookmark bar.
389   std::unique_ptr<BookmarkContextMenu> context_menu_;
390 
391   // Shows the "Other Bookmarks" folder button.
392   views::MenuButton* other_bookmarks_button_ = nullptr;
393 
394   // Shows the managed bookmarks entries.
395   views::MenuButton* managed_bookmarks_button_ = nullptr;
396 
397   // Shows the Apps page shortcut.
398   views::LabelButton* apps_page_shortcut_ = nullptr;
399 
400   // Used to track drops on the bookmark bar view.
401   std::unique_ptr<DropInfo> drop_info_;
402 
403   // Visible if not all the bookmark buttons fit.
404   views::MenuButton* overflow_button_ = nullptr;
405 
406   // The individual bookmark buttons.
407   std::vector<views::LabelButton*> bookmark_buttons_;
408 
409   ButtonSeparatorView* bookmarks_separator_view_ = nullptr;
410 
411   ReadLaterButton* read_later_button_ = nullptr;
412   ButtonSeparatorView* read_later_separator_view_ = nullptr;
413 
414   Browser* const browser_;
415   BrowserView* browser_view_;
416 
417   // True if the owning browser is showing an infobar.
418   bool infobar_visible_ = false;
419 
420   // Animation controlling showing and hiding of the bar.
421   gfx::SlideAnimation size_animation_{this};
422 
423   // If the bookmark bubble is showing, this is the visible ancestor of the URL.
424   // The visible ancestor is either the |other_bookmarks_button_|,
425   // |overflow_button_| or a button on the bar.
426   views::Button* throbbing_view_ = nullptr;
427 
428   BookmarkBar::State bookmark_bar_state_ = BookmarkBar::SHOW;
429 
430   base::ObserverList<BookmarkBarViewObserver>::Unchecked observers_;
431 
432   // Factory used to delay showing of the drop menu.
433   base::WeakPtrFactory<BookmarkBarView> show_folder_method_factory_{this};
434 
435   DISALLOW_COPY_AND_ASSIGN(BookmarkBarView);
436 };
437 
438 #endif  // CHROME_BROWSER_UI_VIEWS_BOOKMARKS_BOOKMARK_BAR_VIEW_H_
439