diff options
author | mrossetti@chromium.org <mrossetti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-29 18:24:32 +0000 |
---|---|---|
committer | mrossetti@chromium.org <mrossetti@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-29 18:24:32 +0000 |
commit | 94d2135c70fc622226a72fd970ed1d3dd0428b6d (patch) | |
tree | 5189dc8b8ff562288411b831a8eae399fcc02f2f /chrome/browser/cocoa/bookmark_bar_controller.mm | |
parent | 54cfd69da88df660a056ababe8f9ed8aff8d8e9b (diff) | |
download | chromium_src-94d2135c70fc622226a72fd970ed1d3dd0428b6d.zip chromium_src-94d2135c70fc622226a72fd970ed1d3dd0428b6d.tar.gz chromium_src-94d2135c70fc622226a72fd970ed1d3dd0428b6d.tar.bz2 |
Add and implement cut/copy/paste to the bookmark bar and folder context menus.Prevent dropping bookmark folders onto their children to avoid creating cycles and crashing. Keep folder menu windows open after a cut/copy/paste/delete and drop operation. Add test helpers for easing comparison of bookmark model changes.
BUG=41217,36614,32064, 41404
Add and implement cut/copy/paste to the bookmark bar and folder context menus.Prevent dropping bookmark folders onto their children to avoid creating cycles and crashing. Keep folder menu windows open after a cut/copy/paste/delete and drop operation.
TEST=1) Present the context menu for the bookmark bar in various places and insure that the cut/copy/paste menu items are shown and properly enabled or disabled.
2) Present the context menu for a folder menu in various places and insure that the cut/copy/paste menu items are shown and properly enabled or disabled.
3) Perform cut/copy/paste/delete operations in a folder menu and subfolder menu and verify that the menu remains presented after the operation completes.
4) Drag a bookmark item from the bar to another location on the bar and verify that the button no longer shows up in the old location but does show up in the new location.
5) Drag a bookmark item from the bar to be within a folder located _after_ the item being dragged. Verify the bar no longer shows the bookmark item. Verify the folder does show the bookmark item. Verify that the folder window has shifted to the left so that it remains aligned with it corresponding bar folder indicator.
6) Drag a bookmark item from the bar to be within a folder located _before_ the item being dragged. Verify the bar no longer shows the bookmark item. Verify the folder does show the bookmark item. Verify that the folder window has _not_ shifted to the left and that it remains aligned with it corresponding bar folder indicator.
7) Drag a bookmark around within the same folder and verify that the folder window does not move.
8) Drag a bookmark item from a folder to the bar in a position _after_ the folder form which the item comes. The folder menu window should close.
9) Drag a bookmark item from a folder to a different folder and verify that the original folder closes and the new folder remains open.
10) Drag an item from a parent folder to a child folder and verify that both folders remain open. Verify that the subfolder remains aligned to its associated folder icon in the parent folder.
11) Drag an item from a child folder to a parent folder. Verify that the child folder closes and the parent folder remains open.
12) Try dragging a parent folder onto a child folder and verify that the drag is not allowed to complete.
13) Hover over a folder item in a folder menu window. Verify that the folder opens up a subview menu window. Now drag the original folder (the one being hovered over) and verify that its subfolder menu automatically closes.
14) Slowly shrink window width until "off the side" menu appears; make sure it appears as soon as the last button gets removed. Now slowly grow window and make sure it goes away at the right time.
15) Add enough bookmarks to the bookmark bar to nearly fill the width of the window. Verify that the off-the-side chevron is not showing. Slowly shrink the width of the window until the off-the-side-chevron appears. Verify that the right-most bookmark button has disappeared and does not draw over the top of the chevron. Continue to shrink the window and verify that bookmark buttons disappear. View the contents of the chevron and verify that the bookmark buttons which previously appeared on the bar now appear in the chevron. Slowly grow the window and verify that bookmark buttons reappear without drawing over the top of the chevron. Verify that those bookmark buttons no longer appear in the chevron when it is popped up. Continue growing the window until all bookmark buttons appear in the bar and the chevron disappears.
16) Shrink the window so that the off-the-side chevron appears and contains three or more bookmarks. Pop open the chevron. Drag bookmarks up and down in the chevron menu and verify that moves and copies are performed. Drag a bar bookmark into the chevron and verify that the chevron pops up and allows the drag. Verify that the bar and chevron contents rearrange such that buttons on the bar fill but do not overflow the bar. Drag a chevron bookmark out onto the bar. Once again verify that the bar and chevron buttons rearrange such that the bar is full but not overflowing.
Review URL: http://codereview.chromium.org/1742003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@45965 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/cocoa/bookmark_bar_controller.mm')
-rw-r--r-- | chrome/browser/cocoa/bookmark_bar_controller.mm | 410 |
1 files changed, 323 insertions, 87 deletions
diff --git a/chrome/browser/cocoa/bookmark_bar_controller.mm b/chrome/browser/cocoa/bookmark_bar_controller.mm index 1ebe272..809cba4 100644 --- a/chrome/browser/cocoa/bookmark_bar_controller.mm +++ b/chrome/browser/cocoa/bookmark_bar_controller.mm @@ -138,6 +138,11 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12; // Modifies self->buttons_. Do not add more buttons than will fit on the view. - (void)addNodesToButtonList:(const BookmarkNode*)node; +// Create an autoreleased button appropriate for insertion into the bookmark +// bar. Update |xOffset| with the offset appropriate for the subsequent button. +- (BookmarkButton*)buttonForNode:(const BookmarkNode*)node + xOffset:(int*)xOffset; + // Puts stuff into the final visual state without animating, stopping a running // animation if necessary. - (void)finalizeVisualState; @@ -161,6 +166,14 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12; - (int)indexForDragOfButton:(BookmarkButton*)sourceButton toPoint:(NSPoint)point; +// Add or remove buttons to/from the bar until it is filled but not overflowed. +- (void)redistributeButtonsOnBarAsNeeded; + +// Determine the nature of the bookmark bar contents based on the number of +// buttons showing. If too many then show the off-the-side list, if none +// then show the no items label. +- (void)reconfigureBookmarkBar; + - (void)addNode:(const BookmarkNode*)child toMenu:(NSMenu*)menu; - (void)addFolderNode:(const BookmarkNode*)node toMenu:(NSMenu*)menu; - (void)tagEmptyMenu:(NSMenu*)menu; @@ -368,11 +381,11 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12; // Configure the off-the-side button (e.g. specify the node range, // check if we should enable or disable it, etc). - (void)configureOffTheSideButtonContentsAndVisibility { - [[offTheSideButton_ cell] setStartingChildIndex:bookmarkBarDisplayedButtons_]; + [[offTheSideButton_ cell] setStartingChildIndex:displayedButtonCount_]; [[offTheSideButton_ cell] setBookmarkNode:bookmarkModel_->GetBookmarkBarNode()]; int bookmarkChildren = bookmarkModel_->GetBookmarkBarNode()->GetChildCount(); - if (bookmarkChildren > bookmarkBarDisplayedButtons_) { + if (bookmarkChildren > displayedButtonCount_) { [offTheSideButton_ setHidden:NO]; } else { [offTheSideButton_ setHidden:YES]; @@ -387,18 +400,8 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12; - (void)frameDidChange { if (!bookmarkModel_->IsLoaded()) return; - CGFloat width = NSWidth([[self view] frame]); - if (width > savedFrameWidth_) { - savedFrameWidth_ = width; - [self clearBookmarkBar]; - [self addNodesToButtonList:bookmarkModel_->GetBookmarkBarNode()]; - [self updateTheme:[[[self view] window] themeProvider]]; - [self addButtonsToView]; - } - [self positionOffTheSideButton]; - [self addButtonsToView]; - [self configureOffTheSideButtonContentsAndVisibility]; - [self centerNoItemsLabel]; + [self updateTheme:[[[self view] window] themeProvider]]; + [self reconfigureBookmarkBar]; } // Close all bookmark folders. "Folder" here is the fake menu for @@ -449,6 +452,11 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12; // Else open a new one if it makes sense to do so. if ([sender bookmarkNode]->is_folder()) [folderTarget_ openBookmarkFolderFromButton:sender]; +#if 1 + else + // We're over a non-folder bookmark so close any old folders. + [self closeAllBookmarkFolders]; +#endif } // BookmarkButtonDelegate protocol implementation. @@ -528,6 +536,9 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12; return folderTarget_.get(); } +- (int)displayedButtonCount { + return displayedButtonCount_; +} // Keep the "no items" label centered in response to a frame size change. - (void)centerNoItemsLabel { @@ -742,11 +753,11 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12; 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, bookmarkBarDisplayedButtons_); + return std::min(index, displayedButtonCount_); } // If nothing is to my right I am at the end! - return bookmarkBarDisplayedButtons_; + return displayedButtonCount_; } - (BOOL)dragButton:(BookmarkButton*)sourceButton @@ -776,11 +787,10 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12; // Be sure we don't try and drop a folder into itself. if (sourceNode != destParent) { - if (copy) { + if (copy) bookmarkModel_->Copy(sourceNode, destParent, destIndex); - } else { + else bookmarkModel_->Move(sourceNode, destParent, destIndex); - } } [self closeAllBookmarkFolders]; // For a hover open, if needed. @@ -862,6 +872,21 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { - (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 @@ -916,7 +941,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { toPoint:(NSPoint)point { CGFloat x = 0; int destIndex = [self indexForDragOfButton:sourceButton toPoint:point]; - int numButtons = bookmarkBarDisplayedButtons_; + int numButtons = displayedButtonCount_; // If it's a drop strictly between existing buttons ... if (destIndex >= 0 && destIndex < numButtons) { @@ -1121,7 +1146,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { - (IBAction)openOffTheSideFolderFromButton:(id)sender { DCHECK([sender isKindOfClass:[BookmarkButton class]]); DCHECK([[sender cell] isKindOfClass:[BookmarkButtonCell class]]); - [[sender cell] setStartingChildIndex:bookmarkBarDisplayedButtons_]; + [[sender cell] setStartingChildIndex:displayedButtonCount_]; [folderTarget_ openBookmarkFolderFromButton:sender]; } @@ -1407,8 +1432,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { [buttons_ makeObjectsPerformSelector:@selector(removeFromSuperview)]; [buttons_ removeAllObjects]; [self clearMenuTagMap]; - needToRebuildOffTheSideMenu_ = YES; - bookmarkBarDisplayedButtons_ = 0; + displayedButtonCount_ = 0; // Make sure there are no stale pointers in the pasteboard. This // can be important if a bookmark is deleted (via bookmark sync) @@ -1428,7 +1452,8 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { // 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 = [self contextMenuForNode:node]; + NSMenu* menu = node && node->is_folder() ? buttonFolderContextMenu_ : + buttonContextMenu_; BookmarkButtonCell* cell = [BookmarkButtonCell buttonCellForNode:node contextMenu:menu cellText:nil @@ -1440,11 +1465,6 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { return cell; } -- (NSMenu*)contextMenuForNode:(const BookmarkNode*)node { - return node && node->is_folder() ? buttonFolderContextMenu_ : - buttonContextMenu_; -} - // 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 @@ -1461,12 +1481,8 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { // for the next button. - (NSRect)frameForBookmarkButtonFromCell:(NSCell*)cell xOffset:(int*)xOffset { + DCHECK(xOffset); NSRect bounds = [buttonView_ bounds]; - // TODO(erg,jrg): There used to be an if statement here, comparing the height - // to 0. This essentially broke sizing because we're dealing with floats and - // the height wasn't precisely zero. The previous author wrote that they were - // doing this because of an animator, but we are not doing animations for beta - // so we do not care. bounds.size.height = bookmarks::kBookmarkButtonHeight; NSRect frame = NSInsetRect(bounds, @@ -1525,42 +1541,45 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { int xOffset = 0; for (int i = 0; i < node->GetChildCount(); i++) { const BookmarkNode* child = node->GetChild(i); + BookmarkButton* button = [self buttonForNode:child xOffset:&xOffset]; + if (NSMinX([button frame]) >= maxViewX) + break; + [buttons_ addObject:button]; + } +} - NSCell* cell = [self cellForBookmarkNode:child]; - NSRect frame = [self frameForBookmarkButtonFromCell:cell xOffset:&xOffset]; +- (BookmarkButton*)buttonForNode:(const BookmarkNode*)node + xOffset:(int*)xOffset { + NSCell* cell = [self cellForBookmarkNode:node]; + NSRect frame = [self frameForBookmarkButtonFromCell:cell xOffset:xOffset]; - // Break early if the button is off the end of the parent view. - if (NSMinX(frame) >= maxViewX) - break; + scoped_nsobject<BookmarkButton> + button([[BookmarkButton alloc] initWithFrame:frame]); + DCHECK(button.get()); - scoped_nsobject<BookmarkButton> - button([[BookmarkButton alloc] initWithFrame:frame]); - DCHECK(button.get()); - [buttons_ addObject:button]; + // [NSButton setCell:] warns to NOT use setCell: other than in the + // initializer of a control. However, we are using a basic + // NSButton whose initializer does not take an NSCell as an + // object. To honor the assumed semantics, we do nothing with + // NSButton between alloc/init and setCell:. + [button setCell:cell]; + [button setDelegate:self]; - // [NSButton setCell:] warns to NOT use setCell: other than in the - // initializer of a control. However, we are using a basic - // NSButton whose initializer does not take an NSCell as an - // object. To honor the assumed semantics, we do nothing with - // NSButton between alloc/init and setCell:. - [button setCell:cell]; - [button setDelegate:self]; - - if (child->is_folder()) { - [button setTarget:self]; - [button setAction:@selector(openBookmarkFolderFromButton:)]; - } else { - // Make the button do something - [button setTarget:self]; - [button setAction:@selector(openBookmark:)]; - // Add a tooltip. - NSString* title = base::SysWideToNSString(child->GetTitle()); - std::string url_string = child->GetURL().possibly_invalid_spec(); - NSString* tooltip = [NSString stringWithFormat:@"%@\n%s", title, - url_string.c_str()]; - [button setToolTip:tooltip]; - } + if (node->is_folder()) { + [button setTarget:self]; + [button setAction:@selector(openBookmarkFolderFromButton:)]; + } else { + // Make the button do something + [button setTarget:self]; + [button setAction:@selector(openBookmark:)]; + // Add a tooltip. + NSString* title = base::SysWideToNSString(node->GetTitle()); + std::string url_string = node->GetURL().possibly_invalid_spec(); + NSString* tooltip = [NSString stringWithFormat:@"%@\n%s", title, + url_string.c_str()]; + [button setToolTip:tooltip]; } + return [[button.get() retain] autorelease]; } // Add non-bookmark buttons to the view. This includes the chevron @@ -1577,21 +1596,23 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { // Add bookmark buttons to the view only if they are completely // visible and don't overlap the "other bookmarks". Remove buttons -// which are clipped. Called when building the bookmark bar and when -// the window resizes. +// which are clipped. Called when building the bookmark bar the first time. - (void)addButtonsToView { - NSView* superview = nil; - bookmarkBarDisplayedButtons_ = 0; - for (NSButton* button in buttons_.get()) { - superview = [button superview]; - if (NSMaxX([button frame]) <= NSMinX([offTheSideButton_ frame])) { - if (!superview) - [buttonView_ addSubview:button]; - bookmarkBarDisplayedButtons_++; - } else { - if (superview) - [button removeFromSuperview]; - } + displayedButtonCount_ = 0; + NSMutableArray* buttons = [self buttons]; + for (NSButton* button in buttons) { + if (NSMaxX([button frame]) > (NSMinX([offTheSideButton_ frame]) - + bookmarks::kBookmarkHorizontalPadding)) + break; + [buttonView_ addSubview:button]; + ++displayedButtonCount_; + } + NSUInteger removalCount = + [buttons count] - (NSUInteger)displayedButtonCount_; + if (removalCount > 0) { + NSRange removalRange = + NSMakeRange(displayedButtonCount_, removalCount); + [buttons removeObjectsInRange:removalRange]; } } @@ -1668,23 +1689,37 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { // http://crbug.com/38665 } -// TODO(jrg): for now this is brute force. - (void)nodeMoved:(BookmarkModel*)model oldParent:(const BookmarkNode*)oldParent oldIndex:(int)oldIndex newParent:(const BookmarkNode*)newParent newIndex:(int)newIndex { - [self loaded:model]; + 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]; + } } -// TODO(jrg): for now this is brute force. - (void)nodeAdded:(BookmarkModel*)model - parent:(const BookmarkNode*)oldParent index:(int)index { - [self loaded:model]; + parent:(const BookmarkNode*)newParent index:(int)newIndex { + const BookmarkNode* newNode = newParent->GetChild(newIndex); + id<BookmarkButtonControllerProtocol> newController = + [self controllerForNode:newParent]; + [newController addButtonForNode:newNode atIndex:newIndex]; } -// TODO(jrg): for now this is brute force. - (void)nodeRemoved:(BookmarkModel*)model parent:(const BookmarkNode*)oldParent index:(int)index { - [self loaded:model]; + // 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]; } // TODO(jrg): for now this is brute force. @@ -1722,7 +1757,7 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { [self loaded:model]; } -- (NSArray*)buttons { +- (NSMutableArray*)buttons { return buttons_.get(); } @@ -1916,4 +1951,205 @@ static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { [[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]; +} + +- (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 BookmarkButtonControllerProtocol + +- (void)addButtonForNode:(const BookmarkNode*)node + atIndex:(NSInteger)buttonIndex { + int newOffset = 0; + if (buttonIndex == -1) + buttonIndex = [buttons_ count]; // New button goes at the end. + if (buttonIndex <= (NSInteger)[buttons_ count]) { + if (buttonIndex) { + BookmarkButton* targetButton = [buttons_ objectAtIndex:buttonIndex - 1]; + NSRect targetFrame = [targetButton frame]; + newOffset = targetFrame.origin.x + NSWidth(targetFrame) + + bookmarks::kBookmarkHorizontalPadding; + } + BookmarkButton* newButton = [self buttonForNode:node xOffset:&newOffset]; + CGFloat xOffset = + NSWidth([newButton frame]) + bookmarks::kBookmarkHorizontalPadding; + NSUInteger buttonCount = [buttons_ count]; + for (NSUInteger i = buttonIndex; i < buttonCount; ++i) { + BookmarkButton* button = [buttons_ objectAtIndex:i]; + NSPoint buttonOrigin = [button frame].origin; + buttonOrigin.x += xOffset; + [button setFrameOrigin:buttonOrigin]; + } + ++displayedButtonCount_; + [buttons_ insertObject:newButton atIndex:buttonIndex]; + [buttonView_ addSubview:newButton]; + + // See if any buttons need to be pushed off to or brought in from the side. + [self reconfigureBookmarkBar]; + } else { + // A button from somewhere else (not the bar) is being moved to the + // off-the-side so insure it gets redrawn if its showing. + [self reconfigureBookmarkBar]; + [folderController_ reconfigureMenu]; + } +} + +// TODO(mrossetti): jrg wants this broken up into smaller functions. +- (void)moveButtonFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex { + if (fromIndex != toIndex) { + NSInteger buttonCount = (NSInteger)[buttons_ count]; + if (toIndex == -1) + toIndex = buttonCount; + // See if we have a simple move within the bar, which will be the case if + // both button indexes are in the visible space. + if (fromIndex < buttonCount && toIndex < buttonCount) { + BookmarkButton* movedButton = [buttons_ objectAtIndex:fromIndex]; + NSRect movedFrame = [movedButton frame]; + NSPoint toOrigin = movedFrame.origin; + CGFloat xOffset = + NSWidth(movedFrame) + bookmarks::kBookmarkHorizontalPadding; + // Hide the button to reduce flickering while drawing the window. + [movedButton setHidden:YES]; + [buttons_ removeObjectAtIndex:fromIndex]; + if (fromIndex < toIndex) { + // Move the button from left to right within the bar. + BookmarkButton* targetButton = [buttons_ objectAtIndex:toIndex - 1]; + NSRect toFrame = [targetButton frame]; + toOrigin.x = toFrame.origin.x - NSWidth(movedFrame) + NSWidth(toFrame); + for (NSInteger i = fromIndex; i < toIndex; ++i) { + BookmarkButton* button = [buttons_ objectAtIndex:i]; + NSRect frame = [button frame]; + frame.origin.x -= xOffset; + [button setFrameOrigin:frame.origin]; + } + } else { + // Move the button from right to left within the bar. + 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.x += xOffset; + [button setFrameOrigin:buttonFrame.origin]; + } + } + [buttons_ insertObject:movedButton atIndex:toIndex]; + [movedButton setFrameOrigin:toOrigin]; + [movedButton setHidden:NO]; + } else if (fromIndex < buttonCount) { + // A button is being removed from the bar and added to off-the-side. + // By now the node has already been inserted into the model so the + // button to be added is represented by |toIndex|. Things get + // complicated because the off-the-side is showing and must be redrawn + // while possibly re-laying out the bookmark bar. + [self removeButton:fromIndex animate:NO]; + [self reconfigureBookmarkBar]; + [folderController_ reconfigureMenu]; + } else if (toIndex < buttonCount) { + // A button is being added to the bar and removed from off-the-side. + // By now the node has already been inserted into the model so the + // button to be added is represented by |toIndex|. + const BookmarkNode* node = bookmarkModel_->GetBookmarkBarNode(); + const BookmarkNode* movedNode = node->GetChild(toIndex); + DCHECK(movedNode); + [self addButtonForNode:movedNode atIndex:toIndex]; + [self reconfigureBookmarkBar]; + } else { + // A button is being moved within the off-the-side. + fromIndex -= buttonCount; + toIndex -= buttonCount; + [folderController_ moveButtonFromIndex:fromIndex toIndex:toIndex]; + } + } +} + +- (void)removeButton:(NSInteger)buttonIndex animate:(BOOL)animate { + if (buttonIndex < (NSInteger)[buttons_ count]) { + 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]; + NSRect oldFrame = [oldButton frame]; + [oldButton removeFromSuperview]; + if (animate) + NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint, + NSZeroSize, nil, nil, nil); + CGFloat xOffset = NSWidth(oldFrame) + bookmarks::kBookmarkHorizontalPadding; + [buttons_ removeObjectAtIndex:buttonIndex]; + NSUInteger buttonCount = [buttons_ count]; + for (NSUInteger i = buttonIndex; i < buttonCount; ++i) { + BookmarkButton* button = [buttons_ objectAtIndex:i]; + NSRect buttonFrame = [button frame]; + buttonFrame.origin.x -= xOffset; + [button setFrame:buttonFrame]; + // If this button is showing its menu then we need to move the menu, too. + if (button == [folderController_ parentButton]) + [folderController_ offsetFolderMenuWindow:NSMakeSize(xOffset, 0.0)]; + } + --displayedButtonCount_; + [self reconfigureBookmarkBar]; + } +} + @end |