Home | History | Annotate | Download | only in bookmarks
      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;
     28 class BookmarkModel;
     29 class BookmarkNode;
     30 class Browser;
     31 class ChromeBookmarkClient;
     32 class GURL;
     33 namespace ui {
     34 class ThemeProvider;
     35 }
     36 
     37 namespace 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.
     43 const CGFloat kDefaultBookmarkWidth = 150.0;
     44 
     45 // Horizontal frame inset for buttons in the bookmark bar.
     46 const CGFloat kBookmarkHorizontalPadding = 1.0;
     47 
     48 // Vertical frame inset for buttons in the bookmark bar.
     49 const CGFloat kBookmarkVerticalPadding = 2.0;
     50 
     51 // Left margin before the first button in the bookmark bar.
     52 const CGFloat kBookmarkLeftMargin = 2.0;
     53 
     54 // Right margin before the last button in the bookmark bar.
     55 const CGFloat kBookmarkRightMargin = 2.0;
     56 
     57 // Used as a min/max width for buttons on menus (not on the bar).
     58 const CGFloat kBookmarkMenuButtonMinimumWidth = 100.0;
     59 const 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:]).
     65 const 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.
     72 const CGFloat kScrollViewContentWidthMargin = 2;
     73 
     74 // Make subfolder menus overlap their parent menu a bit to give a better
     75 // perception of a menuing system.
     76 const 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.
     82 const 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.
     86 const 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.
     92 const CGFloat kBookmarkBarButtonOffset = 2.0;
     93 
     94 // Delay before opening a subfolder (and closing the previous one)
     95 // when hovering over a folder button.
     96 const 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
    103 const 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.
    129 const 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
    148 willAnimateFromState:(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 - (BookmarkBarView*)buttonView;
    393 - (NSMutableArray*)buttons;
    394 - (NSButton*)offTheSideButton;
    395 - (NSButton*)appsPageShortcutButton;
    396 - (BOOL)offTheSideButtonIsHidden;
    397 - (BOOL)appsPageShortcutButtonIsHidden;
    398 - (BookmarkButton*)otherBookmarksButton;
    399 - (BookmarkBarFolderController*)folderController;
    400 - (id)folderTarget;
    401 - (int)displayedButtonCount;
    402 - (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition;
    403 - (void)clearBookmarkBar;
    404 - (BookmarkButtonCell*)cellForBookmarkNode:(const BookmarkNode*)node;
    405 - (BookmarkButtonCell*)cellForCustomButtonWithText:(NSString*)text
    406                                              image:(NSImage*)image;
    407 - (NSRect)frameForBookmarkButtonFromCell:(NSCell*)cell xOffset:(int*)xOffset;
    408 - (void)checkForBookmarkButtonGrowth:(NSButton*)button;
    409 - (void)frameDidChange;
    410 - (int64)nodeIdFromMenuTag:(int32)tag;
    411 - (int32)menuTagFromNodeId:(int64)menuid;
    412 - (void)updateTheme:(ui::ThemeProvider*)themeProvider;
    413 - (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point;
    414 - (BOOL)isEventAnExitEvent:(NSEvent*)event;
    415 - (BOOL)shrinkOrHideView:(NSView*)view forMaxX:(CGFloat)maxViewX;
    416 - (void)unhighlightBookmark:(const BookmarkNode*)node;
    417 
    418 // The following are for testing purposes only and are not used internally.
    419 - (NSMenu *)menuForFolderNode:(const BookmarkNode*)node;
    420 @end
    421 
    422 #endif  // CHROME_BROWSER_UI_COCOA_BOOKMARKS_BOOKMARK_BAR_CONTROLLER_H_
    423