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