summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authormrossetti@chromium.org <mrossetti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-20 20:19:43 +0000
committermrossetti@chromium.org <mrossetti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-20 20:19:43 +0000
commit8056a7541d58f0c1bf7e0c1c6954c2d027b93848 (patch)
tree66741623a28e68256bb21ee9c47a753f666b07cb /chrome
parent2c607924904d6801cbf50f290f2cf4dc0145bf8d (diff)
downloadchromium_src-8056a7541d58f0c1bf7e0c1c6954c2d027b93848.zip
chromium_src-8056a7541d58f0c1bf7e0c1c6954c2d027b93848.tar.gz
chromium_src-8056a7541d58f0c1bf7e0c1c6954c2d027b93848.tar.bz2
Simple code rearranging to better group by function area and protocol.
BUG=None TEST=None Review URL: http://codereview.chromium.org/2136020 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@47838 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/cocoa/bookmark_bar_controller.h33
-rw-r--r--chrome/browser/cocoa/bookmark_bar_controller.mm2324
-rw-r--r--chrome/browser/cocoa/bookmark_bar_folder_controller.mm827
3 files changed, 1601 insertions, 1583 deletions
diff --git a/chrome/browser/cocoa/bookmark_bar_controller.h b/chrome/browser/cocoa/bookmark_bar_controller.h
index 7efc2a6..51ebf6f 100644
--- a/chrome/browser/cocoa/bookmark_bar_controller.h
+++ b/chrome/browser/cocoa/bookmark_bar_controller.h
@@ -319,15 +319,15 @@ willAnimateFromState:(bookmarks::VisualState)oldState
@interface BookmarkBarController(BridgeRedirect)
- (void)loaded:(BookmarkModel*)model;
- (void)beingDeleted:(BookmarkModel*)model;
+- (void)nodeAdded:(BookmarkModel*)model
+ parent:(const BookmarkNode*)oldParent index:(int)index;
+- (void)nodeChanged:(BookmarkModel*)model
+ node:(const BookmarkNode*)node;
- (void)nodeMoved:(BookmarkModel*)model
oldParent:(const BookmarkNode*)oldParent oldIndex:(int)oldIndex
newParent:(const BookmarkNode*)newParent newIndex:(int)newIndex;
-- (void)nodeAdded:(BookmarkModel*)model
- parent:(const BookmarkNode*)oldParent index:(int)index;
- (void)nodeRemoved:(BookmarkModel*)model
parent:(const BookmarkNode*)oldParent index:(int)index;
-- (void)nodeChanged:(BookmarkModel*)model
- node:(const BookmarkNode*)node;
- (void)nodeFavIconLoaded:(BookmarkModel*)model
node:(const BookmarkNode*)node;
- (void)nodeChildrenReordered:(BookmarkModel*)model
@@ -336,29 +336,30 @@ willAnimateFromState:(bookmarks::VisualState)oldState
// These APIs should only be used by unit tests (or used internally).
@interface BookmarkBarController(InternalOrTestingAPI)
-- (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition;
-- (NSCell*)cellForBookmarkNode:(const BookmarkNode*)node;
-- (void)clearBookmarkBar;
- (BookmarkBarView*)buttonView;
- (NSMutableArray*)buttons;
+- (NSMenu*)offTheSideMenu;
+- (NSButton*)offTheSideButton;
+- (BOOL)offTheSideButtonIsHidden;
+- (NSButton*)otherBookmarksButton;
+- (BookmarkBarFolderController*)folderController;
+- (id)folderTarget;
+- (int)displayedButtonCount;
+- (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition;
+- (void)clearBookmarkBar;
+- (NSCell*)cellForBookmarkNode:(const BookmarkNode*)node;
- (NSRect)frameForBookmarkButtonFromCell:(NSCell*)cell xOffset:(int*)xOffset;
- (void)checkForBookmarkButtonGrowth:(NSButton*)button;
- (void)frameDidChange;
-- (BOOL)offTheSideButtonIsHidden;
-- (NSMenu *)menuForFolderNode:(const BookmarkNode*)node;
- (int64)nodeIdFromMenuTag:(int32)tag;
- (int32)menuTagFromNodeId:(int64)menuid;
-- (void)buildOffTheSideMenuIfNeeded;
-- (NSMenu*)offTheSideMenu;
-- (NSButton*)offTheSideButton;
-- (NSButton*)otherBookmarksButton;
- (const BookmarkNode*)nodeFromMenuItem:(id)sender;
- (void)updateTheme:(ThemeProvider*)themeProvider;
-- (BookmarkBarFolderController*)folderController;
- (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point;
- (BOOL)isEventAnExitEvent:(NSEvent*)event;
-- (id)folderTarget;
-- (int)displayedButtonCount;
+
+// The following are for testing purposes only and are not used internally.
+- (NSMenu *)menuForFolderNode:(const BookmarkNode*)node;
- (NSMenu*)buttonContextMenu;
- (void)setButtonContextMenu:(id)menu;
// Set to YES in order to prevent animations.
diff --git a/chrome/browser/cocoa/bookmark_bar_controller.mm b/chrome/browser/cocoa/bookmark_bar_controller.mm
index c585070..ed04eb0 100644
--- a/chrome/browser/cocoa/bookmark_bar_controller.mm
+++ b/chrome/browser/cocoa/bookmark_bar_controller.mm
@@ -267,37 +267,6 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12;
[super dealloc];
}
-// Adapt appearance of buttons to the current theme. Called after
-// theme changes, or when our view is added to the view hierarchy.
-// Oddly, the view pings us instead of us pinging our view. This is
-// because our trigger is an [NSView viewWillMoveToWindow:], which the
-// controller doesn't normally know about. Otherwise we don't have
-// access to the theme before we know what window we will be on.
-- (void)updateTheme:(ThemeProvider*)themeProvider {
- if (!themeProvider)
- return;
- NSColor* color =
- themeProvider->GetNSColor(BrowserThemeProvider::COLOR_BOOKMARK_TEXT,
- true);
- for (BookmarkButton* button in buttons_.get()) {
- BookmarkButtonCell* cell = [button cell];
- [cell setTextColor:color];
- }
- [[otherBookmarksButton_ cell] setTextColor:color];
-}
-
-// Exposed purely for testing.
-- (BookmarkBarFolderController*)folderController {
- return folderController_;
-}
-
-// Called after the current theme has changed.
-- (void)themeDidChangeNotification:(NSNotification*)aNotification {
- ThemeProvider* themeProvider =
- static_cast<ThemeProvider*>([[aNotification object] pointerValue]);
- [self updateTheme:themeProvider];
-}
-
- (void)awakeFromNib {
// We default to NOT open, which means height=0.
DCHECK([[self view] isHidden]); // Hidden so it's OK to change.
@@ -360,11 +329,312 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12;
object:[[self view] window]];
}
+// NSNotificationCenter callback.
+- (void)parentWindowWillClose:(NSNotification*)notification {
+ [self closeFolderAndStopTrackingMenus];
+}
+
+// NSNotificationCenter callback.
+- (void)parentWindowDidResignKey:(NSNotification*)notification {
+ [self closeFolderAndStopTrackingMenus];
+}
+
+// Change the layout of the bookmark bar's subviews in response to a visibility
+// change (e.g., show or hide the bar) or style change (attached or floating).
+- (void)layoutSubviews {
+ NSRect frame = [[self view] frame];
+ NSRect buttonViewFrame = NSMakeRect(0, 0, NSWidth(frame), NSHeight(frame));
+
+ // The state of our morph (if any); 1 is total bubble, 0 is the regular bar.
+ CGFloat morph = [self detachedMorphProgress];
+
+ // Add padding to the detached bookmark bar.
+ buttonViewFrame = NSInsetRect(buttonViewFrame,
+ morph * bookmarks::kNTPBookmarkBarPadding,
+ morph * bookmarks::kNTPBookmarkBarPadding);
+
+ [buttonView_ setFrame:buttonViewFrame];
+}
+
+// We don't change a preference; we only change visibility. Preference changing
+// (global state) is handled in |BrowserWindowCocoa::ToggleBookmarkBar()|. We
+// simply update based on what we're told.
+- (void)updateVisibility {
+ [self showBookmarkBarWithAnimation:NO];
+}
+
+- (void)setBookmarkBarEnabled:(BOOL)enabled {
+ if (enabled != barIsEnabled_) {
+ barIsEnabled_ = enabled;
+ [self updateVisibility];
+ }
+}
+
+- (CGFloat)getDesiredToolbarHeightCompression {
+ // Some special cases....
+ if (!barIsEnabled_)
+ return 0;
+
+ if ([self isAnimationRunning]) {
+ // No toolbar compression when animating between hidden and showing, nor
+ // between showing and detached.
+ if ([self isAnimatingBetweenState:bookmarks::kHiddenState
+ andState:bookmarks::kShowingState] ||
+ [self isAnimatingBetweenState:bookmarks::kShowingState
+ andState:bookmarks::kDetachedState])
+ return 0;
+
+ // If we ever need any other animation cases, code would go here.
+ }
+
+ return [self isInState:bookmarks::kShowingState] ? kBookmarkBarOverlap : 0;
+}
+
+- (CGFloat)toolbarDividerOpacity {
+ // Some special cases....
+ if ([self isAnimationRunning]) {
+ // In general, the toolbar shouldn't show a divider while we're animating
+ // between showing and hidden. The exception is when our height is < 1, in
+ // which case we can't draw it. It's all-or-nothing (no partial opacity).
+ if ([self isAnimatingBetweenState:bookmarks::kHiddenState
+ andState:bookmarks::kShowingState])
+ return (NSHeight([[self view] frame]) < 1) ? 1 : 0;
+
+ // The toolbar should show the divider when animating between showing and
+ // detached (but opacity will vary).
+ if ([self isAnimatingBetweenState:bookmarks::kShowingState
+ andState:bookmarks::kDetachedState])
+ return static_cast<CGFloat>([self detachedMorphProgress]);
+
+ // If we ever need any other animation cases, code would go here.
+ }
+
+ // In general, only show the divider when it's in the normal showing state.
+ return [self isInState:bookmarks::kShowingState] ? 0 : 1;
+}
+
+- (NSImage*)favIconForNode:(const BookmarkNode*)node {
+ if (!node)
+ return defaultImage_;
+
+ if (node->is_folder())
+ return folderImage_;
+
+ const SkBitmap& favIcon = bookmarkModel_->GetFavIcon(node);
+ if (!favIcon.isNull())
+ return gfx::SkBitmapToNSImage(favIcon);
+
+ return defaultImage_;
+}
+
+- (void)closeFolderAndStopTrackingMenus {
+ showFolderMenus_ = NO;
+ [self closeAllBookmarkFolders];
+}
+
+#pragma mark Actions
+
+- (IBAction)openBookmark:(id)sender {
+ [self closeFolderAndStopTrackingMenus];
+ DCHECK([sender respondsToSelector:@selector(bookmarkNode)]);
+ const BookmarkNode* node = [sender bookmarkNode];
+ WindowOpenDisposition disposition =
+ event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]);
+ [self openURL:node->GetURL() disposition:disposition];
+}
+
+// Redirect to our logic shared with BookmarkBarFolderController.
+- (IBAction)openBookmarkFolderFromButton:(id)sender {
+ // Toggle presentation of bar folder menus.
+ showFolderMenus_ = !showFolderMenus_;
+ [folderTarget_ openBookmarkFolderFromButton:sender];
+}
+
+// The button that sends this one is special; the "off the side"
+// button (chevron) opens like a folder button but isn't exactly a
+// parent folder.
+- (IBAction)openOffTheSideFolderFromButton:(id)sender {
+ DCHECK([sender isKindOfClass:[BookmarkButton class]]);
+ DCHECK([[sender cell] isKindOfClass:[BookmarkButtonCell class]]);
+ [[sender cell] setStartingChildIndex:displayedButtonCount_];
+ [folderTarget_ openBookmarkFolderFromButton:sender];
+}
+
+- (IBAction)openBookmarkInNewForegroundTab:(id)sender {
+ const BookmarkNode* node = [self nodeFromMenuItem:sender];
+ if (node)
+ [self openURL:node->GetURL() disposition:NEW_FOREGROUND_TAB];
+}
+
+- (IBAction)openBookmarkInNewWindow:(id)sender {
+ const BookmarkNode* node = [self nodeFromMenuItem:sender];
+ if (node)
+ [self openURL:node->GetURL() disposition:NEW_WINDOW];
+}
+
+- (IBAction)openBookmarkInIncognitoWindow:(id)sender {
+ const BookmarkNode* node = [self nodeFromMenuItem:sender];
+ if (node)
+ [self openURL:node->GetURL() disposition:OFF_THE_RECORD];
+}
+
+- (IBAction)editBookmark:(id)sender {
+ const BookmarkNode* node = [self nodeFromMenuItem:sender];
+ if (!node)
+ return;
+
+ if (node->is_folder()) {
+ BookmarkNameFolderController* controller =
+ [[BookmarkNameFolderController alloc]
+ initWithParentWindow:[[self view] window]
+ profile:browser_->profile()
+ node:node];
+ [controller runAsModalSheet];
+ return;
+ }
+
+ // There is no real need to jump to a platform-common routine at
+ // this point (which just jumps back to objc) other than consistency
+ // across platforms.
+ //
+ // TODO(jrg): identify when we NO_TREE. I can see it in the code
+ // for the other platforms but can't find a way to trigger it in the
+ // UI.
+ BookmarkEditor::Show([[self view] window],
+ browser_->profile(),
+ node->GetParent(),
+ BookmarkEditor::EditDetails(node),
+ BookmarkEditor::SHOW_TREE);
+}
+
+- (IBAction)cutBookmark:(id)sender {
+ const BookmarkNode* node = [self nodeFromMenuItem:sender];
+ if (node) {
+ std::vector<const BookmarkNode*> nodes;
+ nodes.push_back(node);
+ bookmark_utils::CopyToClipboard(bookmarkModel_, nodes, true);
+ }
+}
+
+- (IBAction)copyBookmark:(id)sender {
+ const BookmarkNode* node = [self nodeFromMenuItem:sender];
+ if (node) {
+ std::vector<const BookmarkNode*> nodes;
+ nodes.push_back(node);
+ bookmark_utils::CopyToClipboard(bookmarkModel_, nodes, false);
+ }
+}
+
+// Paste the copied node immediately after the node for which the context
+// menu has been presented if the node is a non-folder bookmark, otherwise
+// past at the end of the folder node.
+- (IBAction)pasteBookmark:(id)sender {
+ const BookmarkNode* node = [self nodeFromMenuItem:sender];
+ if (node) {
+ int index = -1;
+ if (node != bookmarkModel_->GetBookmarkBarNode() && !node->is_folder()) {
+ const BookmarkNode* parent = node->GetParent();
+ index = parent->IndexOfChild(node) + 1;
+ if (index > parent->GetChildCount())
+ index = -1;
+ node = parent;
+ }
+ bookmark_utils::PasteFromClipboard(bookmarkModel_, node, index);
+ }
+}
+
+- (IBAction)deleteBookmark:(id)sender {
+ const BookmarkNode* node = [self nodeFromMenuItem:sender];
+ if (node) {
+ bookmarkModel_->Remove(node->GetParent(),
+ node->GetParent()->IndexOfChild(node));
+ }
+}
+
+- (IBAction)openAllBookmarks:(id)sender {
+ const BookmarkNode* node = [self nodeFromMenuItem:sender];
+ if (node) {
+ [self openAll:node disposition:NEW_FOREGROUND_TAB];
+ UserMetrics::RecordAction(UserMetricsAction("OpenAllBookmarks"),
+ browser_->profile());
+ }
+}
+
+- (IBAction)openAllBookmarksNewWindow:(id)sender {
+ const BookmarkNode* node = [self nodeFromMenuItem:sender];
+ if (node) {
+ [self openAll:node disposition:NEW_WINDOW];
+ UserMetrics::RecordAction(UserMetricsAction("OpenAllBookmarksNewWindow"),
+ browser_->profile());
+ }
+}
+
+- (IBAction)openAllBookmarksIncognitoWindow:(id)sender {
+ const BookmarkNode* node = [self nodeFromMenuItem:sender];
+ if (node) {
+ [self openAll:node disposition:OFF_THE_RECORD];
+ UserMetrics::RecordAction(
+ UserMetricsAction("OpenAllBookmarksIncognitoWindow"),
+ browser_->profile());
+ }
+}
+
+// May be called from the bar or from a folder button.
+// If called from a button, that button becomes the parent.
+- (IBAction)addPage:(id)sender {
+ const BookmarkNode* parent = [self nodeFromMenuItem:sender];
+ if (!parent)
+ parent = bookmarkModel_->GetBookmarkBarNode();
+ BookmarkEditor::Show([[self view] window],
+ browser_->profile(),
+ parent,
+ BookmarkEditor::EditDetails(),
+ BookmarkEditor::SHOW_TREE);
+}
+
+// Might be called from the context menu over the bar OR over a
+// button. If called from a button, that button becomes a sibling of
+// the new node. If called from the bar, add to the end of the bar.
+- (IBAction)addFolder:(id)sender {
+ const BookmarkNode* senderNode = [self nodeFromMenuItem:sender];
+ const BookmarkNode* parent = NULL;
+ int newIndex = 0;
+ // If triggered from the bar, folder or "others" folder - add as a child to
+ // the end.
+ // If triggered from a bookmark, add as next sibling.
+ BookmarkNode::Type type = senderNode->type();
+ if (type == BookmarkNode::BOOKMARK_BAR ||
+ type == BookmarkNode::OTHER_NODE ||
+ type == BookmarkNode::FOLDER) {
+ parent = senderNode;
+ newIndex = parent->GetChildCount();
+ } else {
+ parent = senderNode->GetParent();
+ newIndex = parent->IndexOfChild(senderNode) + 1;
+ }
+ BookmarkNameFolderController* controller =
+ [[BookmarkNameFolderController alloc]
+ initWithParentWindow:[[self view] window]
+ profile:browser_->profile()
+ parent:parent
+ newIndex:newIndex];
+ [controller runAsModalSheet];
+}
+
- (IBAction)importBookmarks:(id)sender {
[ImportSettingsDialogController showImportSettingsDialogForProfile:
browser_->profile()];
}
+#pragma mark Private Methods
+
+// Called after the current theme has changed.
+- (void)themeDidChangeNotification:(NSNotification*)aNotification {
+ ThemeProvider* themeProvider =
+ static_cast<ThemeProvider*>([[aNotification object] pointerValue]);
+ [self updateTheme:themeProvider];
+}
+
// (Private) Method is the same as [self view], but is provided to be explicit.
- (BackgroundGradientView*)backgroundGradientView {
DCHECK([[self view] isKindOfClass:[BackgroundGradientView class]]);
@@ -408,87 +678,6 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12;
}
}
-- (BOOL)offTheSideButtonIsHidden {
- return [offTheSideButton_ isHidden];
-}
-
-// Called when our controlled frame has changed size.
-- (void)frameDidChange {
- if (!bookmarkModel_->IsLoaded())
- return;
- [self updateTheme:[[[self view] window] themeProvider]];
- [self reconfigureBookmarkBar];
-}
-
-// Close all bookmark folders. "Folder" here is the fake menu for
-// bookmark folders, not a button context menu.
-- (void)closeAllBookmarkFolders {
- [self watchForExitEvent:NO];
- [folderController_ close];
- folderController_ = nil;
-}
-
-- (void)closeBookmarkFolder:(id)sender {
- // We're the top level, so close one means close them all.
- [self closeAllBookmarkFolders];
-}
-
-- (void)closeFolderAndStopTrackingMenus {
- showFolderMenus_ = NO;
- [self closeAllBookmarkFolders];
-}
-
-- (BookmarkModel*)bookmarkModel {
- return bookmarkModel_;
-}
-
-// NSNotificationCenter callback.
-- (void)parentWindowWillClose:(NSNotification*)notification {
- [self closeFolderAndStopTrackingMenus];
-}
-
-// NSNotificationCenter callback.
-- (void)parentWindowDidResignKey:(NSNotification*)notification {
- [self closeFolderAndStopTrackingMenus];
-}
-
-// BookmarkButtonDelegate protocol implementation. When menus are
-// "active" (e.g. you clicked to open one), moving the mouse over
-// another folder button should close the 1st and open the 2nd (like
-// real menus). We detect and act here.
-- (void)mouseEnteredButton:(id)sender event:(NSEvent*)event {
- DCHECK([sender isKindOfClass:[BookmarkButton class]]);
-
- // If folder menus are not being shown, do nothing. This is different from
- // BookmarkBarFolderController's implementation because the bar should NOT
- // automatically open folder menus when the mouse passes over a folder
- // button while the BookmarkBarFolderController DOES automically open
- // a subfolder menu.
- if (!showFolderMenus_)
- return;
-
- // From here down: same logic as BookmarkBarFolderController.
- // TODO(jrg): find a way to share these 4 non-comment lines?
- // http://crbug.com/35966
- // If already opened, then we exited but re-entered the button, so do nothing.
- if ([folderController_ parentButton] == sender)
- return;
- // Else open a new one if it makes sense to do so.
- if ([sender bookmarkNode]->is_folder()) {
- [folderTarget_ openBookmarkFolderFromButton:sender];
- } else {
- // We're over a non-folder bookmark so close any old folders.
- [folderController_ close];
- folderController_ = nil;
- }
-}
-
-// BookmarkButtonDelegate protocol implementation.
-- (void)mouseExitedButton:(id)sender event:(NSEvent*)event {
- // Don't care; do nothing.
- // This is different behavior that the folder menus.
-}
-
// Begin (or end) watching for a click outside this window. Unlike
// normal NSWindows, bookmark folder "fake menu" windows do not become
// key or main. Thus, traditional notification (e.g. WillResignKey)
@@ -509,72 +698,6 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12;
watchingForExitEvent_ = watch;
}
-// Implementation of CrApplicationEventHookProtocol.
-// NOT an override of a standard Cocoa call made to NSViewControllers.
-- (void)hookForEvent:(NSEvent*)theEvent {
- if ([self isEventAnExitEvent:theEvent])
- [self closeFolderAndStopTrackingMenus];
-}
-
-// Return YES if the event indicates an exit from the bookmark bar
-// folder menus. E.g. "click outside" of the area we are watching.
-// At this time we are watching the area that includes all popup
-// bookmark folder windows.
-- (BOOL)isEventAnExitEvent:(NSEvent*)event {
- NSWindow* eventWindow = [event window];
- NSWindow* myWindow = [[self view] window];
- switch ([event type]) {
- case NSLeftMouseDown:
- case NSRightMouseDown:
- // If the click is in my window but NOT in the bookmark bar, consider
- // it a click 'outside'. Clicks directly on an active button (i.e. one
- // that is a folder and for which its folder menu is showing) are 'in'.
- // All other clicks on the bookmarks bar are counted as 'outside'
- // because they should close any open bookmark folder menu.
- if (eventWindow == myWindow) {
- NSView* hitView =
- [[eventWindow contentView] hitTest:[event locationInWindow]];
- if (hitView == [folderController_ parentButton])
- return NO;
- if (![hitView isDescendantOf:[self view]] || hitView == buttonView_)
- return YES;
- }
- // If a click in a bookmark bar folder window and that isn't
- // one of my bookmark bar folders, YES is click outside.
- if (![eventWindow isKindOfClass:[BookmarkBarFolderWindow
- class]]) {
- return YES;
- }
- break;
- case NSKeyDown:
- case NSKeyUp:
- // Any key press ends things.
- return YES;
- default:
- break;
- }
- return NO;
-}
-
-// Exposed for testing.
-- (id)folderTarget {
- return folderTarget_.get();
-}
-
-- (int)displayedButtonCount {
- return displayedButtonCount_;
-}
-
-- (NSMenu*)buttonContextMenu {
- return buttonContextMenu_;
-}
-
-// Intentionally ignores ownership issues; used for testing and we try
-// to minimize touching the object passed in (likely a mock).
-- (void)setButtonContextMenu:(id)menu {
- buttonContextMenu_ = menu;
-}
-
// Keep the "no items" label centered in response to a frame size change.
- (void)centerNoItemsLabel {
// Note that this computation is done in the parent's coordinate system,
@@ -587,23 +710,6 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12;
[[buttonView_ noItemContainer] setFrameOrigin:NSMakePoint(0, yoffset)];
}
-// Change the layout of the bookmark bar's subviews in response to a visibility
-// change (e.g., show or hide the bar) or style change (attached or floating).
-- (void)layoutSubviews {
- NSRect frame = [[self view] frame];
- NSRect buttonViewFrame = NSMakeRect(0, 0, NSWidth(frame), NSHeight(frame));
-
- // The state of our morph (if any); 1 is total bubble, 0 is the regular bar.
- CGFloat morph = [self detachedMorphProgress];
-
- // Add padding to the detached bookmark bar.
- buttonViewFrame = NSInsetRect(buttonViewFrame,
- morph * bookmarks::kNTPBookmarkBarPadding,
- morph * bookmarks::kNTPBookmarkBarPadding);
-
- [buttonView_ setFrame:buttonViewFrame];
-}
-
// (Private)
- (void)showBookmarkBarWithAnimation:(BOOL)animate {
if (animate && !ignoreAnimations_) {
@@ -674,432 +780,6 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12;
return YES;
}
-// We don't change a preference; we only change visibility. Preference changing
-// (global state) is handled in |BrowserWindowCocoa::ToggleBookmarkBar()|. We
-// simply update based on what we're told.
-- (void)updateVisibility {
- [self showBookmarkBarWithAnimation:NO];
-}
-
-- (void)setBookmarkBarEnabled:(BOOL)enabled {
- if (enabled != barIsEnabled_) {
- barIsEnabled_ = enabled;
- [self updateVisibility];
- }
-}
-
-- (CGFloat)getDesiredToolbarHeightCompression {
- // Some special cases....
- if (!barIsEnabled_)
- return 0;
-
- if ([self isAnimationRunning]) {
- // No toolbar compression when animating between hidden and showing, nor
- // between showing and detached.
- if ([self isAnimatingBetweenState:bookmarks::kHiddenState
- andState:bookmarks::kShowingState] ||
- [self isAnimatingBetweenState:bookmarks::kShowingState
- andState:bookmarks::kDetachedState])
- return 0;
-
- // If we ever need any other animation cases, code would go here.
- }
-
- return [self isInState:bookmarks::kShowingState] ? kBookmarkBarOverlap : 0;
-}
-
-- (CGFloat)toolbarDividerOpacity {
- // Some special cases....
- if ([self isAnimationRunning]) {
- // In general, the toolbar shouldn't show a divider while we're animating
- // between showing and hidden. The exception is when our height is < 1, in
- // which case we can't draw it. It's all-or-nothing (no partial opacity).
- if ([self isAnimatingBetweenState:bookmarks::kHiddenState
- andState:bookmarks::kShowingState])
- return (NSHeight([[self view] frame]) < 1) ? 1 : 0;
-
- // The toolbar should show the divider when animating between showing and
- // detached (but opacity will vary).
- if ([self isAnimatingBetweenState:bookmarks::kShowingState
- andState:bookmarks::kDetachedState])
- return static_cast<CGFloat>([self detachedMorphProgress]);
-
- // If we ever need any other animation cases, code would go here.
- }
-
- // In general, only show the divider when it's in the normal showing state.
- return [self isInState:bookmarks::kShowingState] ? 0 : 1;
-}
-
-// TODO(mrossetti): Duplicate code with BookmarkBarFolderController.
-// http://crbug.com/35966
-- (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point {
- DCHECK([urls count] == [titles count]);
- BOOL nodesWereAdded = NO;
- // Figure out where these new bookmarks nodes are to be added.
- BookmarkButton* button = [self buttonForDroppingOnAtPoint:point];
- const BookmarkNode* destParent = NULL;
- int destIndex = 0;
- if ([button isFolder]) {
- destParent = [button bookmarkNode];
- // Drop it at the end.
- destIndex = [button bookmarkNode]->GetChildCount();
- } else {
- // Else we're dropping somewhere on the bar, so find the right spot.
- destParent = bookmarkModel_->GetBookmarkBarNode();
- destIndex = [self indexForDragToPoint:point];
- }
-
- // Don't add the bookmarks if the destination index shows an error.
- if (destIndex >= 0) {
- // Create and add the new bookmark nodes.
- size_t urlCount = [urls count];
- for (size_t i = 0; i < urlCount; ++i) {
- // URLs come in several forms (see NSPasteboard+Utils.mm).
- GURL gurl;
- const char* string = [[urls objectAtIndex:i] UTF8String];
- if (string)
- gurl = GURL(string);
- if (!gurl.is_valid() && string) {
- gurl = GURL([[NSString stringWithFormat:@"file://%s", string]
- UTF8String]);
- }
- DCHECK(gurl.is_valid());
- if (gurl.is_valid()) {
- bookmarkModel_->AddURL(destParent,
- destIndex++,
- base::SysNSStringToWide([titles
- objectAtIndex:i]),
- gurl);
- nodesWereAdded = YES;
- }
- }
- }
- return nodesWereAdded;
-}
-
-- (int)indexForDragToPoint:(NSPoint)point {
- // TODO(jrg): revisit position info based on UI team feedback.
- // dropLocation is in bar local coordinates.
- NSPoint dropLocation =
- [[self view] convertPoint:point
- fromView:[[[self view] window] contentView]];
- BookmarkButton* buttonToTheRightOfDraggedButton = nil;
- for (BookmarkButton* button in buttons_.get()) {
- CGFloat midpoint = NSMidX([button frame]);
- if (dropLocation.x <= midpoint) {
- buttonToTheRightOfDraggedButton = button;
- break;
- }
- }
- if (buttonToTheRightOfDraggedButton) {
- const BookmarkNode* afterNode =
- [buttonToTheRightOfDraggedButton bookmarkNode];
- DCHECK(afterNode);
- int index = afterNode->GetParent()->IndexOfChild(afterNode);
- // Make sure we don't get confused by buttons which aren't visible.
- return std::min(index, displayedButtonCount_);
- }
-
- // If nothing is to my right I am at the end!
- return displayedButtonCount_;
-}
-
-// TODO(mrossetti,jrg): Yet more code dup with BookmarkBarFolderController.
-// http://crbug.com/35966
-- (BOOL)dragButton:(BookmarkButton*)sourceButton
- to:(NSPoint)point
- copy:(BOOL)copy {
- DCHECK([sourceButton isKindOfClass:[BookmarkButton class]]);
- const BookmarkNode* sourceNode = [sourceButton bookmarkNode];
- return [self dragBookmark:sourceNode to:point copy:copy];
-}
-
-// TODO(mrossetti,jrg): Yet more duplicated code.
-// http://crbug.com/35966
-- (BOOL)dragBookmark:(const BookmarkNode*)sourceNode
- to:(NSPoint)point
- copy:(BOOL)copy {
- DCHECK(sourceNode);
- // Drop destination.
- const BookmarkNode* destParent = NULL;
- int destIndex = 0;
-
- // First check if we're dropping on a button. If we have one, and
- // it's a folder, drop in it.
- BookmarkButton* button = [self buttonForDroppingOnAtPoint:point];
- if ([button isFolder]) {
- destParent = [button bookmarkNode];
- // Drop it at the end.
- destIndex = [button bookmarkNode]->GetChildCount();
- } else {
- // Else we're dropping somewhere on the bar, so find the right spot.
- destParent = bookmarkModel_->GetBookmarkBarNode();
- destIndex = [self indexForDragToPoint:point];
- }
-
- // Be sure we don't try and drop a folder into itself.
- if (sourceNode != destParent) {
- if (copy)
- bookmarkModel_->Copy(sourceNode, destParent, destIndex);
- else
- bookmarkModel_->Move(sourceNode, destParent, destIndex);
- }
-
- [self closeAllBookmarkFolders]; // For a hover open, if needed.
-
- // Movement of a node triggers observers (like us) to rebuild the
- // bar so we don't have to do so explicitly.
-
- return YES;
-}
-
-// Find something like std::is_between<T>? I can't believe one doesn't exist.
-static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
- return ((value >= low) && (value <= high));
-}
-
-// Return the proposed drop target for a hover open button from the
-// given array, or nil if none. We use this for distinguishing
-// between a hover-open candidate or drop-indicator draw.
-// Helper for buttonForDroppingOnAtPoint:.
-// Get UI review on "middle half" ness.
-// http://crbug.com/36276
-- (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point
- fromArray:(NSArray*)array {
- for (BookmarkButton* button in array) {
- // Break early if we've gone too far.
- if ((NSMinX([button frame]) > point.x) || (![button superview]))
- return nil;
- // Careful -- this only applies to the bar with horiz buttons.
- // Intentionally NOT using NSPointInRect() so that scrolling into
- // a submenu doesn't cause it to be closed.
- if (ValueInRangeInclusive(NSMinX([button frame]),
- point.x,
- NSMaxX([button frame]))) {
- // Over a button but let's be a little more specific (make sure
- // it's over the middle half, not just over it).
- NSRect frame = [button frame];
- NSRect middleHalfOfButton = NSInsetRect(frame, frame.size.width / 4, 0);
- if (ValueInRangeInclusive(NSMinX(middleHalfOfButton),
- point.x,
- NSMaxX(middleHalfOfButton))) {
- // It makes no sense to drop on a non-folder; there is no hover.
- if (![button isFolder])
- return nil;
- // Got it!
- return button;
- } else {
- // Over a button but not over the middle half.
- return nil;
- }
- }
- }
- // Not hovering over a button.
- return nil;
-}
-
-// Return the proposed drop target for a hover open button, or nil if
-// none. Works with both the bookmark buttons and the "Other
-// Bookmarks" button. Point is in [self view] coordinates.
-- (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point {
- point = [[self view] convertPoint:point
- fromView:[[[self view] window] contentView]];
- BookmarkButton* button = [self buttonForDroppingOnAtPoint:point
- fromArray:buttons_.get()];
- // One more chance -- try "Other Bookmarks" and "off the side" (if visible).
- // This is different than BookmarkBarFolderController.
- if (!button) {
- NSMutableArray* array = [NSMutableArray array];
- if (![self offTheSideButtonIsHidden])
- [array addObject:offTheSideButton_];
- [array addObject:otherBookmarksButton_];
- button = [self buttonForDroppingOnAtPoint:point
- fromArray:array];
- }
- return button;
-}
-
-// TODO(jrg): much of this logic is duped with
-// [BookmarkBarFolderController draggingEntered:] except when noted.
-// http://crbug.com/35966
-- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info {
- NSPoint point = [info draggingLocation];
- BookmarkButton* button = [self buttonForDroppingOnAtPoint:point];
-
- // Don't allow drops that would result in cycles.
- if (button) {
- NSData* data = [[info draggingPasteboard]
- dataForType:kBookmarkButtonDragType];
- if (data && [info draggingSource]) {
- BookmarkButton* sourceButton = nil;
- [data getBytes:&sourceButton length:sizeof(sourceButton)];
- const BookmarkNode* sourceNode = [sourceButton bookmarkNode];
- const BookmarkNode* destNode = [button bookmarkNode];
- if (destNode->HasAncestor(sourceNode))
- button = nil;
- }
- }
-
- if ([button isFolder]) {
- if (hoverButton_ == button) {
- return NSDragOperationMove; // already open or timed to open
- }
- if (hoverButton_) {
- // Oops, another one triggered or open.
- [NSObject cancelPreviousPerformRequestsWithTarget:[hoverButton_
- target]];
- // Unlike BookmarkBarFolderController, we do not delay the close
- // of the previous one. Given the lack of diagonal movement,
- // there is no need, and it feels awkward to do so. See
- // comments about kDragHoverCloseDelay in
- // bookmark_bar_folder_controller.mm for more details.
- [[hoverButton_ target] closeBookmarkFolder:hoverButton_];
- hoverButton_.reset();
- }
- hoverButton_.reset([button retain]);
- DCHECK([[hoverButton_ target]
- respondsToSelector:@selector(openBookmarkFolderFromButton:)]);
- [[hoverButton_ target]
- performSelector:@selector(openBookmarkFolderFromButton:)
- withObject:hoverButton_
- afterDelay:bookmarks::kDragHoverOpenDelay];
- }
- if (!button) {
- if (hoverButton_) {
- [NSObject cancelPreviousPerformRequestsWithTarget:[hoverButton_ target]];
- [[hoverButton_ target] closeBookmarkFolder:hoverButton_];
- hoverButton_.reset();
- }
- }
-
- // Thrown away but kept to be consistent with the draggingEntered: interface.
- return NSDragOperationMove;
-}
-
-- (void)draggingExited:(id<NSDraggingInfo>)info {
- // NOT the same as a cancel --> we may have moved the mouse into the submenu.
- if (hoverButton_) {
- [NSObject cancelPreviousPerformRequestsWithTarget:[hoverButton_ target]];
- hoverButton_.reset();
- }
-}
-
-- (void)draggingEnded:(id<NSDraggingInfo>)info {
- [self closeFolderAndStopTrackingMenus];
-}
-
-// Return YES if we should show the drop indicator, else NO.
-- (BOOL)shouldShowIndicatorShownForPoint:(NSPoint)point {
- return ![self buttonForDroppingOnAtPoint:point];
-}
-
-// Return the x position for a drop indicator.
-- (CGFloat)indicatorPosForDragToPoint:(NSPoint)point {
- CGFloat x = 0;
- int destIndex = [self indexForDragToPoint:point];
- int numButtons = displayedButtonCount_;
-
- // If it's a drop strictly between existing buttons ...
- if (destIndex >= 0 && destIndex < numButtons) {
- // ... put the indicator right between the buttons.
- BookmarkButton* button =
- [buttons_ objectAtIndex:static_cast<NSUInteger>(destIndex)];
- DCHECK(button);
- NSRect buttonFrame = [button frame];
- x = buttonFrame.origin.x - 0.5 * bookmarks::kBookmarkHorizontalPadding;
-
- // If it's a drop at the end (past the last button, if there are any) ...
- } else if (destIndex == numButtons) {
- // and if it's past the last button ...
- if (numButtons > 0) {
- // ... find the last button, and put the indicator to its right.
- BookmarkButton* button =
- [buttons_ objectAtIndex:static_cast<NSUInteger>(destIndex - 1)];
- DCHECK(button);
- NSRect buttonFrame = [button frame];
- x = NSMaxX(buttonFrame) + 0.5 * bookmarks::kBookmarkHorizontalPadding;
-
- // Otherwise, put it right at the beginning.
- } else {
- x = 0.5 * bookmarks::kBookmarkHorizontalPadding;
- }
- } else {
- NOTREACHED();
- }
-
- return x;
-}
-
-- (int)currentTabContentsHeight {
- return browser_->GetSelectedTabContents() ?
- browser_->GetSelectedTabContents()->view()->GetContainerSize().height() :
- 0;
-}
-
-- (ThemeProvider*)themeProvider {
- return browser_->profile()->GetThemeProvider();
-}
-
-- (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child {
- // If the bookmarkbar is not in detached mode, lock bar visibility, forcing
- // the overlay to stay open when in fullscreen mode.
- if (![self isInState:bookmarks::kDetachedState] &&
- ![self isAnimatingToState:bookmarks::kDetachedState]) {
- BrowserWindowController* browserController =
- [BrowserWindowController browserWindowControllerForView:[self view]];
- [browserController lockBarVisibilityForOwner:child
- withAnimation:NO
- delay:NO];
- }
-}
-
-- (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child {
- // Release bar visibility, allowing the overlay to close if in fullscreen
- // mode.
- BrowserWindowController* browserController =
- [BrowserWindowController browserWindowControllerForView:[self view]];
- [browserController releaseBarVisibilityForOwner:child
- withAnimation:NO
- delay:NO];
-}
-
-- (BOOL)dragShouldLockBarVisibility {
- return ![self isInState:bookmarks::kDetachedState] &&
- ![self isAnimatingToState:bookmarks::kDetachedState];
-}
-
-- (std::vector<const BookmarkNode*>)retrieveBookmarkDragDataNodes {
- std::vector<const BookmarkNode*> dragDataNodes;
- BookmarkDragData dragData;
- if(dragData.ReadFromDragClipboard()) {
- BookmarkModel* bookmarkModel = [self bookmarkModel];
- Profile* profile = bookmarkModel->profile();
- std::vector<const BookmarkNode*> nodes(dragData.GetNodes(profile));
- dragDataNodes.assign(nodes.begin(), nodes.end());
- }
- return dragDataNodes;
-}
-
-- (BOOL)dragBookmarkData:(id<NSDraggingInfo>)info {
- BOOL dragged = NO;
- std::vector<const BookmarkNode*> nodes([self retrieveBookmarkDragDataNodes]);
- if (nodes.size()) {
- BOOL copy = !([info draggingSourceOperationMask] & NSDragOperationMove);
- NSPoint dropPoint = [info draggingLocation];
- for (std::vector<const BookmarkNode*>::const_iterator it = nodes.begin();
- it != nodes.end(); ++it) {
- const BookmarkNode* sourceNode = *it;
- dragged = [self dragBookmark:sourceNode to:dropPoint copy:copy];
- }
- }
- return dragged;
-}
-
-- (NSWindow*)browserWindow {
- return [[self view] window];
-}
-
// Enable or disable items. We are the menu delegate for both the bar
// and for bookmark folder buttons.
- (BOOL)validateUserInterfaceItem:(id)item {
@@ -1172,27 +852,6 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
browser_->OpenURL(url, GURL(), disposition, PageTransition::AUTO_BOOKMARK);
}
-- (IBAction)openBookmark:(id)sender {
- [self closeFolderAndStopTrackingMenus];
- DCHECK([sender respondsToSelector:@selector(bookmarkNode)]);
- const BookmarkNode* node = [sender bookmarkNode];
- WindowOpenDisposition disposition =
- event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]);
- [self openURL:node->GetURL() disposition:disposition];
-}
-
-// Given a NSMenuItem tag, return the appropriate bookmark node id.
-- (int64)nodeIdFromMenuTag:(int32)tag {
- return menuTagMap_[tag];
-}
-
-// Create and return a new tag for the given node id.
-- (int32)menuTagFromNodeId:(int64)menuid {
- int tag = seedId_++;
- menuTagMap_[tag] = menuid;
- return tag;
-}
-
- (void)clearMenuTagMap {
seedId_ = 0;
menuTagMap_.clear();
@@ -1218,23 +877,6 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
}
}
-// Redirect to our logic shared with BookmarkBarFolderController.
-- (IBAction)openBookmarkFolderFromButton:(id)sender {
- // Toggle presentation of bar folder menus.
- showFolderMenus_ = !showFolderMenus_;
- [folderTarget_ openBookmarkFolderFromButton:sender];
-}
-
-// The button that sends this one is special; the "off the side"
-// button (chevron) opens like a folder button but isn't exactly a
-// parent folder.
-- (IBAction)openOffTheSideFolderFromButton:(id)sender {
- DCHECK([sender isKindOfClass:[BookmarkButton class]]);
- DCHECK([[sender cell] isKindOfClass:[BookmarkButtonCell class]]);
- [[sender cell] setStartingChildIndex:displayedButtonCount_];
- [folderTarget_ openBookmarkFolderFromButton:sender];
-}
-
// Recursively add the given bookmark node and all its children to
// menu, one menu item per node.
- (void)addNode:(const BookmarkNode*)child toMenu:(NSMenu*)menu {
@@ -1298,255 +940,6 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
return menu;
}
-// Add a new folder controller as triggered by the given folder button.
-- (void)addNewFolderControllerWithParentButton:(BookmarkButton*)parentButton {
- if (folderController_)
- [self closeAllBookmarkFolders];
-
- // Folder controller, like many window controllers, owns itself.
- folderController_ =
- [[BookmarkBarFolderController alloc] initWithParentButton:parentButton
- parentController:nil
- barController:self];
- [folderController_ showWindow:self];
-
- // Only BookmarkBarController has this; the
- // BookmarkBarFolderController does not.
- [self watchForExitEvent:YES];
-}
-
-// As a convention we set the menu's delegate to be the button's cell
-// so we can easily obtain bookmark info. Convention applied in
-// -[BookmarkButtonCell menu].
-
-- (IBAction)openBookmarkInNewForegroundTab:(id)sender {
- const BookmarkNode* node = [self nodeFromMenuItem:sender];
- if (node)
- [self openURL:node->GetURL() disposition:NEW_FOREGROUND_TAB];
-}
-
-- (IBAction)openBookmarkInNewWindow:(id)sender {
- const BookmarkNode* node = [self nodeFromMenuItem:sender];
- if (node)
- [self openURL:node->GetURL() disposition:NEW_WINDOW];
-}
-
-- (IBAction)openBookmarkInIncognitoWindow:(id)sender {
- const BookmarkNode* node = [self nodeFromMenuItem:sender];
- if (node)
- [self openURL:node->GetURL() disposition:OFF_THE_RECORD];
-}
-
-- (IBAction)editBookmark:(id)sender {
- const BookmarkNode* node = [self nodeFromMenuItem:sender];
- if (!node)
- return;
-
- if (node->is_folder()) {
- BookmarkNameFolderController* controller =
- [[BookmarkNameFolderController alloc]
- initWithParentWindow:[[self view] window]
- profile:browser_->profile()
- node:node];
- [controller runAsModalSheet];
- return;
- }
-
- // There is no real need to jump to a platform-common routine at
- // this point (which just jumps back to objc) other than consistency
- // across platforms.
- //
- // TODO(jrg): identify when we NO_TREE. I can see it in the code
- // for the other platforms but can't find a way to trigger it in the
- // UI.
- BookmarkEditor::Show([[self view] window],
- browser_->profile(),
- node->GetParent(),
- BookmarkEditor::EditDetails(node),
- BookmarkEditor::SHOW_TREE);
-}
-
-- (IBAction)cutBookmark:(id)sender {
- const BookmarkNode* node = [self nodeFromMenuItem:sender];
- if (node) {
- std::vector<const BookmarkNode*> nodes;
- nodes.push_back(node);
- bookmark_utils::CopyToClipboard(bookmarkModel_, nodes, true);
- }
-}
-
-- (IBAction)copyBookmark:(id)sender {
- const BookmarkNode* node = [self nodeFromMenuItem:sender];
- if (node) {
- std::vector<const BookmarkNode*> nodes;
- nodes.push_back(node);
- bookmark_utils::CopyToClipboard(bookmarkModel_, nodes, false);
- }
-}
-
-// Paste the copied node immediately after the node for which the context
-// menu has been presented if the node is a non-folder bookmark, otherwise
-// past at the end of the folder node.
-- (IBAction)pasteBookmark:(id)sender {
- const BookmarkNode* node = [self nodeFromMenuItem:sender];
- if (node) {
- int index = -1;
- if (node != bookmarkModel_->GetBookmarkBarNode() && !node->is_folder()) {
- const BookmarkNode* parent = node->GetParent();
- index = parent->IndexOfChild(node) + 1;
- if (index > parent->GetChildCount())
- index = -1;
- node = parent;
- }
- bookmark_utils::PasteFromClipboard(bookmarkModel_, node, index);
- }
-}
-
-- (IBAction)deleteBookmark:(id)sender {
- const BookmarkNode* node = [self nodeFromMenuItem:sender];
- if (node) {
- bookmarkModel_->Remove(node->GetParent(),
- node->GetParent()->IndexOfChild(node));
- }
-}
-
-// Return the BookmarkNode associated with the given NSMenuItem. Can
-// return NULL which means "do nothing". One case where it would
-// return NULL is if the bookmark model gets modified while you have a
-// context menu open.
-- (const BookmarkNode*)nodeFromMenuItem:(id)sender {
- const BookmarkNode* node = NULL;
- BookmarkMenu* menu = (BookmarkMenu*)[sender menu];
- if ([menu isKindOfClass:[BookmarkMenu class]]) {
- int64 id = [menu id];
- node = bookmarkModel_->GetNodeByID(id);
- }
- return node;
-}
-
-- (void)openAll:(const BookmarkNode*)node
- disposition:(WindowOpenDisposition)disposition {
- [self closeFolderAndStopTrackingMenus];
- bookmark_utils::OpenAll([[self view] window],
- browser_->profile(),
- browser_,
- node,
- disposition);
-}
-
-- (IBAction)openAllBookmarks:(id)sender {
- const BookmarkNode* node = [self nodeFromMenuItem:sender];
- if (node) {
- [self openAll:node disposition:NEW_FOREGROUND_TAB];
- UserMetrics::RecordAction(UserMetricsAction("OpenAllBookmarks"),
- browser_->profile());
- }
-}
-
-- (IBAction)openAllBookmarksNewWindow:(id)sender {
- const BookmarkNode* node = [self nodeFromMenuItem:sender];
- if (node) {
- [self openAll:node disposition:NEW_WINDOW];
- UserMetrics::RecordAction(UserMetricsAction("OpenAllBookmarksNewWindow"),
- browser_->profile());
- }
-}
-
-- (IBAction)openAllBookmarksIncognitoWindow:(id)sender {
- const BookmarkNode* node = [self nodeFromMenuItem:sender];
- if (node) {
- [self openAll:node disposition:OFF_THE_RECORD];
- UserMetrics::RecordAction(
- UserMetricsAction("OpenAllBookmarksIncognitoWindow"),
- browser_->profile());
- }
-}
-
-// May be called from the bar or from a folder button.
-// If called from a button, that button becomes the parent.
-- (IBAction)addPage:(id)sender {
- const BookmarkNode* parent = [self nodeFromMenuItem:sender];
- if (!parent)
- parent = bookmarkModel_->GetBookmarkBarNode();
- BookmarkEditor::Show([[self view] window],
- browser_->profile(),
- parent,
- BookmarkEditor::EditDetails(),
- BookmarkEditor::SHOW_TREE);
-}
-
-// Might be called from the context menu over the bar OR over a
-// button. If called from a button, that button becomes a sibling of
-// the new node. If called from the bar, add to the end of the bar.
-- (IBAction)addFolder:(id)sender {
- const BookmarkNode* senderNode = [self nodeFromMenuItem:sender];
- const BookmarkNode* parent = NULL;
- int newIndex = 0;
- // If triggered from the bar, folder or "others" folder - add as a child to
- // the end.
- // If triggered from a bookmark, add as next sibling.
- BookmarkNode::Type type = senderNode->type();
- if (type == BookmarkNode::BOOKMARK_BAR ||
- type == BookmarkNode::OTHER_NODE ||
- type == BookmarkNode::FOLDER) {
- parent = senderNode;
- newIndex = parent->GetChildCount();
- } else {
- parent = senderNode->GetParent();
- newIndex = parent->IndexOfChild(senderNode) + 1;
- }
- BookmarkNameFolderController* controller =
- [[BookmarkNameFolderController alloc]
- initWithParentWindow:[[self view] window]
- profile:browser_->profile()
- parent:parent
- newIndex:newIndex];
- [controller runAsModalSheet];
-}
-
-- (BookmarkBarView*)buttonView {
- return buttonView_;
-}
-
-// Delete all buttons (bookmarks, chevron, "other bookmarks") from the
-// bookmark bar; reset knowledge of bookmarks.
-- (void)clearBookmarkBar {
- [buttons_ makeObjectsPerformSelector:@selector(removeFromSuperview)];
- [buttons_ removeAllObjects];
- [self clearMenuTagMap];
- displayedButtonCount_ = 0;
-
- // Make sure there are no stale pointers in the pasteboard. This
- // can be important if a bookmark is deleted (via bookmark sync)
- // while in the middle of a drag. The "drag completed" code
- // (e.g. [BookmarkBarView performDragOperationForBookmarkButton:]) is
- // careful enough to bail if there is no data found at "drop" time.
- //
- // Unfortunately the clearContents selector is 10.6 only. The best
- // we can do is make sure something else is present in place of the
- // stale bookmark.
- NSPasteboard* pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
- [pboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self];
- [pboard setString:@"" forType:NSStringPboardType];
-}
-
-// Return an autoreleased NSCell suitable for a bookmark button.
-// TODO(jrg): move much of the cell config into the BookmarkButtonCell class.
-- (NSCell*)cellForBookmarkNode:(const BookmarkNode*)node {
- NSImage* image = node ? [self favIconForNode:node] : nil;
- NSMenu* menu = node && node->is_folder() ? buttonFolderContextMenu_ :
- buttonContextMenu_;
- BookmarkButtonCell* cell = [BookmarkButtonCell buttonCellForNode:node
- contextMenu:menu
- cellText:nil
- cellImage:image];
-
- // Note: a quirk of setting a cell's text color is that it won't work
- // until the cell is associated with a button, so we can't theme the cell yet.
-
- return cell;
-}
-
// Return an appropriate width for the given bookmark button cell.
// The "+2" is needed because, sometimes, Cocoa is off by a tad.
// Example: for a bookmark named "Moma" or "SFGate", it is one pixel
@@ -1557,53 +950,6 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
return std::min(desired, bookmarks::kDefaultBookmarkWidth);
}
-// Returns a frame appropriate for the given bookmark cell, suitable
-// for creating an NSButton that will contain it. |xOffset| is the X
-// offset for the frame; it is increased to be an appropriate X offset
-// for the next button.
-- (NSRect)frameForBookmarkButtonFromCell:(NSCell*)cell
- xOffset:(int*)xOffset {
- DCHECK(xOffset);
- NSRect bounds = [buttonView_ bounds];
- bounds.size.height = bookmarks::kBookmarkButtonHeight;
-
- NSRect frame = NSInsetRect(bounds,
- bookmarks::kBookmarkHorizontalPadding,
- bookmarks::kBookmarkVerticalPadding);
- frame.size.width = [self widthForBookmarkButtonCell:cell];
-
- // Add an X offset based on what we've already done
- frame.origin.x += *xOffset;
-
- // And up the X offset for next time.
- *xOffset = NSMaxX(frame);
-
- return frame;
-}
-
-// A bookmark button's contents changed. Check for growth
-// (e.g. increase the width up to the maximum). If we grew, move
-// other bookmark buttons over.
-- (void)checkForBookmarkButtonGrowth:(NSButton*)button {
- NSRect frame = [button frame];
- CGFloat desiredSize = [self widthForBookmarkButtonCell:[button cell]];
- CGFloat delta = desiredSize - frame.size.width;
- if (delta) {
- frame.size.width = desiredSize;
- [button setFrame:frame];
- for (NSButton* button in buttons_.get()) {
- NSRect buttonFrame = [button frame];
- if (buttonFrame.origin.x > frame.origin.x) {
- buttonFrame.origin.x += delta;
- [button setFrame:buttonFrame];
- }
- }
- }
- // We may have just crossed a threshold to enable the off-the-side
- // button.
- [self configureOffTheSideButtonContentsAndVisibility];
-}
-
- (IBAction)openBookmarkMenuItem:(id)sender {
int64 tag = [self nodeIdFromMenuTag:[sender tag]];
const BookmarkNode* node = bookmarkModel_->GetNodeByID(tag);
@@ -1737,33 +1083,6 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
[self positionOffTheSideButton];
}
-// TODO(jrg): for now this is brute force.
-- (void)loaded:(BookmarkModel*)model {
- DCHECK(model == bookmarkModel_);
- if (!model->IsLoaded())
- return;
-
- // If this is a rebuild request while we have a folder open, close it.
- // TODO(mrossetti): Eliminate the need for this because it causes the folder
- // menu to disappear after a cut/copy/paste/delete change.
- // See: http://crbug.com/36614
- if (folderController_)
- [self closeAllBookmarkFolders];
-
- // Brute force nuke and build.
- savedFrameWidth_ = NSWidth([[self view] frame]);
- const BookmarkNode* node = model->GetBookmarkBarNode();
- [self clearBookmarkBar];
- [self addNodesToButtonList:node];
- [self createOtherBookmarksButton];
- [self updateTheme:[[[self view] window] themeProvider]];
- [self positionOffTheSideButton];
- [self addNonBookmarkButtonsToView];
- [self addButtonsToView];
- [self configureOffTheSideButtonContentsAndVisibility];
- [self setNodeForBarMenu];
-}
-
// Now that the model is loaded, set the bookmark bar root as the node
// represented by the bookmark bar (default, background) menu.
- (void)setNodeForBarMenu {
@@ -1775,31 +1094,6 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
[menu setRepresentedObject:[NSNumber numberWithLongLong:node->id()]];
}
-- (void)beingDeleted:(BookmarkModel*)model {
- // The browser may be being torn down; little is safe to do. As an
- // example, it may not be safe to clear the pasteboard.
- // http://crbug.com/38665
-}
-
-- (void)nodeMoved:(BookmarkModel*)model
- oldParent:(const BookmarkNode*)oldParent oldIndex:(int)oldIndex
- newParent:(const BookmarkNode*)newParent newIndex:(int)newIndex {
- const BookmarkNode* movedNode = newParent->GetChild(newIndex);
- id<BookmarkButtonControllerProtocol> oldController =
- [self controllerForNode:oldParent];
- id<BookmarkButtonControllerProtocol> newController =
- [self controllerForNode:newParent];
- if (newController == oldController) {
- [oldController moveButtonFromIndex:oldIndex toIndex:newIndex];
- } else {
- [oldController removeButton:oldIndex animate:NO];
- [newController addButtonForNode:movedNode atIndex:newIndex];
- }
- // If we moved the only item on the "off the side" menu somewhere
- // else, we may no longer need to show it.
- [self configureOffTheSideButtonContentsAndVisibility];
-}
-
// To avoid problems with sync, changes that may impact the current
// bookmark (e.g. deletion) make sure context menus are closed. This
// prevents deleting a node which no longer exists.
@@ -1808,99 +1102,6 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
[buttonFolderContextMenu_ cancelTracking];
}
-- (void)nodeAdded:(BookmarkModel*)model
- parent:(const BookmarkNode*)newParent index:(int)newIndex {
- // If a context menu is open, close it.
- [self cancelMenuTracking];
-
- const BookmarkNode* newNode = newParent->GetChild(newIndex);
- id<BookmarkButtonControllerProtocol> newController =
- [self controllerForNode:newParent];
- [newController addButtonForNode:newNode atIndex:newIndex];
- // If we go from 0 --> 1 bookmarks we may need to hide the
- // "bookmarks go here" text container.
- [self showOrHideNoItemContainerForNode:model->GetBookmarkBarNode()];
-}
-
-- (void)nodeRemoved:(BookmarkModel*)model
- parent:(const BookmarkNode*)oldParent index:(int)index {
- // If a context menu is open, close it.
- [self cancelMenuTracking];
-
- // Locate the parent node. The parent may not be showing, in which case
- // we do nothing.
- id<BookmarkButtonControllerProtocol> parentController =
- [self controllerForNode:oldParent];
- [parentController removeButton:index animate:YES];
- // If we go from 1 --> 0 bookmarks we may need to show the
- // "bookmarks go here" text container.
- [self showOrHideNoItemContainerForNode:model->GetBookmarkBarNode()];
- // If we deleted the only item on the "off the side" menu we no
- // longer need to show it.
- [self configureOffTheSideButtonContentsAndVisibility];
-}
-
-// TODO(jrg): for now this is brute force.
-- (void)nodeChanged:(BookmarkModel*)model
- node:(const BookmarkNode*)node {
- [self loaded:model];
-}
-
-// TODO(jrg): linear searching is bad.
-// Need a BookmarkNode-->NSCell mapping.
-//
-// TODO(jrg): if the bookmark bar is open on launch, we see the
-// buttons all placed, then "scooted over" as the favicons load. If
-// this looks bad I may need to change widthForBookmarkButtonCell to
-// add space for an image even if not there on the assumption that
-// favicons will eventually load.
-- (void)nodeFavIconLoaded:(BookmarkModel*)model
- node:(const BookmarkNode*)node {
- for (BookmarkButton* button in buttons_.get()) {
- const BookmarkNode* cellnode = [button bookmarkNode];
- if (cellnode == node) {
- [[button cell] setBookmarkCellText:nil
- image:[self favIconForNode:node]];
- // Adding an image means we might need more room for the
- // bookmark. Test for it by growing the button (if needed)
- // and shifting everything else over.
- [self checkForBookmarkButtonGrowth:button];
- }
- }
-}
-
-// TODO(jrg): for now this is brute force.
-- (void)nodeChildrenReordered:(BookmarkModel*)model
- node:(const BookmarkNode*)node {
- [self loaded:model];
-}
-
-- (NSMutableArray*)buttons {
- return buttons_.get();
-}
-
-- (NSButton*)offTheSideButton {
- return offTheSideButton_;
-}
-
-- (NSButton*)otherBookmarksButton {
- return otherBookmarksButton_.get();
-}
-
-- (NSImage*)favIconForNode:(const BookmarkNode*)node {
- if (!node)
- return defaultImage_;
-
- if (node->is_folder())
- return folderImage_;
-
- const SkBitmap& favIcon = bookmarkModel_->GetFavIcon(node);
- if (!favIcon.isNull())
- return gfx::SkBitmapToNSImage(favIcon);
-
- return defaultImage_;
-}
-
// Determines the appropriate state for the given situation.
+ (bookmarks::VisualState)visualStateToShowNormalBar:(BOOL)showNormalBar
showDetachedBar:(BOOL)showDetachedBar {
@@ -2004,6 +1205,536 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
[self finalizeVisualState];
}
+- (void)reconfigureBookmarkBar {
+ [self redistributeButtonsOnBarAsNeeded];
+ [self positionOffTheSideButton];
+ [self configureOffTheSideButtonContentsAndVisibility];
+ [self centerNoItemsLabel];
+}
+
+- (void)redistributeButtonsOnBarAsNeeded {
+ const BookmarkNode* node = bookmarkModel_->GetBookmarkBarNode();
+ NSInteger barCount = node->GetChildCount();
+
+ // Determine the current maximum extent of the visible buttons.
+ CGFloat maxViewX = NSMaxX([[self view] bounds]);
+ NSButton* otherBookmarksButton = otherBookmarksButton_.get();
+ // If necessary, pull in the width to account for the Other Bookmarks button.
+ if (otherBookmarksButton_)
+ maxViewX = [otherBookmarksButton frame].origin.x -
+ bookmarks::kBookmarkHorizontalPadding;
+ // If we're already overflowing, then we need to account for the chevron.
+ if (barCount > displayedButtonCount_)
+ maxViewX = [offTheSideButton_ frame].origin.x -
+ bookmarks::kBookmarkHorizontalPadding;
+
+ // As a result of pasting or dragging, the bar may now have more buttons
+ // than will fit so remove any which overflow. They will be shown in
+ // the off-the-side folder.
+ while (displayedButtonCount_ > 0) {
+ BookmarkButton* button = [buttons_ lastObject];
+ if (NSMaxX([button frame]) < maxViewX)
+ break;
+ [buttons_ removeLastObject];
+ [button removeFromSuperview];
+ --displayedButtonCount_;
+ }
+
+ // As a result of cutting, deleting and dragging, the bar may now have room
+ // for more buttons.
+ int xOffset = displayedButtonCount_ > 0 ?
+ NSMaxX([[buttons_ lastObject] frame]) +
+ bookmarks::kBookmarkHorizontalPadding : 0;
+ for (int i = displayedButtonCount_; i < barCount; ++i) {
+ const BookmarkNode* child = node->GetChild(i);
+ BookmarkButton* button = [self buttonForNode:child xOffset:&xOffset];
+ // If we're testing against the last possible button then account
+ // for the chevron no longer needing to be shown.
+ if (i == barCount + 1)
+ maxViewX += NSWidth([offTheSideButton_ frame]) +
+ bookmarks::kBookmarkHorizontalPadding;
+ if (NSMaxX([button frame]) >= maxViewX)
+ break;
+ ++displayedButtonCount_;
+ [buttons_ addObject:button];
+ [buttonView_ addSubview:button];
+ }
+}
+
+#pragma mark Private Methods Exposed for Testing
+
+- (BookmarkBarView*)buttonView {
+ return buttonView_;
+}
+
+- (NSMutableArray*)buttons {
+ return buttons_.get();
+}
+
+- (NSButton*)offTheSideButton {
+ return offTheSideButton_;
+}
+
+- (BOOL)offTheSideButtonIsHidden {
+ return [offTheSideButton_ isHidden];
+}
+
+- (NSButton*)otherBookmarksButton {
+ return otherBookmarksButton_.get();
+}
+
+- (BookmarkBarFolderController*)folderController {
+ return folderController_;
+}
+
+- (id)folderTarget {
+ return folderTarget_.get();
+}
+
+- (int)displayedButtonCount {
+ return displayedButtonCount_;
+}
+
+// Delete all buttons (bookmarks, chevron, "other bookmarks") from the
+// bookmark bar; reset knowledge of bookmarks.
+- (void)clearBookmarkBar {
+ [buttons_ makeObjectsPerformSelector:@selector(removeFromSuperview)];
+ [buttons_ removeAllObjects];
+ [self clearMenuTagMap];
+ displayedButtonCount_ = 0;
+
+ // Make sure there are no stale pointers in the pasteboard. This
+ // can be important if a bookmark is deleted (via bookmark sync)
+ // while in the middle of a drag. The "drag completed" code
+ // (e.g. [BookmarkBarView performDragOperationForBookmarkButton:]) is
+ // careful enough to bail if there is no data found at "drop" time.
+ //
+ // Unfortunately the clearContents selector is 10.6 only. The best
+ // we can do is make sure something else is present in place of the
+ // stale bookmark.
+ NSPasteboard* pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
+ [pboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self];
+ [pboard setString:@"" forType:NSStringPboardType];
+}
+
+// Return an autoreleased NSCell suitable for a bookmark button.
+// TODO(jrg): move much of the cell config into the BookmarkButtonCell class.
+- (NSCell*)cellForBookmarkNode:(const BookmarkNode*)node {
+ NSImage* image = node ? [self favIconForNode:node] : nil;
+ NSMenu* menu = node && node->is_folder() ? buttonFolderContextMenu_ :
+ buttonContextMenu_;
+ BookmarkButtonCell* cell = [BookmarkButtonCell buttonCellForNode:node
+ contextMenu:menu
+ cellText:nil
+ cellImage:image];
+
+ // Note: a quirk of setting a cell's text color is that it won't work
+ // until the cell is associated with a button, so we can't theme the cell yet.
+
+ return cell;
+}
+
+// Returns a frame appropriate for the given bookmark cell, suitable
+// for creating an NSButton that will contain it. |xOffset| is the X
+// offset for the frame; it is increased to be an appropriate X offset
+// for the next button.
+- (NSRect)frameForBookmarkButtonFromCell:(NSCell*)cell
+ xOffset:(int*)xOffset {
+ DCHECK(xOffset);
+ NSRect bounds = [buttonView_ bounds];
+ bounds.size.height = bookmarks::kBookmarkButtonHeight;
+
+ NSRect frame = NSInsetRect(bounds,
+ bookmarks::kBookmarkHorizontalPadding,
+ bookmarks::kBookmarkVerticalPadding);
+ frame.size.width = [self widthForBookmarkButtonCell:cell];
+
+ // Add an X offset based on what we've already done
+ frame.origin.x += *xOffset;
+
+ // And up the X offset for next time.
+ *xOffset = NSMaxX(frame);
+
+ return frame;
+}
+
+// A bookmark button's contents changed. Check for growth
+// (e.g. increase the width up to the maximum). If we grew, move
+// other bookmark buttons over.
+- (void)checkForBookmarkButtonGrowth:(NSButton*)button {
+ NSRect frame = [button frame];
+ CGFloat desiredSize = [self widthForBookmarkButtonCell:[button cell]];
+ CGFloat delta = desiredSize - frame.size.width;
+ if (delta) {
+ frame.size.width = desiredSize;
+ [button setFrame:frame];
+ for (NSButton* button in buttons_.get()) {
+ NSRect buttonFrame = [button frame];
+ if (buttonFrame.origin.x > frame.origin.x) {
+ buttonFrame.origin.x += delta;
+ [button setFrame:buttonFrame];
+ }
+ }
+ }
+ // We may have just crossed a threshold to enable the off-the-side
+ // button.
+ [self configureOffTheSideButtonContentsAndVisibility];
+}
+
+// Called when our controlled frame has changed size.
+- (void)frameDidChange {
+ if (!bookmarkModel_->IsLoaded())
+ return;
+ [self updateTheme:[[[self view] window] themeProvider]];
+ [self reconfigureBookmarkBar];
+}
+
+// Given a NSMenuItem tag, return the appropriate bookmark node id.
+- (int64)nodeIdFromMenuTag:(int32)tag {
+ return menuTagMap_[tag];
+}
+
+// Create and return a new tag for the given node id.
+- (int32)menuTagFromNodeId:(int64)menuid {
+ int tag = seedId_++;
+ menuTagMap_[tag] = menuid;
+ return tag;
+}
+
+// Return the BookmarkNode associated with the given NSMenuItem. Can
+// return NULL which means "do nothing". One case where it would
+// return NULL is if the bookmark model gets modified while you have a
+// context menu open.
+- (const BookmarkNode*)nodeFromMenuItem:(id)sender {
+ const BookmarkNode* node = NULL;
+ BookmarkMenu* menu = (BookmarkMenu*)[sender menu];
+ if ([menu isKindOfClass:[BookmarkMenu class]]) {
+ int64 id = [menu id];
+ node = bookmarkModel_->GetNodeByID(id);
+ }
+ return node;
+}
+
+// Adapt appearance of buttons to the current theme. Called after
+// theme changes, or when our view is added to the view hierarchy.
+// Oddly, the view pings us instead of us pinging our view. This is
+// because our trigger is an [NSView viewWillMoveToWindow:], which the
+// controller doesn't normally know about. Otherwise we don't have
+// access to the theme before we know what window we will be on.
+- (void)updateTheme:(ThemeProvider*)themeProvider {
+ if (!themeProvider)
+ return;
+ NSColor* color =
+ themeProvider->GetNSColor(BrowserThemeProvider::COLOR_BOOKMARK_TEXT,
+ true);
+ for (BookmarkButton* button in buttons_.get()) {
+ BookmarkButtonCell* cell = [button cell];
+ [cell setTextColor:color];
+ }
+ [[otherBookmarksButton_ cell] setTextColor:color];
+}
+
+// Return YES if the event indicates an exit from the bookmark bar
+// folder menus. E.g. "click outside" of the area we are watching.
+// At this time we are watching the area that includes all popup
+// bookmark folder windows.
+- (BOOL)isEventAnExitEvent:(NSEvent*)event {
+ NSWindow* eventWindow = [event window];
+ NSWindow* myWindow = [[self view] window];
+ switch ([event type]) {
+ case NSLeftMouseDown:
+ case NSRightMouseDown:
+ // If the click is in my window but NOT in the bookmark bar, consider
+ // it a click 'outside'. Clicks directly on an active button (i.e. one
+ // that is a folder and for which its folder menu is showing) are 'in'.
+ // All other clicks on the bookmarks bar are counted as 'outside'
+ // because they should close any open bookmark folder menu.
+ if (eventWindow == myWindow) {
+ NSView* hitView =
+ [[eventWindow contentView] hitTest:[event locationInWindow]];
+ if (hitView == [folderController_ parentButton])
+ return NO;
+ if (![hitView isDescendantOf:[self view]] || hitView == buttonView_)
+ return YES;
+ }
+ // If a click in a bookmark bar folder window and that isn't
+ // one of my bookmark bar folders, YES is click outside.
+ if (![eventWindow isKindOfClass:[BookmarkBarFolderWindow
+ class]]) {
+ return YES;
+ }
+ break;
+ case NSKeyDown:
+ case NSKeyUp:
+ // Any key press ends things.
+ return YES;
+ default:
+ break;
+ }
+ return NO;
+}
+
+#pragma mark Drag & Drop
+
+// Find something like std::is_between<T>? I can't believe one doesn't exist.
+static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
+ return ((value >= low) && (value <= high));
+}
+
+// Return the proposed drop target for a hover open button from the
+// given array, or nil if none. We use this for distinguishing
+// between a hover-open candidate or drop-indicator draw.
+// Helper for buttonForDroppingOnAtPoint:.
+// Get UI review on "middle half" ness.
+// http://crbug.com/36276
+- (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point
+ fromArray:(NSArray*)array {
+ for (BookmarkButton* button in array) {
+ // Break early if we've gone too far.
+ if ((NSMinX([button frame]) > point.x) || (![button superview]))
+ return nil;
+ // Careful -- this only applies to the bar with horiz buttons.
+ // Intentionally NOT using NSPointInRect() so that scrolling into
+ // a submenu doesn't cause it to be closed.
+ if (ValueInRangeInclusive(NSMinX([button frame]),
+ point.x,
+ NSMaxX([button frame]))) {
+ // Over a button but let's be a little more specific (make sure
+ // it's over the middle half, not just over it).
+ NSRect frame = [button frame];
+ NSRect middleHalfOfButton = NSInsetRect(frame, frame.size.width / 4, 0);
+ if (ValueInRangeInclusive(NSMinX(middleHalfOfButton),
+ point.x,
+ NSMaxX(middleHalfOfButton))) {
+ // It makes no sense to drop on a non-folder; there is no hover.
+ if (![button isFolder])
+ return nil;
+ // Got it!
+ return button;
+ } else {
+ // Over a button but not over the middle half.
+ return nil;
+ }
+ }
+ }
+ // Not hovering over a button.
+ return nil;
+}
+
+// Return the proposed drop target for a hover open button, or nil if
+// none. Works with both the bookmark buttons and the "Other
+// Bookmarks" button. Point is in [self view] coordinates.
+- (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point {
+ point = [[self view] convertPoint:point
+ fromView:[[[self view] window] contentView]];
+ BookmarkButton* button = [self buttonForDroppingOnAtPoint:point
+ fromArray:buttons_.get()];
+ // One more chance -- try "Other Bookmarks" and "off the side" (if visible).
+ // This is different than BookmarkBarFolderController.
+ if (!button) {
+ NSMutableArray* array = [NSMutableArray array];
+ if (![self offTheSideButtonIsHidden])
+ [array addObject:offTheSideButton_];
+ [array addObject:otherBookmarksButton_];
+ button = [self buttonForDroppingOnAtPoint:point
+ fromArray:array];
+ }
+ return button;
+}
+
+- (int)indexForDragToPoint:(NSPoint)point {
+ // TODO(jrg): revisit position info based on UI team feedback.
+ // dropLocation is in bar local coordinates.
+ NSPoint dropLocation =
+ [[self view] convertPoint:point
+ fromView:[[[self view] window] contentView]];
+ BookmarkButton* buttonToTheRightOfDraggedButton = nil;
+ for (BookmarkButton* button in buttons_.get()) {
+ CGFloat midpoint = NSMidX([button frame]);
+ if (dropLocation.x <= midpoint) {
+ buttonToTheRightOfDraggedButton = button;
+ break;
+ }
+ }
+ if (buttonToTheRightOfDraggedButton) {
+ const BookmarkNode* afterNode =
+ [buttonToTheRightOfDraggedButton bookmarkNode];
+ DCHECK(afterNode);
+ int index = afterNode->GetParent()->IndexOfChild(afterNode);
+ // Make sure we don't get confused by buttons which aren't visible.
+ return std::min(index, displayedButtonCount_);
+ }
+
+ // If nothing is to my right I am at the end!
+ return displayedButtonCount_;
+}
+
+// TODO(mrossetti,jrg): Yet more duplicated code.
+// http://crbug.com/35966
+- (BOOL)dragBookmark:(const BookmarkNode*)sourceNode
+ to:(NSPoint)point
+ copy:(BOOL)copy {
+ DCHECK(sourceNode);
+ // Drop destination.
+ const BookmarkNode* destParent = NULL;
+ int destIndex = 0;
+
+ // First check if we're dropping on a button. If we have one, and
+ // it's a folder, drop in it.
+ BookmarkButton* button = [self buttonForDroppingOnAtPoint:point];
+ if ([button isFolder]) {
+ destParent = [button bookmarkNode];
+ // Drop it at the end.
+ destIndex = [button bookmarkNode]->GetChildCount();
+ } else {
+ // Else we're dropping somewhere on the bar, so find the right spot.
+ destParent = bookmarkModel_->GetBookmarkBarNode();
+ destIndex = [self indexForDragToPoint:point];
+ }
+
+ // Be sure we don't try and drop a folder into itself.
+ if (sourceNode != destParent) {
+ if (copy)
+ bookmarkModel_->Copy(sourceNode, destParent, destIndex);
+ else
+ bookmarkModel_->Move(sourceNode, destParent, destIndex);
+ }
+
+ [self closeAllBookmarkFolders]; // For a hover open, if needed.
+
+ // Movement of a node triggers observers (like us) to rebuild the
+ // bar so we don't have to do so explicitly.
+
+ return YES;
+}
+
+- (void)draggingEnded:(id<NSDraggingInfo>)info {
+ [self closeFolderAndStopTrackingMenus];
+}
+
+#pragma mark Bridge Notification Handlers
+
+// TODO(jrg): for now this is brute force.
+- (void)loaded:(BookmarkModel*)model {
+ DCHECK(model == bookmarkModel_);
+ if (!model->IsLoaded())
+ return;
+
+ // If this is a rebuild request while we have a folder open, close it.
+ // TODO(mrossetti): Eliminate the need for this because it causes the folder
+ // menu to disappear after a cut/copy/paste/delete change.
+ // See: http://crbug.com/36614
+ if (folderController_)
+ [self closeAllBookmarkFolders];
+
+ // Brute force nuke and build.
+ savedFrameWidth_ = NSWidth([[self view] frame]);
+ const BookmarkNode* node = model->GetBookmarkBarNode();
+ [self clearBookmarkBar];
+ [self addNodesToButtonList:node];
+ [self createOtherBookmarksButton];
+ [self updateTheme:[[[self view] window] themeProvider]];
+ [self positionOffTheSideButton];
+ [self addNonBookmarkButtonsToView];
+ [self addButtonsToView];
+ [self configureOffTheSideButtonContentsAndVisibility];
+ [self setNodeForBarMenu];
+}
+
+- (void)beingDeleted:(BookmarkModel*)model {
+ // The browser may be being torn down; little is safe to do. As an
+ // example, it may not be safe to clear the pasteboard.
+ // http://crbug.com/38665
+}
+
+- (void)nodeAdded:(BookmarkModel*)model
+ parent:(const BookmarkNode*)newParent index:(int)newIndex {
+ // If a context menu is open, close it.
+ [self cancelMenuTracking];
+
+ const BookmarkNode* newNode = newParent->GetChild(newIndex);
+ id<BookmarkButtonControllerProtocol> newController =
+ [self controllerForNode:newParent];
+ [newController addButtonForNode:newNode atIndex:newIndex];
+ // If we go from 0 --> 1 bookmarks we may need to hide the
+ // "bookmarks go here" text container.
+ [self showOrHideNoItemContainerForNode:model->GetBookmarkBarNode()];
+}
+
+// TODO(jrg): for now this is brute force.
+- (void)nodeChanged:(BookmarkModel*)model
+ node:(const BookmarkNode*)node {
+ [self loaded:model];
+}
+
+- (void)nodeMoved:(BookmarkModel*)model
+ oldParent:(const BookmarkNode*)oldParent oldIndex:(int)oldIndex
+ newParent:(const BookmarkNode*)newParent newIndex:(int)newIndex {
+ const BookmarkNode* movedNode = newParent->GetChild(newIndex);
+ id<BookmarkButtonControllerProtocol> oldController =
+ [self controllerForNode:oldParent];
+ id<BookmarkButtonControllerProtocol> newController =
+ [self controllerForNode:newParent];
+ if (newController == oldController) {
+ [oldController moveButtonFromIndex:oldIndex toIndex:newIndex];
+ } else {
+ [oldController removeButton:oldIndex animate:NO];
+ [newController addButtonForNode:movedNode atIndex:newIndex];
+ }
+ // If we moved the only item on the "off the side" menu somewhere
+ // else, we may no longer need to show it.
+ [self configureOffTheSideButtonContentsAndVisibility];
+}
+
+- (void)nodeRemoved:(BookmarkModel*)model
+ parent:(const BookmarkNode*)oldParent index:(int)index {
+ // If a context menu is open, close it.
+ [self cancelMenuTracking];
+
+ // Locate the parent node. The parent may not be showing, in which case
+ // we do nothing.
+ id<BookmarkButtonControllerProtocol> parentController =
+ [self controllerForNode:oldParent];
+ [parentController removeButton:index animate:YES];
+ // If we go from 1 --> 0 bookmarks we may need to show the
+ // "bookmarks go here" text container.
+ [self showOrHideNoItemContainerForNode:model->GetBookmarkBarNode()];
+ // If we deleted the only item on the "off the side" menu we no
+ // longer need to show it.
+ [self configureOffTheSideButtonContentsAndVisibility];
+}
+
+// TODO(jrg): linear searching is bad.
+// Need a BookmarkNode-->NSCell mapping.
+//
+// TODO(jrg): if the bookmark bar is open on launch, we see the
+// buttons all placed, then "scooted over" as the favicons load. If
+// this looks bad I may need to change widthForBookmarkButtonCell to
+// add space for an image even if not there on the assumption that
+// favicons will eventually load.
+- (void)nodeFavIconLoaded:(BookmarkModel*)model
+ node:(const BookmarkNode*)node {
+ for (BookmarkButton* button in buttons_.get()) {
+ const BookmarkNode* cellnode = [button bookmarkNode];
+ if (cellnode == node) {
+ [[button cell] setBookmarkCellText:nil
+ image:[self favIconForNode:node]];
+ // Adding an image means we might need more room for the
+ // bookmark. Test for it by growing the button (if needed)
+ // and shifting everything else over.
+ [self checkForBookmarkButtonGrowth:button];
+ }
+ }
+}
+
+// TODO(jrg): for now this is brute force.
+- (void)nodeChildrenReordered:(BookmarkModel*)model
+ node:(const BookmarkNode*)node {
+ [self loaded:model];
+}
+
+#pragma mark BookmarkBarState Protocol
+
// (BookmarkBarState protocol)
- (BOOL)isVisible {
return barIsEnabled_ && (visualState_ == bookmarks::kShowingState ||
@@ -2063,78 +1794,283 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
return 0;
}
-// (BookmarkButtonDelegate protocol)
+#pragma mark BookmarkBarToolbarViewController Protocol
+
+- (int)currentTabContentsHeight {
+ return browser_->GetSelectedTabContents() ?
+ browser_->GetSelectedTabContents()->view()->GetContainerSize().height() :
+ 0;
+}
+
+- (ThemeProvider*)themeProvider {
+ return browser_->profile()->GetThemeProvider();
+}
+
+#pragma mark BookmarkButtonDelegate Protocol
+
- (void)fillPasteboard:(NSPasteboard*)pboard
forDragOfButton:(BookmarkButton*)button {
[[self folderTarget] fillPasteboard:pboard forDragOfButton:button];
}
-- (id<BookmarkButtonControllerProtocol>)controllerForNode:
- (const BookmarkNode*)node {
- // See if it's in the bar, then if it is in the hierarchy of visible
- // folder menus.
- if (bookmarkModel_->GetBookmarkBarNode() == node)
- return self;
- return [folderController_ controllerForNode:node];
+// BookmarkButtonDelegate protocol implementation. When menus are
+// "active" (e.g. you clicked to open one), moving the mouse over
+// another folder button should close the 1st and open the 2nd (like
+// real menus). We detect and act here.
+- (void)mouseEnteredButton:(id)sender event:(NSEvent*)event {
+ DCHECK([sender isKindOfClass:[BookmarkButton class]]);
+
+ // If folder menus are not being shown, do nothing. This is different from
+ // BookmarkBarFolderController's implementation because the bar should NOT
+ // automatically open folder menus when the mouse passes over a folder
+ // button while the BookmarkBarFolderController DOES automically open
+ // a subfolder menu.
+ if (!showFolderMenus_)
+ return;
+
+ // From here down: same logic as BookmarkBarFolderController.
+ // TODO(jrg): find a way to share these 4 non-comment lines?
+ // http://crbug.com/35966
+ // If already opened, then we exited but re-entered the button, so do nothing.
+ if ([folderController_ parentButton] == sender)
+ return;
+ // Else open a new one if it makes sense to do so.
+ if ([sender bookmarkNode]->is_folder()) {
+ [folderTarget_ openBookmarkFolderFromButton:sender];
+ } else {
+ // We're over a non-folder bookmark so close any old folders.
+ [folderController_ close];
+ folderController_ = nil;
+ }
}
-- (void)reconfigureBookmarkBar {
- [self redistributeButtonsOnBarAsNeeded];
- [self positionOffTheSideButton];
- [self configureOffTheSideButtonContentsAndVisibility];
- [self centerNoItemsLabel];
+// BookmarkButtonDelegate protocol implementation.
+- (void)mouseExitedButton:(id)sender event:(NSEvent*)event {
+ // Don't care; do nothing.
+ // This is different behavior that the folder menus.
}
-- (void)redistributeButtonsOnBarAsNeeded {
- const BookmarkNode* node = bookmarkModel_->GetBookmarkBarNode();
- NSInteger barCount = node->GetChildCount();
+- (NSWindow*)browserWindow {
+ return [[self view] window];
+}
- // Determine the current maximum extent of the visible buttons.
- CGFloat maxViewX = NSMaxX([[self view] bounds]);
- NSButton* otherBookmarksButton = otherBookmarksButton_.get();
- // If necessary, pull in the width to account for the Other Bookmarks button.
- if (otherBookmarksButton_)
- maxViewX = [otherBookmarksButton frame].origin.x -
- bookmarks::kBookmarkHorizontalPadding;
- // If we're already overflowing, then we need to account for the chevron.
- if (barCount > displayedButtonCount_)
- maxViewX = [offTheSideButton_ frame].origin.x -
- bookmarks::kBookmarkHorizontalPadding;
+#pragma mark BookmarkButtonControllerProtocol
- // As a result of pasting or dragging, the bar may now have more buttons
- // than will fit so remove any which overflow. They will be shown in
- // the off-the-side folder.
- while (displayedButtonCount_ > 0) {
- BookmarkButton* button = [buttons_ lastObject];
- if (NSMaxX([button frame]) < maxViewX)
- break;
- [buttons_ removeLastObject];
- [button removeFromSuperview];
- --displayedButtonCount_;
+// Close all bookmark folders. "Folder" here is the fake menu for
+// bookmark folders, not a button context menu.
+- (void)closeAllBookmarkFolders {
+ [self watchForExitEvent:NO];
+ [folderController_ close];
+ folderController_ = nil;
+}
+
+- (void)closeBookmarkFolder:(id)sender {
+ // We're the top level, so close one means close them all.
+ [self closeAllBookmarkFolders];
+}
+
+- (BookmarkModel*)bookmarkModel {
+ return bookmarkModel_;
+}
+
+// TODO(jrg): much of this logic is duped with
+// [BookmarkBarFolderController draggingEntered:] except when noted.
+// http://crbug.com/35966
+- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info {
+ NSPoint point = [info draggingLocation];
+ BookmarkButton* button = [self buttonForDroppingOnAtPoint:point];
+
+ // Don't allow drops that would result in cycles.
+ if (button) {
+ NSData* data = [[info draggingPasteboard]
+ dataForType:kBookmarkButtonDragType];
+ if (data && [info draggingSource]) {
+ BookmarkButton* sourceButton = nil;
+ [data getBytes:&sourceButton length:sizeof(sourceButton)];
+ const BookmarkNode* sourceNode = [sourceButton bookmarkNode];
+ const BookmarkNode* destNode = [button bookmarkNode];
+ if (destNode->HasAncestor(sourceNode))
+ button = nil;
+ }
}
- // As a result of cutting, deleting and dragging, the bar may now have room
- // for more buttons.
- int xOffset = displayedButtonCount_ > 0 ?
- NSMaxX([[buttons_ lastObject] frame]) +
- bookmarks::kBookmarkHorizontalPadding : 0;
- for (int i = displayedButtonCount_; i < barCount; ++i) {
- const BookmarkNode* child = node->GetChild(i);
- BookmarkButton* button = [self buttonForNode:child xOffset:&xOffset];
- // If we're testing against the last possible button then account
- // for the chevron no longer needing to be shown.
- if (i == barCount + 1)
- maxViewX += NSWidth([offTheSideButton_ frame]) +
- bookmarks::kBookmarkHorizontalPadding;
- if (NSMaxX([button frame]) >= maxViewX)
- break;
- ++displayedButtonCount_;
- [buttons_ addObject:button];
- [buttonView_ addSubview:button];
+ if ([button isFolder]) {
+ if (hoverButton_ == button) {
+ return NSDragOperationMove; // already open or timed to open
+ }
+ if (hoverButton_) {
+ // Oops, another one triggered or open.
+ [NSObject cancelPreviousPerformRequestsWithTarget:[hoverButton_
+ target]];
+ // Unlike BookmarkBarFolderController, we do not delay the close
+ // of the previous one. Given the lack of diagonal movement,
+ // there is no need, and it feels awkward to do so. See
+ // comments about kDragHoverCloseDelay in
+ // bookmark_bar_folder_controller.mm for more details.
+ [[hoverButton_ target] closeBookmarkFolder:hoverButton_];
+ hoverButton_.reset();
+ }
+ hoverButton_.reset([button retain]);
+ DCHECK([[hoverButton_ target]
+ respondsToSelector:@selector(openBookmarkFolderFromButton:)]);
+ [[hoverButton_ target]
+ performSelector:@selector(openBookmarkFolderFromButton:)
+ withObject:hoverButton_
+ afterDelay:bookmarks::kDragHoverOpenDelay];
+ }
+ if (!button) {
+ if (hoverButton_) {
+ [NSObject cancelPreviousPerformRequestsWithTarget:[hoverButton_ target]];
+ [[hoverButton_ target] closeBookmarkFolder:hoverButton_];
+ hoverButton_.reset();
+ }
+ }
+
+ // Thrown away but kept to be consistent with the draggingEntered: interface.
+ return NSDragOperationMove;
+}
+
+- (void)draggingExited:(id<NSDraggingInfo>)info {
+ // NOT the same as a cancel --> we may have moved the mouse into the submenu.
+ if (hoverButton_) {
+ [NSObject cancelPreviousPerformRequestsWithTarget:[hoverButton_ target]];
+ hoverButton_.reset();
}
}
-#pragma mark BookmarkButtonControllerProtocol
+- (BOOL)dragShouldLockBarVisibility {
+ return ![self isInState:bookmarks::kDetachedState] &&
+ ![self isAnimatingToState:bookmarks::kDetachedState];
+}
+
+// TODO(mrossetti,jrg): Yet more code dup with BookmarkBarFolderController.
+// http://crbug.com/35966
+- (BOOL)dragButton:(BookmarkButton*)sourceButton
+ to:(NSPoint)point
+ copy:(BOOL)copy {
+ DCHECK([sourceButton isKindOfClass:[BookmarkButton class]]);
+ const BookmarkNode* sourceNode = [sourceButton bookmarkNode];
+ return [self dragBookmark:sourceNode to:point copy:copy];
+}
+
+- (BOOL)dragBookmarkData:(id<NSDraggingInfo>)info {
+ BOOL dragged = NO;
+ std::vector<const BookmarkNode*> nodes([self retrieveBookmarkDragDataNodes]);
+ if (nodes.size()) {
+ BOOL copy = !([info draggingSourceOperationMask] & NSDragOperationMove);
+ NSPoint dropPoint = [info draggingLocation];
+ for (std::vector<const BookmarkNode*>::const_iterator it = nodes.begin();
+ it != nodes.end(); ++it) {
+ const BookmarkNode* sourceNode = *it;
+ dragged = [self dragBookmark:sourceNode to:dropPoint copy:copy];
+ }
+ }
+ return dragged;
+}
+
+- (std::vector<const BookmarkNode*>)retrieveBookmarkDragDataNodes {
+ std::vector<const BookmarkNode*> dragDataNodes;
+ BookmarkDragData dragData;
+ if(dragData.ReadFromDragClipboard()) {
+ BookmarkModel* bookmarkModel = [self bookmarkModel];
+ Profile* profile = bookmarkModel->profile();
+ std::vector<const BookmarkNode*> nodes(dragData.GetNodes(profile));
+ dragDataNodes.assign(nodes.begin(), nodes.end());
+ }
+ return dragDataNodes;
+}
+
+// Return YES if we should show the drop indicator, else NO.
+- (BOOL)shouldShowIndicatorShownForPoint:(NSPoint)point {
+ return ![self buttonForDroppingOnAtPoint:point];
+}
+
+// Return the x position for a drop indicator.
+- (CGFloat)indicatorPosForDragToPoint:(NSPoint)point {
+ CGFloat x = 0;
+ int destIndex = [self indexForDragToPoint:point];
+ int numButtons = displayedButtonCount_;
+
+ // If it's a drop strictly between existing buttons ...
+ if (destIndex >= 0 && destIndex < numButtons) {
+ // ... put the indicator right between the buttons.
+ BookmarkButton* button =
+ [buttons_ objectAtIndex:static_cast<NSUInteger>(destIndex)];
+ DCHECK(button);
+ NSRect buttonFrame = [button frame];
+ x = buttonFrame.origin.x - 0.5 * bookmarks::kBookmarkHorizontalPadding;
+
+ // If it's a drop at the end (past the last button, if there are any) ...
+ } else if (destIndex == numButtons) {
+ // and if it's past the last button ...
+ if (numButtons > 0) {
+ // ... find the last button, and put the indicator to its right.
+ BookmarkButton* button =
+ [buttons_ objectAtIndex:static_cast<NSUInteger>(destIndex - 1)];
+ DCHECK(button);
+ NSRect buttonFrame = [button frame];
+ x = NSMaxX(buttonFrame) + 0.5 * bookmarks::kBookmarkHorizontalPadding;
+
+ // Otherwise, put it right at the beginning.
+ } else {
+ x = 0.5 * bookmarks::kBookmarkHorizontalPadding;
+ }
+ } else {
+ NOTREACHED();
+ }
+
+ return x;
+}
+
+- (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child {
+ // If the bookmarkbar is not in detached mode, lock bar visibility, forcing
+ // the overlay to stay open when in fullscreen mode.
+ if (![self isInState:bookmarks::kDetachedState] &&
+ ![self isAnimatingToState:bookmarks::kDetachedState]) {
+ BrowserWindowController* browserController =
+ [BrowserWindowController browserWindowControllerForView:[self view]];
+ [browserController lockBarVisibilityForOwner:child
+ withAnimation:NO
+ delay:NO];
+ }
+}
+
+- (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child {
+ // Release bar visibility, allowing the overlay to close if in fullscreen
+ // mode.
+ BrowserWindowController* browserController =
+ [BrowserWindowController browserWindowControllerForView:[self view]];
+ [browserController releaseBarVisibilityForOwner:child
+ withAnimation:NO
+ delay:NO];
+}
+
+// Add a new folder controller as triggered by the given folder button.
+- (void)addNewFolderControllerWithParentButton:(BookmarkButton*)parentButton {
+ if (folderController_)
+ [self closeAllBookmarkFolders];
+
+ // Folder controller, like many window controllers, owns itself.
+ folderController_ =
+ [[BookmarkBarFolderController alloc] initWithParentButton:parentButton
+ parentController:nil
+ barController:self];
+ [folderController_ showWindow:self];
+
+ // Only BookmarkBarController has this; the
+ // BookmarkBarFolderController does not.
+ [self watchForExitEvent:YES];
+}
+
+- (void)openAll:(const BookmarkNode*)node
+ disposition:(WindowOpenDisposition)disposition {
+ [self closeFolderAndStopTrackingMenus];
+ bookmark_utils::OpenAll([[self view] window],
+ browser_->profile(),
+ browser_,
+ node,
+ disposition);
+}
- (void)addButtonForNode:(const BookmarkNode*)node
atIndex:(NSInteger)buttonIndex {
@@ -2172,6 +2108,53 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
}
}
+// TODO(mrossetti): Duplicate code with BookmarkBarFolderController.
+// http://crbug.com/35966
+- (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point {
+ DCHECK([urls count] == [titles count]);
+ BOOL nodesWereAdded = NO;
+ // Figure out where these new bookmarks nodes are to be added.
+ BookmarkButton* button = [self buttonForDroppingOnAtPoint:point];
+ const BookmarkNode* destParent = NULL;
+ int destIndex = 0;
+ if ([button isFolder]) {
+ destParent = [button bookmarkNode];
+ // Drop it at the end.
+ destIndex = [button bookmarkNode]->GetChildCount();
+ } else {
+ // Else we're dropping somewhere on the bar, so find the right spot.
+ destParent = bookmarkModel_->GetBookmarkBarNode();
+ destIndex = [self indexForDragToPoint:point];
+ }
+
+ // Don't add the bookmarks if the destination index shows an error.
+ if (destIndex >= 0) {
+ // Create and add the new bookmark nodes.
+ size_t urlCount = [urls count];
+ for (size_t i = 0; i < urlCount; ++i) {
+ // URLs come in several forms (see NSPasteboard+Utils.mm).
+ GURL gurl;
+ const char* string = [[urls objectAtIndex:i] UTF8String];
+ if (string)
+ gurl = GURL(string);
+ if (!gurl.is_valid() && string) {
+ gurl = GURL([[NSString stringWithFormat:@"file://%s", string]
+ UTF8String]);
+ }
+ DCHECK(gurl.is_valid());
+ if (gurl.is_valid()) {
+ bookmarkModel_->AddURL(destParent,
+ destIndex++,
+ base::SysNSStringToWide([titles
+ objectAtIndex:i]),
+ gurl);
+ nodesWereAdded = YES;
+ }
+ }
+ }
+ return nodesWereAdded;
+}
+
// TODO(mrossetti): jrg wants this broken up into smaller functions.
- (void)moveButtonFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex {
if (fromIndex != toIndex) {
@@ -2280,6 +2263,35 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
}
}
+- (id<BookmarkButtonControllerProtocol>)controllerForNode:
+ (const BookmarkNode*)node {
+ // See if it's in the bar, then if it is in the hierarchy of visible
+ // folder menus.
+ if (bookmarkModel_->GetBookmarkBarNode() == node)
+ return self;
+ return [folderController_ controllerForNode:node];
+}
+
+#pragma mark BookmarkButtonControllerProtocol
+
+// NOT an override of a standard Cocoa call made to NSViewControllers.
+- (void)hookForEvent:(NSEvent*)theEvent {
+ if ([self isEventAnExitEvent:theEvent])
+ [self closeFolderAndStopTrackingMenus];
+}
+
+#pragma mark TestingAPI Only
+
+- (NSMenu*)buttonContextMenu {
+ return buttonContextMenu_;
+}
+
+// Intentionally ignores ownership issues; used for testing and we try
+// to minimize touching the object passed in (likely a mock).
+- (void)setButtonContextMenu:(id)menu {
+ buttonContextMenu_ = menu;
+}
+
- (void)setIgnoreAnimations:(BOOL)ignore {
ignoreAnimations_ = ignore;
}
diff --git a/chrome/browser/cocoa/bookmark_bar_folder_controller.mm b/chrome/browser/cocoa/bookmark_bar_folder_controller.mm
index 75464f6..2f8f70f 100644
--- a/chrome/browser/cocoa/bookmark_bar_folder_controller.mm
+++ b/chrome/browser/cocoa/bookmark_bar_folder_controller.mm
@@ -152,6 +152,28 @@ const CGFloat kScrollWindowVerticalMargin = 0.0;
[super showWindow:sender];
}
+- (BookmarkButton*)parentButton {
+ return parentButton_.get();
+}
+
+- (void)offsetFolderMenuWindow:(NSSize)offset {
+ NSWindow* window = [self window];
+ NSRect windowFrame = [window frame];
+ windowFrame.origin.x -= offset.width;
+ windowFrame.origin.y += offset.height; // Yes, in the opposite direction!
+ [window setFrame:windowFrame display:YES];
+ [folderController_ offsetFolderMenuWindow:offset];
+}
+
+- (void)reconfigureMenu {
+ for (BookmarkButton* button in buttons_.get())
+ [button removeFromSuperview];
+ [buttons_ removeAllObjects];
+ [self configureWindow];
+}
+
+#pragma mark Private Methods
+
- (BookmarkButtonCell*)cellForBookmarkNode:(const BookmarkNode*)child {
NSImage* image = child ? [barController_ favIconForNode:child] : nil;
NSMenu* menu = child ? child->is_folder() ? folderMenu_ : buttonMenu_ : nil;
@@ -237,10 +259,6 @@ const CGFloat kScrollWindowVerticalMargin = 0.0;
return mainView_;
}
-- (BookmarkBarFolderController*)folderController {
- return folderController_;
-}
-
- (id)folderTarget {
return folderTarget_.get();
}
@@ -399,15 +417,6 @@ const CGFloat kScrollWindowVerticalMargin = 0.0;
[self configureWindowLevel];
}
-- (void)offsetFolderMenuWindow:(NSSize)offset {
- NSWindow* window = [self window];
- NSRect windowFrame = [window frame];
- windowFrame.origin.x -= offset.width;
- windowFrame.origin.y += offset.height; // Yes, in the opposite direction!
- [window setFrame:windowFrame display:YES];
- [folderController_ offsetFolderMenuWindow:offset];
-}
-
// TODO(mrossetti): See if the following can be moved into view's viewWillDraw:.
- (CGFloat)adjustButtonWidths {
CGFloat width = bookmarks::kBookmarkMenuButtonMinimumWidth;
@@ -509,150 +518,6 @@ const CGFloat kScrollWindowVerticalMargin = 0.0;
return scrollable_;
}
-#pragma mark BookmarkButtonControllerProtocol
-
-- (void)addButtonForNode:(const BookmarkNode*)node
- atIndex:(NSInteger)buttonIndex {
- NSRect buttonFrame = NSMakeRect(bookmarks::kBookmarkHorizontalPadding,
- bookmarks::kBookmarkVerticalPadding,
- NSWidth([mainView_ frame]) - 2 * bookmarks::kBookmarkHorizontalPadding -
- bookmarks::kScrollViewContentWidthMargin,
- bookmarks::kBookmarkBarHeight - 2 * bookmarks::kBookmarkVerticalPadding);
- // When adding a button to an empty folder we must remove the 'empty'
- // placeholder button. This can be detected by checking for a parent
- // child count of 1.
- const BookmarkNode* parentNode = node->GetParent();
- if (parentNode->GetChildCount() == 1) {
- BookmarkButton* emptyButton = [buttons_ lastObject];
- [emptyButton removeFromSuperview];
- [buttons_ removeLastObject];
- } else {
- // Set us up to remember the last moved button's frame.
- buttonFrame.origin.y += NSHeight([mainView_ frame]) +
- bookmarks::kBookmarkBarHeight;
- }
-
- if (buttonIndex == -1)
- buttonIndex = [buttons_ count];
-
- BookmarkButton* button = nil; // Remember so it can be de-highlighted.
- for (NSInteger i = 0; i < buttonIndex; ++i) {
- button = [buttons_ objectAtIndex:i];
- buttonFrame = [button frame];
- buttonFrame.origin.y += bookmarks::kBookmarkBarHeight;
- [button setFrame:buttonFrame];
- }
- [[button cell] mouseExited:nil]; // De-highlight.
- if (parentNode->GetChildCount() > 1)
- buttonFrame.origin.y -= bookmarks::kBookmarkBarHeight;
- BookmarkButton* newButton = [self makeButtonForNode:node
- frame:buttonFrame];
- [buttons_ insertObject:newButton atIndex:buttonIndex];
- [mainView_ addSubview:newButton];
-
- // Close any child folder(s) which may still be open.
- [self closeBookmarkFolder:self];
-
- // Prelim height of the window. We'll trim later as needed.
- int height = [buttons_ count] * bookmarks::kBookmarkButtonHeight;
- [self adjustWindowForHeight:height];
-}
-
-- (void)moveButtonFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex {
- if (fromIndex != toIndex) {
- if (toIndex == -1)
- toIndex = [buttons_ count];
- BookmarkButton* movedButton = [buttons_ objectAtIndex:fromIndex];
- [buttons_ removeObjectAtIndex:fromIndex];
- NSRect movedFrame = [movedButton frame];
- NSPoint toOrigin = movedFrame.origin;
- [movedButton setHidden:YES];
- if (fromIndex < toIndex) {
- BookmarkButton* targetButton = [buttons_ objectAtIndex:toIndex - 1];
- toOrigin = [targetButton frame].origin;
- for (NSInteger i = fromIndex; i < toIndex; ++i) {
- BookmarkButton* button = [buttons_ objectAtIndex:i];
- NSRect frame = [button frame];
- frame.origin.y += bookmarks::kBookmarkBarHeight;
- [button setFrameOrigin:frame.origin];
- }
- } else {
- BookmarkButton* targetButton = [buttons_ objectAtIndex:toIndex];
- toOrigin = [targetButton frame].origin;
- for (NSInteger i = fromIndex - 1; i >= toIndex; --i) {
- BookmarkButton* button = [buttons_ objectAtIndex:i];
- NSRect buttonFrame = [button frame];
- buttonFrame.origin.y -= bookmarks::kBookmarkBarHeight;
- [button setFrameOrigin:buttonFrame.origin];
- }
- }
- [buttons_ insertObject:movedButton atIndex:toIndex];
- [movedButton setFrameOrigin:toOrigin];
- [movedButton setHidden:NO];
- }
-}
-
-// TODO(jrg): Refactor BookmarkBarFolder common code. http://crbug.com/35966
-- (void)removeButton:(NSInteger)buttonIndex animate:(BOOL)animate {
- // TODO(mrossetti): Get disappearing animation to work. http://crbug.com/42360
- BookmarkButton* oldButton = [buttons_ objectAtIndex:buttonIndex];
- NSRect poofFrame = [oldButton bounds];
- NSPoint poofPoint = NSMakePoint(NSMidX(poofFrame), NSMidY(poofFrame));
- poofPoint = [oldButton convertPoint:poofPoint toView:nil];
- poofPoint = [[oldButton window] convertBaseToScreen:poofPoint];
- [oldButton removeFromSuperview];
- if (animate && !ignoreAnimations_)
- NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint,
- NSZeroSize, nil, nil, nil);
- [buttons_ removeObjectAtIndex:buttonIndex];
- for (NSInteger i = 0; i < buttonIndex; ++i) {
- BookmarkButton* button = [buttons_ objectAtIndex:i];
- NSRect buttonFrame = [button frame];
- buttonFrame.origin.y -= bookmarks::kBookmarkBarHeight;
- [button setFrame:buttonFrame];
- }
- // Search for and adjust submenus, if necessary.
- NSInteger buttonCount = [buttons_ count];
- if (buttonCount) {
- BookmarkButton* subButton = [folderController_ parentButton];
- for (NSInteger i = buttonIndex; i < buttonCount; ++i) {
- BookmarkButton* aButton = [buttons_ objectAtIndex:i];
- // If this button is showing its menu then we need to move the menu, too.
- if (aButton == subButton)
- [folderController_ offsetFolderMenuWindow:NSMakeSize(0.0,
- bookmarks::kBookmarkBarHeight)];
- }
- } else {
- // If all nodes have been removed from this folder then add in the
- // 'empty' placeholder button.
- NSRect buttonFrame = NSMakeRect(bookmarks::kBookmarkHorizontalPadding,
- bookmarks::kBookmarkButtonHeight -
- (bookmarks::kBookmarkBarHeight -
- bookmarks::kBookmarkVerticalPadding),
- bookmarks::kDefaultBookmarkWidth,
- (bookmarks::kBookmarkBarHeight -
- 2 * bookmarks::kBookmarkVerticalPadding));
- BookmarkButton* button = [self makeButtonForNode:nil
- frame:buttonFrame];
- [buttons_ addObject:button];
- [mainView_ addSubview:button];
- buttonCount = 1;
- }
-
- // Propose a height for the window. We'll trim later as needed.
- int height = buttonCount * bookmarks::kBookmarkButtonHeight;
- [self adjustWindowForHeight:height];
-}
-
-- (id<BookmarkButtonControllerProtocol>)controllerForNode:
- (const BookmarkNode*)node {
- // See if we are holding this node, otherwise see if it is in our
- // hierarchy of visible folder menus.
- if ([parentButton_ bookmarkNode] == node)
- return self;
- return [folderController_ controllerForNode:node];
-}
-
// Start a "scroll up" timer.
- (void)beginScrollWindowUp {
[self addScrollTimerWithDelta:kBookmarkBarFolderScrollAmount];
@@ -813,51 +678,105 @@ const CGFloat kScrollWindowVerticalMargin = 0.0;
scrollTrackingArea_.reset();
}
-- (ThemeProvider*)themeProvider {
- return [parentController_ themeProvider];
+// Delegate callback.
+- (void)windowWillClose:(NSNotification*)notification {
+ [parentController_ childFolderWillClose:self];
+ [self closeBookmarkFolder:self];
+ [self autorelease];
}
-- (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child {
- // Do nothing.
+// Close the old hover-open bookmark folder, and open a new one. We
+// do both in one step to allow for a delay in closing the old one.
+// See comments above kDragHoverCloseDelay (bookmark_bar_controller.h)
+// for more details.
+- (void)openBookmarkFolderFromButtonAndCloseOldOne:(id)sender {
+ // If an old submenu exists, close it immediately.
+ [self closeBookmarkFolder:sender];
+
+ // Open a new one if meaningful.
+ if ([sender isFolder])
+ [folderTarget_ openBookmarkFolderFromButton:sender];
}
-- (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child {
- // Do nothing.
+- (NSArray*)buttons {
+ return buttons_.get();
}
-// Recursively close all bookmark folders.
-- (void)closeAllBookmarkFolders {
- // Closing the top level implicitly closes all children.
- [barController_ closeAllBookmarkFolders];
+- (void)close {
+ [folderController_ close];
+ [super close];
}
-// Close our bookmark folder (a sub-controller) if we have one.
-- (void)closeBookmarkFolder:(id)sender {
- // folderController_ may be nil but that's OK.
- [[folderController_ window] close];
- folderController_ = nil;
+- (void)scrollWheel:(NSEvent *)theEvent {
+ if (scrollable_) {
+ // We go negative since an NSScrollView has a flipped coordinate frame.
+ CGFloat amt = kBookmarkBarFolderScrollWheelAmount * -[theEvent deltaY];
+ [self performOneScroll:amt];
+ }
}
-// Delegate callback.
-- (void)windowWillClose:(NSNotification*)notification {
- [parentController_ childFolderWillClose:self];
+#pragma mark Actions Forwarded to Parent BookmarkBarController
+
+- (IBAction)openBookmark:(id)sender {
+ [barController_ openBookmark:sender];
+}
+
+- (IBAction)openBookmarkInNewForegroundTab:(id)sender {
+ [barController_ openBookmarkInNewForegroundTab:sender];
+}
+
+- (IBAction)openBookmarkInNewWindow:(id)sender {
+ [barController_ openBookmarkInNewWindow:sender];
+}
+
+- (IBAction)openBookmarkInIncognitoWindow:(id)sender {
+ [barController_ openBookmarkInIncognitoWindow:sender];
+}
+
+- (IBAction)editBookmark:(id)sender {
+ [barController_ editBookmark:sender];
+}
+
+- (IBAction)cutBookmark:(id)sender {
[self closeBookmarkFolder:self];
- [self autorelease];
+ [barController_ cutBookmark:sender];
}
-- (BookmarkButton*)parentButton {
- return parentButton_.get();
+- (IBAction)copyBookmark:(id)sender {
+ [barController_ copyBookmark:sender];
}
-// Delegate method. Shared implementation with BookmarkBarController.
-- (void)fillPasteboard:(NSPasteboard*)pboard
- forDragOfButton:(BookmarkButton*)button {
- [[self folderTarget] fillPasteboard:pboard forDragOfButton:button];
+- (IBAction)pasteBookmark:(id)sender {
+ [barController_ pasteBookmark:sender];
+}
- // Close our folder menu and submenus since we know we're going to be dragged.
+- (IBAction)deleteBookmark:(id)sender {
[self closeBookmarkFolder:self];
+ [barController_ deleteBookmark:sender];
}
+- (IBAction)openAllBookmarks:(id)sender {
+ [barController_ openAllBookmarks:sender];
+}
+
+- (IBAction)openAllBookmarksNewWindow:(id)sender {
+ [barController_ openAllBookmarksNewWindow:sender];
+}
+
+- (IBAction)openAllBookmarksIncognitoWindow:(id)sender {
+ [barController_ openAllBookmarksIncognitoWindow:sender];
+}
+
+- (IBAction)addPage:(id)sender {
+ [barController_ addPage:sender];
+}
+
+- (IBAction)addFolder:(id)sender {
+ [barController_ addFolder:sender];
+}
+
+#pragma mark Drag & Drop
+
// Find something like std::is_between<T>? I can't believe one doesn't exist.
// http://crbug.com/35966
static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
@@ -904,48 +823,6 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
return nil;
}
-// TODO(jrg): Refactor BookmarkBarFolder common code. http://crbug.com/35966
-// Most of the work (e.g. drop indicator) is taken care of in the
-// folder_view. Here we handle hover open issues for subfolders.
-// Caution: there are subtle differences between this one and
-// bookmark_bar_controller.mm's version.
-- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info {
- NSPoint currentLocation = [info draggingLocation];
- BookmarkButton* button = [self buttonForDroppingOnAtPoint:currentLocation];
-
- // Don't allow drops that would result in cycles.
- if (button) {
- NSData* data = [[info draggingPasteboard]
- dataForType:kBookmarkButtonDragType];
- if (data && [info draggingSource]) {
- BookmarkButton* sourceButton = nil;
- [data getBytes:&sourceButton length:sizeof(sourceButton)];
- const BookmarkNode* sourceNode = [sourceButton bookmarkNode];
- const BookmarkNode* destNode = [button bookmarkNode];
- if (destNode->HasAncestor(sourceNode))
- button = nil;
- }
- }
- // Delegate handling of dragging over a button to the |hoverState_| member.
- return [hoverState_ draggingEnteredButton:button];
-}
-
-// Unlike bookmark_bar_controller, we need to keep track of dragging state.
-// We also need to make sure we cancel the delayed hover close.
-- (void)draggingExited:(id<NSDraggingInfo>)info {
- // NOT the same as a cancel --> we may have moved the mouse into the submenu.
- // Delegate handling of the hover button to the |hoverState_| member.
- [hoverState_ draggingExited];
-}
-
-- (BOOL)dragShouldLockBarVisibility {
- return [parentController_ dragShouldLockBarVisibility];
-}
-
-- (NSWindow*)browserWindow {
- return [parentController_ browserWindow];
-}
-
// TODO(jrg): again we have code dup, sort of, with
// bookmark_bar_controller.mm, but the axis is changed. One minor
// difference is accomodation for the "empty" button (which may not
@@ -992,66 +869,6 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
[[parentButton_ cell] startingChildIndex]);
}
-// More code which essentially duplicates that of BookmarkBarController.
-// TODO(mrossetti,jrg): http://crbug.com/35966
-- (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point {
- DCHECK([urls count] == [titles count]);
- BOOL nodesWereAdded = NO;
- // Figure out where these new bookmarks nodes are to be added.
- BookmarkButton* button = [self buttonForDroppingOnAtPoint:point];
- BookmarkModel* bookmarkModel = [self bookmarkModel];
- const BookmarkNode* destParent = NULL;
- int destIndex = 0;
- if ([button isFolder]) {
- destParent = [button bookmarkNode];
- // Drop it at the end.
- destIndex = [button bookmarkNode]->GetChildCount();
- } else {
- // Else we're dropping somewhere in the folder, so find the right spot.
- destParent = [parentButton_ bookmarkNode];
- destIndex = [self indexForDragToPoint:point];
- // Be careful if the number of buttons != number of nodes.
- destIndex += [[parentButton_ cell] startingChildIndex];
- }
-
- // Create and add the new bookmark nodes.
- size_t urlCount = [urls count];
- for (size_t i = 0; i < urlCount; ++i) {
- // URLs come in several forms (see NSPasteboard+Utils.mm).
- GURL gurl;
- const char* string = [[urls objectAtIndex:i] UTF8String];
- if (string)
- gurl = GURL(string);
- if (!gurl.is_valid() && string) {
- gurl = GURL([[NSString stringWithFormat:@"file://%s", string]
- UTF8String]);
- }
- DCHECK(gurl.is_valid());
- if (gurl.is_valid()) {
- bookmarkModel->AddURL(destParent,
- destIndex++,
- base::SysNSStringToWide([titles objectAtIndex:i]),
- gurl);
- nodesWereAdded = YES;
- }
- }
- return nodesWereAdded;
-}
-
-- (BookmarkModel*)bookmarkModel {
- return [barController_ bookmarkModel];
-}
-
-// TODO(jrg): ARGH more code dup.
-// http://crbug.com/35966
-- (BOOL)dragButton:(BookmarkButton*)sourceButton
- to:(NSPoint)point
- copy:(BOOL)copy {
- DCHECK([sourceButton isKindOfClass:[BookmarkButton class]]);
- const BookmarkNode* sourceNode = [sourceButton bookmarkNode];
- return [self dragBookmark:sourceNode to:point copy:copy];
-}
-
// TODO(jrg): Yet more code dup.
// http://crbug.com/35966
- (BOOL)dragBookmark:(const BookmarkNode*)sourceNode
@@ -1093,18 +910,120 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
return wasCopiedOrMoved;
}
-// TODO(mrossetti,jrg): Identical to the same function in BookmarkBarController.
-// http://crbug.com/35966
-- (std::vector<const BookmarkNode*>)retrieveBookmarkDragDataNodes {
- std::vector<const BookmarkNode*> dragDataNodes;
- BookmarkDragData dragData;
- if(dragData.ReadFromDragClipboard()) {
- BookmarkModel* bookmarkModel = [self bookmarkModel];
- Profile* profile = bookmarkModel->profile();
- std::vector<const BookmarkNode*> nodes(dragData.GetNodes(profile));
- dragDataNodes.assign(nodes.begin(), nodes.end());
+#pragma mark BookmarkButtonDelegate Protocol
+
+- (void)fillPasteboard:(NSPasteboard*)pboard
+ forDragOfButton:(BookmarkButton*)button {
+ [[self folderTarget] fillPasteboard:pboard forDragOfButton:button];
+
+ // Close our folder menu and submenus since we know we're going to be dragged.
+ [self closeBookmarkFolder:self];
+}
+
+// Called from BookmarkButton.
+// Unlike bookmark_bar_controller's version, we DO default to being enabled.
+- (void)mouseEnteredButton:(id)sender event:(NSEvent*)event {
+ buttonThatMouseIsIn_ = sender;
+
+ // Cancel a previous hover if needed.
+ [NSObject cancelPreviousPerformRequestsWithTarget:self];
+
+ // If already opened, then we exited but re-entered the button
+ // (without entering another button open), do nothing.
+ if ([folderController_ parentButton] == sender)
+ return;
+
+ [self performSelector:@selector(openBookmarkFolderFromButtonAndCloseOldOne:)
+ withObject:sender
+ afterDelay:bookmarks::kHoverOpenDelay];
+}
+
+// Called from the BookmarkButton
+- (void)mouseExitedButton:(id)sender event:(NSEvent*)event {
+ if (buttonThatMouseIsIn_ == sender)
+ buttonThatMouseIsIn_ = nil;
+
+ // Stop any timer about opening a new hover-open folder.
+
+ // Since a performSelector:withDelay: on self retains self, it is
+ // possible that a cancelPreviousPerformRequestsWithTarget: reduces
+ // the refcount to 0, releasing us. That's a bad thing to do while
+ // this object (or others it may own) is in the event chain. Thus
+ // we have a retain/autorelease.
+ [self retain];
+ [NSObject cancelPreviousPerformRequestsWithTarget:self];
+ [self autorelease];
+}
+
+- (NSWindow*)browserWindow {
+ return [parentController_ browserWindow];
+}
+
+#pragma mark BookmarkButtonControllerProtocol
+
+// Recursively close all bookmark folders.
+- (void)closeAllBookmarkFolders {
+ // Closing the top level implicitly closes all children.
+ [barController_ closeAllBookmarkFolders];
+}
+
+// Close our bookmark folder (a sub-controller) if we have one.
+- (void)closeBookmarkFolder:(id)sender {
+ // folderController_ may be nil but that's OK.
+ [[folderController_ window] close];
+ folderController_ = nil;
+}
+
+- (BookmarkModel*)bookmarkModel {
+ return [barController_ bookmarkModel];
+}
+
+// TODO(jrg): Refactor BookmarkBarFolder common code. http://crbug.com/35966
+// Most of the work (e.g. drop indicator) is taken care of in the
+// folder_view. Here we handle hover open issues for subfolders.
+// Caution: there are subtle differences between this one and
+// bookmark_bar_controller.mm's version.
+- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info {
+ NSPoint currentLocation = [info draggingLocation];
+ BookmarkButton* button = [self buttonForDroppingOnAtPoint:currentLocation];
+
+ // Don't allow drops that would result in cycles.
+ if (button) {
+ NSData* data = [[info draggingPasteboard]
+ dataForType:kBookmarkButtonDragType];
+ if (data && [info draggingSource]) {
+ BookmarkButton* sourceButton = nil;
+ [data getBytes:&sourceButton length:sizeof(sourceButton)];
+ const BookmarkNode* sourceNode = [sourceButton bookmarkNode];
+ const BookmarkNode* destNode = [button bookmarkNode];
+ if (destNode->HasAncestor(sourceNode))
+ button = nil;
+ }
}
- return dragDataNodes;
+ // Delegate handling of dragging over a button to the |hoverState_| member.
+ return [hoverState_ draggingEnteredButton:button];
+}
+
+// Unlike bookmark_bar_controller, we need to keep track of dragging state.
+// We also need to make sure we cancel the delayed hover close.
+- (void)draggingExited:(id<NSDraggingInfo>)info {
+ // NOT the same as a cancel --> we may have moved the mouse into the submenu.
+ // Delegate handling of the hover button to the |hoverState_| member.
+ [hoverState_ draggingExited];
+}
+
+- (BOOL)dragShouldLockBarVisibility {
+ return [parentController_ dragShouldLockBarVisibility];
+}
+
+// TODO(jrg): ARGH more code dup.
+// http://crbug.com/35966
+- (BOOL)dragButton:(BookmarkButton*)sourceButton
+ to:(NSPoint)point
+ copy:(BOOL)copy {
+ DCHECK([sourceButton isKindOfClass:[BookmarkButton class]]);
+ const BookmarkNode* sourceNode = [sourceButton bookmarkNode];
+ return [self dragBookmark:sourceNode to:point copy:copy];
}
// TODO(mrossetti,jrg): Identical to the same function in BookmarkBarController.
@@ -1124,6 +1043,20 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
return dragged;
}
+// TODO(mrossetti,jrg): Identical to the same function in BookmarkBarController.
+// http://crbug.com/35966
+- (std::vector<const BookmarkNode*>)retrieveBookmarkDragDataNodes {
+ std::vector<const BookmarkNode*> dragDataNodes;
+ BookmarkDragData dragData;
+ if(dragData.ReadFromDragClipboard()) {
+ BookmarkModel* bookmarkModel = [self bookmarkModel];
+ Profile* profile = bookmarkModel->profile();
+ std::vector<const BookmarkNode*> nodes(dragData.GetNodes(profile));
+ dragDataNodes.assign(nodes.begin(), nodes.end());
+ }
+ return dragDataNodes;
+}
+
// Return YES if we should show the drop indicator, else NO.
// TODO(jrg): ARGH code dup!
// http://crbug.com/35966
@@ -1131,7 +1064,6 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
return ![self buttonForDroppingOnAtPoint:point];
}
-
// Return the y position for a drop indicator.
//
// TODO(jrg): again we have code dup, sort of, with
@@ -1151,7 +1083,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
NSRect buttonFrame = [button frame];
y = NSMaxY(buttonFrame) + 0.5 * bookmarks::kBookmarkVerticalPadding;
- // If it's a drop at the end (past the last button, if there are any) ...
+ // If it's a drop at the end (past the last button, if there are any) ...
} else if (destIndex == numButtons) {
// and if it's past the last button ...
if (numButtons > 0) {
@@ -1160,8 +1092,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
[buttons_ objectAtIndex:static_cast<NSUInteger>(destIndex - 1)];
DCHECK(button);
NSRect buttonFrame = [button frame];
- y = buttonFrame.origin.y -
- 0.5 * bookmarks::kBookmarkVerticalPadding;
+ y = buttonFrame.origin.y - 0.5 * bookmarks::kBookmarkVerticalPadding;
}
} else {
@@ -1171,57 +1102,20 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
return y;
}
-// Close the old hover-open bookmark folder, and open a new one. We
-// do both in one step to allow for a delay in closing the old one.
-// See comments above kDragHoverCloseDelay (bookmark_bar_controller.h)
-// for more details.
-- (void)openBookmarkFolderFromButtonAndCloseOldOne:(id)sender {
- // If an old submenu exists, close it immediately.
- [self closeBookmarkFolder:sender];
-
- // Open a new one if meaningful.
- if ([sender isFolder])
- [folderTarget_ openBookmarkFolderFromButton:sender];
+- (ThemeProvider*)themeProvider {
+ return [parentController_ themeProvider];
}
-// Called from BookmarkButton.
-// Unlike bookmark_bar_controller's version, we DO default to being enabled.
-- (void)mouseEnteredButton:(id)sender event:(NSEvent*)event {
- buttonThatMouseIsIn_ = sender;
-
- // Cancel a previous hover if needed.
- [NSObject cancelPreviousPerformRequestsWithTarget:self];
-
- // If already opened, then we exited but re-entered the button
- // (without entering another button open), do nothing.
- if ([folderController_ parentButton] == sender)
- return;
-
- [self performSelector:@selector(openBookmarkFolderFromButtonAndCloseOldOne:)
- withObject:sender
- afterDelay:bookmarks::kHoverOpenDelay];
+- (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child {
+ // Do nothing.
}
-// Called from the BookmarkButton
-- (void)mouseExitedButton:(id)sender event:(NSEvent*)event {
- if (buttonThatMouseIsIn_ == sender)
- buttonThatMouseIsIn_ = nil;
-
- // Stop any timer about opening a new hover-open folder.
-
- // Since a performSelector:withDelay: on self retains self, it is
- // possible that a cancelPreviousPerformRequestsWithTarget: reduces
- // the refcount to 0, releasing us. That's a bad thing to do while
- // this object (or others it may own) is in the event chain. Thus
- // we have a retain/autorelease.
- [self retain];
- [NSObject cancelPreviousPerformRequestsWithTarget:self];
- [self autorelease];
+- (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child {
+ // Do nothing.
}
-- (void)openAll:(const BookmarkNode*)node
- disposition:(WindowOpenDisposition)disposition {
- [parentController_ openAll:node disposition:disposition];
+- (BookmarkBarFolderController*)folderController {
+ return folderController_;
}
// Add a new folder controller as triggered by the given folder button.
@@ -1237,93 +1131,204 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) {
[folderController_ showWindow:self];
}
-- (NSArray*)buttons {
- return buttons_.get();
-}
-
-- (void)close {
- [folderController_ close];
- [super close];
+- (void)openAll:(const BookmarkNode*)node
+ disposition:(WindowOpenDisposition)disposition {
+ [parentController_ openAll:node disposition:disposition];
}
-- (void)scrollWheel:(NSEvent *)theEvent {
- if (scrollable_) {
- // We go negative since an NSScrollView has a flipped coordinate frame.
- CGFloat amt = kBookmarkBarFolderScrollWheelAmount * -[theEvent deltaY];
- [self performOneScroll:amt];
+- (void)addButtonForNode:(const BookmarkNode*)node
+ atIndex:(NSInteger)buttonIndex {
+ NSRect buttonFrame = NSMakeRect(bookmarks::kBookmarkHorizontalPadding,
+ bookmarks::kBookmarkVerticalPadding,
+ NSWidth([mainView_ frame]) - 2 * bookmarks::kBookmarkHorizontalPadding -
+ bookmarks::kScrollViewContentWidthMargin,
+ bookmarks::kBookmarkBarHeight - 2 *
+ bookmarks::kBookmarkVerticalPadding);
+ // When adding a button to an empty folder we must remove the 'empty'
+ // placeholder button. This can be detected by checking for a parent
+ // child count of 1.
+ const BookmarkNode* parentNode = node->GetParent();
+ if (parentNode->GetChildCount() == 1) {
+ BookmarkButton* emptyButton = [buttons_ lastObject];
+ [emptyButton removeFromSuperview];
+ [buttons_ removeLastObject];
+ } else {
+ // Set us up to remember the last moved button's frame.
+ buttonFrame.origin.y += NSHeight([mainView_ frame]) +
+ bookmarks::kBookmarkBarHeight;
}
-}
-- (void)reconfigureMenu {
- for (BookmarkButton* button in buttons_.get())
- [button removeFromSuperview];
- [buttons_ removeAllObjects];
- [self configureWindow];
-}
-
-- (void)setIgnoreAnimations:(BOOL)ignore {
- ignoreAnimations_ = ignore;
-}
-
-#pragma mark Methods Forwarded to BookmarkBarController
-
-- (IBAction)cutBookmark:(id)sender {
- [self closeBookmarkFolder:self];
- [barController_ cutBookmark:sender];
-}
-
-- (IBAction)copyBookmark:(id)sender {
- [barController_ copyBookmark:sender];
-}
+ if (buttonIndex == -1)
+ buttonIndex = [buttons_ count];
-- (IBAction)pasteBookmark:(id)sender {
- [barController_ pasteBookmark:sender];
-}
+ BookmarkButton* button = nil; // Remember so it can be de-highlighted.
+ for (NSInteger i = 0; i < buttonIndex; ++i) {
+ button = [buttons_ objectAtIndex:i];
+ buttonFrame = [button frame];
+ buttonFrame.origin.y += bookmarks::kBookmarkBarHeight;
+ [button setFrame:buttonFrame];
+ }
+ [[button cell] mouseExited:nil]; // De-highlight.
+ if (parentNode->GetChildCount() > 1)
+ buttonFrame.origin.y -= bookmarks::kBookmarkBarHeight;
+ BookmarkButton* newButton = [self makeButtonForNode:node
+ frame:buttonFrame];
+ [buttons_ insertObject:newButton atIndex:buttonIndex];
+ [mainView_ addSubview:newButton];
-- (IBAction)deleteBookmark:(id)sender {
+ // Close any child folder(s) which may still be open.
[self closeBookmarkFolder:self];
- [barController_ deleteBookmark:sender];
-}
-
-- (IBAction)openBookmark:(id)sender {
- [barController_ openBookmark:sender];
-}
-- (IBAction)addFolder:(id)sender {
- [barController_ addFolder:sender];
+ // Prelim height of the window. We'll trim later as needed.
+ int height = [buttons_ count] * bookmarks::kBookmarkButtonHeight;
+ [self adjustWindowForHeight:height];
}
-- (IBAction)addPage:(id)sender {
- [barController_ addPage:sender];
-}
+// More code which essentially duplicates that of BookmarkBarController.
+// TODO(mrossetti,jrg): http://crbug.com/35966
+- (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point {
+ DCHECK([urls count] == [titles count]);
+ BOOL nodesWereAdded = NO;
+ // Figure out where these new bookmarks nodes are to be added.
+ BookmarkButton* button = [self buttonForDroppingOnAtPoint:point];
+ BookmarkModel* bookmarkModel = [self bookmarkModel];
+ const BookmarkNode* destParent = NULL;
+ int destIndex = 0;
+ if ([button isFolder]) {
+ destParent = [button bookmarkNode];
+ // Drop it at the end.
+ destIndex = [button bookmarkNode]->GetChildCount();
+ } else {
+ // Else we're dropping somewhere in the folder, so find the right spot.
+ destParent = [parentButton_ bookmarkNode];
+ destIndex = [self indexForDragToPoint:point];
+ // Be careful if the number of buttons != number of nodes.
+ destIndex += [[parentButton_ cell] startingChildIndex];
+ }
-- (IBAction)editBookmark:(id)sender {
- [barController_ editBookmark:sender];
+ // Create and add the new bookmark nodes.
+ size_t urlCount = [urls count];
+ for (size_t i = 0; i < urlCount; ++i) {
+ // URLs come in several forms (see NSPasteboard+Utils.mm).
+ GURL gurl;
+ const char* string = [[urls objectAtIndex:i] UTF8String];
+ if (string)
+ gurl = GURL(string);
+ if (!gurl.is_valid() && string) {
+ gurl = GURL([[NSString stringWithFormat:@"file://%s", string]
+ UTF8String]);
+ }
+ DCHECK(gurl.is_valid());
+ if (gurl.is_valid()) {
+ bookmarkModel->AddURL(destParent,
+ destIndex++,
+ base::SysNSStringToWide([titles objectAtIndex:i]),
+ gurl);
+ nodesWereAdded = YES;
+ }
+ }
+ return nodesWereAdded;
}
-- (IBAction)openAllBookmarks:(id)sender {
- [barController_ openAllBookmarks:sender];
+- (void)moveButtonFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex {
+ if (fromIndex != toIndex) {
+ if (toIndex == -1)
+ toIndex = [buttons_ count];
+ BookmarkButton* movedButton = [buttons_ objectAtIndex:fromIndex];
+ [buttons_ removeObjectAtIndex:fromIndex];
+ NSRect movedFrame = [movedButton frame];
+ NSPoint toOrigin = movedFrame.origin;
+ [movedButton setHidden:YES];
+ if (fromIndex < toIndex) {
+ BookmarkButton* targetButton = [buttons_ objectAtIndex:toIndex - 1];
+ toOrigin = [targetButton frame].origin;
+ for (NSInteger i = fromIndex; i < toIndex; ++i) {
+ BookmarkButton* button = [buttons_ objectAtIndex:i];
+ NSRect frame = [button frame];
+ frame.origin.y += bookmarks::kBookmarkBarHeight;
+ [button setFrameOrigin:frame.origin];
+ }
+ } else {
+ BookmarkButton* targetButton = [buttons_ objectAtIndex:toIndex];
+ toOrigin = [targetButton frame].origin;
+ for (NSInteger i = fromIndex - 1; i >= toIndex; --i) {
+ BookmarkButton* button = [buttons_ objectAtIndex:i];
+ NSRect buttonFrame = [button frame];
+ buttonFrame.origin.y -= bookmarks::kBookmarkBarHeight;
+ [button setFrameOrigin:buttonFrame.origin];
+ }
+ }
+ [buttons_ insertObject:movedButton atIndex:toIndex];
+ [movedButton setFrameOrigin:toOrigin];
+ [movedButton setHidden:NO];
+ }
}
-- (IBAction)openAllBookmarksIncognitoWindow:(id)sender {
- [barController_ openAllBookmarksIncognitoWindow:sender];
-}
+// TODO(jrg): Refactor BookmarkBarFolder common code. http://crbug.com/35966
+- (void)removeButton:(NSInteger)buttonIndex animate:(BOOL)animate {
+ // TODO(mrossetti): Get disappearing animation to work. http://crbug.com/42360
+ BookmarkButton* oldButton = [buttons_ objectAtIndex:buttonIndex];
+ NSRect poofFrame = [oldButton bounds];
+ NSPoint poofPoint = NSMakePoint(NSMidX(poofFrame), NSMidY(poofFrame));
+ poofPoint = [oldButton convertPoint:poofPoint toView:nil];
+ poofPoint = [[oldButton window] convertBaseToScreen:poofPoint];
+ [oldButton removeFromSuperview];
+ if (animate && !ignoreAnimations_)
+ NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint,
+ NSZeroSize, nil, nil, nil);
+ [buttons_ removeObjectAtIndex:buttonIndex];
+ for (NSInteger i = 0; i < buttonIndex; ++i) {
+ BookmarkButton* button = [buttons_ objectAtIndex:i];
+ NSRect buttonFrame = [button frame];
+ buttonFrame.origin.y -= bookmarks::kBookmarkBarHeight;
+ [button setFrame:buttonFrame];
+ }
+ // Search for and adjust submenus, if necessary.
+ NSInteger buttonCount = [buttons_ count];
+ if (buttonCount) {
+ BookmarkButton* subButton = [folderController_ parentButton];
+ for (NSInteger i = buttonIndex; i < buttonCount; ++i) {
+ BookmarkButton* aButton = [buttons_ objectAtIndex:i];
+ // If this button is showing its menu then we need to move the menu, too.
+ if (aButton == subButton)
+ [folderController_ offsetFolderMenuWindow:NSMakeSize(0.0,
+ bookmarks::kBookmarkBarHeight)];
+ }
+ } else {
+ // If all nodes have been removed from this folder then add in the
+ // 'empty' placeholder button.
+ NSRect buttonFrame = NSMakeRect(bookmarks::kBookmarkHorizontalPadding,
+ bookmarks::kBookmarkButtonHeight -
+ (bookmarks::kBookmarkBarHeight -
+ bookmarks::kBookmarkVerticalPadding),
+ bookmarks::kDefaultBookmarkWidth,
+ (bookmarks::kBookmarkBarHeight -
+ 2 * bookmarks::kBookmarkVerticalPadding));
+ BookmarkButton* button = [self makeButtonForNode:nil
+ frame:buttonFrame];
+ [buttons_ addObject:button];
+ [mainView_ addSubview:button];
+ buttonCount = 1;
+ }
-- (IBAction)openAllBookmarksNewWindow:(id)sender {
- [barController_ openAllBookmarksNewWindow:sender];
+ // Propose a height for the window. We'll trim later as needed.
+ int height = buttonCount * bookmarks::kBookmarkButtonHeight;
+ [self adjustWindowForHeight:height];
}
-- (IBAction)openBookmarkInIncognitoWindow:(id)sender {
- [barController_ openBookmarkInIncognitoWindow:sender];
+- (id<BookmarkButtonControllerProtocol>)controllerForNode:
+ (const BookmarkNode*)node {
+ // See if we are holding this node, otherwise see if it is in our
+ // hierarchy of visible folder menus.
+ if ([parentButton_ bookmarkNode] == node)
+ return self;
+ return [folderController_ controllerForNode:node];
}
-- (IBAction)openBookmarkInNewForegroundTab:(id)sender {
- [barController_ openBookmarkInNewForegroundTab:sender];
-}
+#pragma mark TestingAPI Only
-- (IBAction)openBookmarkInNewWindow:(id)sender {
- [barController_ openBookmarkInNewWindow:sender];
+- (void)setIgnoreAnimations:(BOOL)ignore {
+ ignoreAnimations_ = ignore;
}
-
@end // BookmarkBarFolderController