Home | History | Annotate | Download | only in bookmarks
      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