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; 29class BookmarkModel; 30@class BookmarkMenu; 31class BookmarkNode; 32class Browser; 33class GURL; 34 35namespace 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. 41const CGFloat kDefaultBookmarkWidth = 150.0; 42 43// Horizontal frame inset for buttons in the bookmark bar. 44const CGFloat kBookmarkHorizontalPadding = 1.0; 45 46// Vertical frame inset for buttons in the bookmark bar. 47const CGFloat kBookmarkVerticalPadding = 2.0; 48 49// Used as a min/max width for buttons on menus (not on the bar). 50const CGFloat kBookmarkMenuButtonMinimumWidth = 100.0; 51const 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:]). 57const 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. 64const CGFloat kScrollViewContentWidthMargin = 2; 65 66// Make subfolder menus overlap their parent menu a bit to give a better 67// perception of a menuing system. 68const 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. 74const 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. 78const 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. 84const CGFloat kBookmarkBarButtonOffset = 2.0; 85 86// Delay before opening a subfolder (and closing the previous one) 87// when hovering over a folder button. 88const 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 95const 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. 121const 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 140willAnimateFromState:(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