bookmark_bar_controller.h revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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_COCOA_BOOKMARKS_BOOKMARK_BAR_CONTROLLER_H_
6#define CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_CONTROLLER_H_
7
8#import <Cocoa/Cocoa.h>
9#include <map>
10
11#import "base/mac/cocoa_protocols.h"
12#include "base/mac/scoped_nsobject.h"
13#include "base/memory/scoped_ptr.h"
14#include "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_bridge.h"
15#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h"
16#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_state.h"
17#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_toolbar_view.h"
18#import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h"
19#include "chrome/browser/ui/cocoa/tabs/tab_strip_model_observer_bridge.h"
20#include "ui/base/window_open_disposition.h"
21
22@class BookmarkBarController;
23@class BookmarkBarFolderController;
24@class BookmarkBarView;
25@class BookmarkButtonCell;
26@class BookmarkFolderTarget;
27@class BookmarkContextMenuCocoaController;
28class BookmarkModel;
29class BookmarkNode;
30class Browser;
31class ChromeBookmarkClient;
32class GURL;
33namespace ui {
34class ThemeProvider;
35}
36
37namespace bookmarks {
38
39// Magic numbers from Cole
40// TODO(jrg): create an objc-friendly version of bookmark_bar_constants.h?
41
42// Used as a maximum width for buttons on the bar.
43const CGFloat kDefaultBookmarkWidth = 150.0;
44
45// Horizontal frame inset for buttons in the bookmark bar.
46const CGFloat kBookmarkHorizontalPadding = 1.0;
47
48// Vertical frame inset for buttons in the bookmark bar.
49const CGFloat kBookmarkVerticalPadding = 2.0;
50
51// Left margin before the first button in the bookmark bar.
52const CGFloat kBookmarkLeftMargin = 2.0;
53
54// Right margin before the last button in the bookmark bar.
55const CGFloat kBookmarkRightMargin = 2.0;
56
57// Used as a min/max width for buttons on menus (not on the bar).
58const CGFloat kBookmarkMenuButtonMinimumWidth = 100.0;
59const CGFloat kBookmarkMenuButtonMaximumWidth = 485.0;
60
61// The minimum separation between a folder menu and the edge of the screen.
62// If the menu gets closer to the edge of the screen (either right or left)
63// then it is pops up in the opposite direction.
64// (See -[BookmarkBarFolderController childFolderWindowLeftForWidth:]).
65const CGFloat kBookmarkHorizontalScreenPadding = 8.0;
66
67// Our NSScrollView is supposed to be just barely big enough to fit its
68// contentView.  It is actually a hair too small.
69// This turns on horizontal scrolling which, although slight, is awkward.
70// Make sure our window (and NSScrollView) are wider than its documentView
71// by at least this much.
72const CGFloat kScrollViewContentWidthMargin = 2;
73
74// Make subfolder menus overlap their parent menu a bit to give a better
75// perception of a menuing system.
76const CGFloat kBookmarkMenuOverlap = 2.0;
77
78// When constraining a scrolling bookmark bar folder window to the
79// screen, shrink the "constrain" by this much vertically.  Currently
80// this is 0.0 to avoid a problem with tracking areas leaving the
81// window, but should probably be 8.0 or something.
82const CGFloat kScrollWindowVerticalMargin = 6.0;
83
84// How far to offset a folder menu from the top of the bookmark bar. This
85// is set just above the bar so that it become distinctive when drawn.
86const CGFloat kBookmarkBarMenuOffset = 2.0;
87
88// How far to offset a folder menu's left edge horizontally in relation to
89// the left edge of the button from which it springs. Because of drawing
90// differences, simply aligning the |frame| of each does not render the
91// pproper result, so we have to offset.
92const CGFloat kBookmarkBarButtonOffset = 2.0;
93
94// Delay before opening a subfolder (and closing the previous one)
95// when hovering over a folder button.
96const NSTimeInterval kHoverOpenDelay = 0.3;
97
98// Delay on hover before a submenu opens when dragging.
99// Experimentally a drag hover open delay needs to be bigger than a
100// normal (non-drag) menu hover open such as used in the bookmark folder.
101//  TODO(jrg): confirm feel of this constant with ui-team.
102//  http://crbug.com/36276
103const NSTimeInterval kDragHoverOpenDelay = 0.7;
104
105// Notes on use of kDragHoverCloseDelay in
106// -[BookmarkBarFolderController draggingEntered:].
107//
108// We have an implicit delay on stop-hover-open before a submenu
109// closes.  This cannot be zero since it's nice to move the mouse in a
110// direct line from "current position" to "position of item in
111// submenu".  However, by doing so, it's possible to overlap a
112// different button on the current menu.  Example:
113//
114//  Folder1
115//  Folder2  ---> Sub1
116//  Folder3       Sub2
117//                Sub3
118//
119// If you hover over the F in Folder2 to open the sub, and then want to
120// select Sub3, a direct line movement of the mouse may cross over
121// Folder3.  Without this delay, that'll cause Sub to be closed before
122// you get there, since a "hover over" of Folder3 gets activated.
123// It's subtle but without the delay it feels broken.
124//
125// This is only really a problem with vertical menu --> vertical menu
126// movement; the bookmark bar (horizontal menu, sort of) seems fine,
127// perhaps because mouse move direction is purely vertical so there is
128// no opportunity for overlap.
129const NSTimeInterval kDragHoverCloseDelay = 0.4;
130
131}  // namespace bookmarks
132
133// The interface for the bookmark bar controller's delegate. Currently, the
134// delegate is the BWC and is responsible for ensuring that the toolbar is
135// displayed correctly (as specified by |-getDesiredToolbarHeightCompression|
136// and |-toolbarDividerOpacity|) at the beginning and at the end of an animation
137// (or after a state change).
138@protocol BookmarkBarControllerDelegate
139
140// Sent when the state has changed (after any animation), but before the final
141// display update.
142- (void)bookmarkBar:(BookmarkBarController*)controller
143 didChangeFromState:(BookmarkBar::State)oldState
144            toState:(BookmarkBar::State)newState;
145
146// Sent before the animation begins.
147- (void)bookmarkBar:(BookmarkBarController*)controller
148willAnimateFromState:(BookmarkBar::State)oldState
149            toState:(BookmarkBar::State)newState;
150
151@end
152
153// A controller for the bookmark bar in the browser window. Handles showing
154// and hiding based on the preference in the given profile.
155@interface BookmarkBarController :
156    NSViewController<BookmarkBarState,
157                     BookmarkBarToolbarViewController,
158                     BookmarkButtonDelegate,
159                     BookmarkButtonControllerProtocol,
160                     NSDraggingDestination> {
161 @private
162  // The state of the bookmark bar. If an animation is running, this is set to
163  // the "destination" and |lastState_| is set to the "original" state.
164  BookmarkBar::State currentState_;
165
166  // The "original" state of the bookmark bar if an animation is running.
167  BookmarkBar::State lastState_;
168
169  // YES if an animation is running.
170  BOOL isAnimationRunning_;
171
172  Browser* browser_;              // weak; owned by its window
173  BookmarkModel* bookmarkModel_;  // weak; part of the profile owned by the
174                                  // top-level Browser object.
175  ChromeBookmarkClient* bookmarkClient_;
176
177  // Our initial view width, which is applied in awakeFromNib.
178  CGFloat initialWidth_;
179
180  // BookmarkNodes have a 64bit id.  NSMenuItems have a 32bit tag used
181  // to represent the bookmark node they refer to.  This map provides
182  // a mapping from one to the other, so we can properly identify the
183  // node from the item.  When adding items in, we start with seedId_.
184  int32 seedId_;
185  std::map<int32,int64> menuTagMap_;
186
187  // Our bookmark buttons, ordered from L-->R.
188  base::scoped_nsobject<NSMutableArray> buttons_;
189
190  // The folder image so we can use one copy for all buttons
191  base::scoped_nsobject<NSImage> folderImage_;
192
193  // The default image, so we can use one copy for all buttons.
194  base::scoped_nsobject<NSImage> defaultImage_;
195
196  // If the bar is disabled, we hide it and ignore show/hide commands.
197  // Set when using fullscreen mode.
198  BOOL barIsEnabled_;
199
200  // Bridge from Chrome-style C++ notifications (e.g. derived from
201  // BookmarkModelObserver)
202  scoped_ptr<BookmarkBarBridge> bridge_;
203
204  // Delegate that is informed about state changes in the bookmark bar.
205  id<BookmarkBarControllerDelegate> delegate_;  // weak
206
207  // Delegate that can resize us.
208  id<ViewResizer> resizeDelegate_;  // weak
209
210  // Logic for dealing with a click on a bookmark folder button.
211  base::scoped_nsobject<BookmarkFolderTarget> folderTarget_;
212
213  // A controller for a pop-up bookmark folder window (custom menu).
214  // This is not a scoped_nsobject because it owns itself (when its
215  // window closes the controller gets autoreleased).
216  BookmarkBarFolderController* folderController_;
217
218  // The event tap that allows monitoring of all events, to properly close with
219  // a click outside the bounds of the window.
220  id exitEventTap_;
221
222  IBOutlet BookmarkBarView* buttonView_;  // Contains 'no items' text fields.
223  IBOutlet BookmarkButton* offTheSideButton_;  // aka the chevron.
224
225  NSRect originalNoItemsRect_;  // Original, pre-resized field rect.
226  NSRect originalImportBookmarksRect_;  // Original, pre-resized field rect.
227
228  // "Apps" button on the left side.
229  base::scoped_nsobject<BookmarkButton> appsPageShortcutButton_;
230
231  // "Managed bookmarks" button on the left side, next to the apps button.
232  base::scoped_nsobject<BookmarkButton> managedBookmarksButton_;
233
234  // "Other bookmarks" button on the right side.
235  base::scoped_nsobject<BookmarkButton> otherBookmarksButton_;
236
237  // When doing a drag, this is folder button "hovered over" which we
238  // may want to open after a short delay.  There are cases where a
239  // mouse-enter can open a folder (e.g. if the menus are "active")
240  // but that doesn't use this variable or need a delay so "hover" is
241  // the wrong term.
242  base::scoped_nsobject<BookmarkButton> hoverButton_;
243
244  // We save the view width when we add bookmark buttons.  This lets
245  // us avoid a rebuild until we've grown the window bigger than our
246  // initial build.
247  CGFloat savedFrameWidth_;
248
249  // The number of buttons we display in the bookmark bar.  This does
250  // not include the "off the side" chevron or the "Other Bookmarks"
251  // button.  We use this number to determine if we need to display
252  // the chevron, and to know what to place in the chevron's menu.
253  // Since we create everything before doing layout we can't be sure
254  // that all bookmark buttons we create will be visible.  Thus,
255  // [buttons_ count] isn't a definitive check.
256  int displayedButtonCount_;
257
258  // A state flag which tracks when the bar's folder menus should be shown.
259  // An initial click in any of the folder buttons turns this on and
260  // one of the following will turn it off: another click in the button,
261  // the window losing focus, a click somewhere other than in the bar
262  // or a folder menu.
263  BOOL showFolderMenus_;
264
265  // If YES then state changes (for example, from hidden to shown) are animated.
266  // This is turned off for unit tests.
267  BOOL stateAnimationsEnabled_;
268
269  // If YES then changes inside the bookmark bar (for example, removing a
270  // bookmark) are animated. This is turned off for unit tests.
271  BOOL innerContentAnimationsEnabled_;
272
273  // YES if there is a possible drop about to happen in the bar.
274  BOOL hasInsertionPos_;
275
276  // The x point on the bar where the left edge of the new item will end
277  // up if it is dropped.
278  CGFloat insertionPos_;
279
280  // Controller responsible for all bookmark context menus.
281  base::scoped_nsobject<BookmarkContextMenuCocoaController>
282      contextMenuController_;
283}
284
285@property(readonly, nonatomic) BookmarkBar::State currentState;
286@property(readonly, nonatomic) BookmarkBar::State lastState;
287@property(readonly, nonatomic) BOOL isAnimationRunning;
288@property(assign, nonatomic) id<BookmarkBarControllerDelegate> delegate;
289@property(assign, nonatomic) BOOL stateAnimationsEnabled;
290@property(assign, nonatomic) BOOL innerContentAnimationsEnabled;
291
292// Initializes the bookmark bar controller with the given browser
293// profile and delegates.
294- (id)initWithBrowser:(Browser*)browser
295         initialWidth:(CGFloat)initialWidth
296             delegate:(id<BookmarkBarControllerDelegate>)delegate
297       resizeDelegate:(id<ViewResizer>)resizeDelegate;
298
299// The Browser corresponding to this BookmarkBarController.
300- (Browser*)browser;
301
302// The controller for all bookmark bar context menus.
303- (BookmarkContextMenuCocoaController*)menuController;
304
305// Updates the bookmark bar (from its current, possibly in-transition) state to
306// the new state.
307- (void)updateState:(BookmarkBar::State)newState
308         changeType:(BookmarkBar::AnimateChangeType)changeType;
309
310// Update the visible state of the bookmark bar.
311- (void)updateVisibility;
312
313// Update the visible state of the extra butons on the bookmark bar: the
314// apps shortcut and the managed bookmarks folder.
315- (void)updateExtraButtonsVisibility;
316
317// Hides or shows the bookmark bar depending on the current state.
318- (void)updateHiddenState;
319
320// Turn on or off the bookmark bar and prevent or reallow its appearance. On
321// disable, toggle off if shown. On enable, show only if needed. App and popup
322// windows do not show a bookmark bar.
323- (void)setBookmarkBarEnabled:(BOOL)enabled;
324
325// Returns the amount by which the toolbar above should be compressed.
326- (CGFloat)getDesiredToolbarHeightCompression;
327
328// Gets the appropriate opacity for the toolbar's divider; 0 means that it
329// shouldn't be shown.
330- (CGFloat)toolbarDividerOpacity;
331
332// Updates the sizes and positions of the subviews.
333// TODO(viettrungluu): I'm not convinced this should be public, but I currently
334// need it for animations. Try not to propagate its use.
335- (void)layoutSubviews;
336
337// Called by our view when it is moved to a window.
338- (void)viewDidMoveToWindow;
339
340// Provide a favicon for a bookmark node.  May return nil.
341- (NSImage*)faviconForNode:(const BookmarkNode*)node;
342
343// Used for situations where the bookmark bar folder menus should no longer
344// be actively popping up. Called when the window loses focus, a click has
345// occured outside the menus or a bookmark has been activated. (Note that this
346// differs from the behavior of the -[BookmarkButtonControllerProtocol
347// closeAllBookmarkFolders] method in that the latter does not terminate menu
348// tracking since it may be being called in response to actions (such as
349// dragging) where a 'stale' menu presentation should first be collapsed before
350// presenting a new menu.)
351- (void)closeFolderAndStopTrackingMenus;
352
353// Checks if operations such as edit or delete are allowed.
354- (BOOL)canEditBookmark:(const BookmarkNode*)node;
355
356// Checks if bookmark editing is enabled at all.
357- (BOOL)canEditBookmarks;
358
359// Actions for manipulating bookmarks.
360// Open a normal bookmark or folder from a button, ...
361- (IBAction)openBookmark:(id)sender;
362- (IBAction)openBookmarkFolderFromButton:(id)sender;
363// From the "off the side" button, ...
364- (IBAction)openOffTheSideFolderFromButton:(id)sender;
365// Import bookmarks from another browser.
366- (IBAction)importBookmarks:(id)sender;
367@end
368
369// Redirects from BookmarkBarBridge, the C++ object which glues us to
370// the rest of Chromium.  Internal to BookmarkBarController.
371@interface BookmarkBarController(BridgeRedirect)
372- (void)loaded:(BookmarkModel*)model;
373- (void)beingDeleted:(BookmarkModel*)model;
374- (void)nodeAdded:(BookmarkModel*)model
375           parent:(const BookmarkNode*)oldParent index:(int)index;
376- (void)nodeChanged:(BookmarkModel*)model
377               node:(const BookmarkNode*)node;
378- (void)nodeMoved:(BookmarkModel*)model
379        oldParent:(const BookmarkNode*)oldParent oldIndex:(int)oldIndex
380        newParent:(const BookmarkNode*)newParent newIndex:(int)newIndex;
381- (void)nodeRemoved:(BookmarkModel*)model
382             parent:(const BookmarkNode*)oldParent index:(int)index;
383- (void)nodeFaviconLoaded:(BookmarkModel*)model
384                     node:(const BookmarkNode*)node;
385- (void)nodeChildrenReordered:(BookmarkModel*)model
386                         node:(const BookmarkNode*)node;
387@end
388
389// These APIs should only be used by unit tests (or used internally).
390@interface BookmarkBarController(InternalOrTestingAPI)
391- (void)openBookmarkFolder:(id)sender;
392- (void)openOrCloseBookmarkFolderForOffTheSideButton;
393- (BookmarkBarView*)buttonView;
394- (NSMutableArray*)buttons;
395- (NSButton*)offTheSideButton;
396- (NSButton*)appsPageShortcutButton;
397- (BOOL)offTheSideButtonIsHidden;
398- (BOOL)appsPageShortcutButtonIsHidden;
399- (BookmarkButton*)otherBookmarksButton;
400- (BookmarkBarFolderController*)folderController;
401- (id)folderTarget;
402- (int)displayedButtonCount;
403- (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition;
404- (void)clearBookmarkBar;
405- (BookmarkButtonCell*)cellForBookmarkNode:(const BookmarkNode*)node;
406- (BookmarkButtonCell*)cellForCustomButtonWithText:(NSString*)text
407                                             image:(NSImage*)image;
408- (NSRect)frameForBookmarkButtonFromCell:(NSCell*)cell xOffset:(int*)xOffset;
409- (void)checkForBookmarkButtonGrowth:(NSButton*)button;
410- (void)frameDidChange;
411- (int64)nodeIdFromMenuTag:(int32)tag;
412- (int32)menuTagFromNodeId:(int64)menuid;
413- (void)updateTheme:(ui::ThemeProvider*)themeProvider;
414- (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point;
415- (BOOL)isEventAnExitEvent:(NSEvent*)event;
416- (BOOL)shrinkOrHideView:(NSView*)view forMaxX:(CGFloat)maxViewX;
417- (void)unhighlightBookmark:(const BookmarkNode*)node;
418
419// The following are for testing purposes only and are not used internally.
420- (NSMenu *)menuForFolderNode:(const BookmarkNode*)node;
421@end
422
423#endif  // CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_CONTROLLER_H_
424