| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h" | 5 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h" |
| 6 | 6 |
| 7 #include "base/mac/mac_util.h" | 7 #include "base/mac/mac_util.h" |
| 8 #include "base/metrics/histogram.h" | 8 #include "base/metrics/histogram.h" |
| 9 #include "base/sys_string_conversions.h" | 9 #include "base/sys_string_conversions.h" |
| 10 #include "chrome/browser/bookmarks/bookmark_editor.h" | 10 #include "chrome/browser/bookmarks/bookmark_editor.h" |
| (...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 203 | 203 |
| 204 - (void)addNode:(const BookmarkNode*)child toMenu:(NSMenu*)menu; | 204 - (void)addNode:(const BookmarkNode*)child toMenu:(NSMenu*)menu; |
| 205 - (void)addFolderNode:(const BookmarkNode*)node toMenu:(NSMenu*)menu; | 205 - (void)addFolderNode:(const BookmarkNode*)node toMenu:(NSMenu*)menu; |
| 206 - (void)tagEmptyMenu:(NSMenu*)menu; | 206 - (void)tagEmptyMenu:(NSMenu*)menu; |
| 207 - (void)clearMenuTagMap; | 207 - (void)clearMenuTagMap; |
| 208 - (int)preferredHeight; | 208 - (int)preferredHeight; |
| 209 - (void)addNonBookmarkButtonsToView; | 209 - (void)addNonBookmarkButtonsToView; |
| 210 - (void)addButtonsToView; | 210 - (void)addButtonsToView; |
| 211 - (void)centerNoItemsLabel; | 211 - (void)centerNoItemsLabel; |
| 212 - (void)setNodeForBarMenu; | 212 - (void)setNodeForBarMenu; |
| 213 - (void)watchForExitEvent:(BOOL)watch; | |
| 214 - (void)resetAllButtonPositionsWithAnimation:(BOOL)animate; | 213 - (void)resetAllButtonPositionsWithAnimation:(BOOL)animate; |
| 215 - (BOOL)animationEnabled; | 214 - (BOOL)animationEnabled; |
| 216 | 215 |
| 217 @end | 216 @end |
| 218 | 217 |
| 219 @implementation BookmarkBarController | 218 @implementation BookmarkBarController |
| 220 | 219 |
| 221 @synthesize visualState = visualState_; | 220 @synthesize visualState = visualState_; |
| 222 @synthesize lastVisualState = lastVisualState_; | 221 @synthesize lastVisualState = lastVisualState_; |
| 223 @synthesize delegate = delegate_; | 222 @synthesize delegate = delegate_; |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 319 | 318 |
| 320 // For safety, make sure the buttons can no longer call us. | 319 // For safety, make sure the buttons can no longer call us. |
| 321 for (BookmarkButton* button in buttons_.get()) { | 320 for (BookmarkButton* button in buttons_.get()) { |
| 322 [button setDelegate:nil]; | 321 [button setDelegate:nil]; |
| 323 [button setTarget:nil]; | 322 [button setTarget:nil]; |
| 324 [button setAction:nil]; | 323 [button setAction:nil]; |
| 325 } | 324 } |
| 326 | 325 |
| 327 bridge_.reset(NULL); | 326 bridge_.reset(NULL); |
| 328 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 327 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 329 [self watchForExitEvent:NO]; | |
| 330 [super dealloc]; | 328 [super dealloc]; |
| 331 } | 329 } |
| 332 | 330 |
| 333 - (void)awakeFromNib { | 331 - (void)awakeFromNib { |
| 334 // We default to NOT open, which means height=0. | 332 // We default to NOT open, which means height=0. |
| 335 DCHECK([[self view] isHidden]); // Hidden so it's OK to change. | 333 DCHECK([[self view] isHidden]); // Hidden so it's OK to change. |
| 336 | 334 |
| 337 // Set our initial height to zero, since that is what the superview | 335 // Set our initial height to zero, since that is what the superview |
| 338 // expects. We will resize ourselves open later if needed. | 336 // expects. We will resize ourselves open later if needed. |
| 339 [[self view] setFrame:NSMakeRect(0, 0, initialWidth_, 0)]; | 337 [[self view] setFrame:NSMakeRect(0, 0, initialWidth_, 0)]; |
| (...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 512 node == bookmarkModel_->bookmark_bar_node() || | 510 node == bookmarkModel_->bookmark_bar_node() || |
| 513 node == bookmarkModel_->other_node() || | 511 node == bookmarkModel_->other_node() || |
| 514 node == bookmarkModel_->synced_node()) | 512 node == bookmarkModel_->synced_node()) |
| 515 return NO; | 513 return NO; |
| 516 return YES; | 514 return YES; |
| 517 } | 515 } |
| 518 | 516 |
| 519 #pragma mark Actions | 517 #pragma mark Actions |
| 520 | 518 |
| 521 - (IBAction)openBookmark:(id)sender { | 519 - (IBAction)openBookmark:(id)sender { |
| 522 BOOL isMenuItem = [[sender cell] isFolderButtonCell]; | 520 BOOL isMenuItem = [sender isFolder]; |
| 523 BOOL animate = isMenuItem && [self animationEnabled]; | 521 BOOL animate = isMenuItem && [self animationEnabled]; |
| 524 DCHECK([sender respondsToSelector:@selector(bookmarkNode)]); | 522 DCHECK([sender respondsToSelector:@selector(bookmarkNode)]); |
| 525 const BookmarkNode* node = [sender bookmarkNode]; | 523 const BookmarkNode* node = [sender bookmarkNode]; |
| 526 WindowOpenDisposition disposition = | 524 WindowOpenDisposition disposition = |
| 527 event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); | 525 event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); |
| 528 RecordAppLaunch(browser_->profile(), node->url()); | 526 RecordAppLaunch(browser_->profile(), node->url()); |
| 529 [self openURL:node->url() disposition:disposition]; | 527 [self openURL:node->url() disposition:disposition]; |
| 530 | 528 |
| 531 if (!animate) | 529 if (!animate) |
| 532 [self closeFolderAndStopTrackingMenus]; | 530 [self closeFolderAndStopTrackingMenus]; |
| (...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 772 // If we just deleted the last item in an off-the-side menu so the | 770 // If we just deleted the last item in an off-the-side menu so the |
| 773 // button will be going away, make sure the menu goes away. | 771 // button will be going away, make sure the menu goes away. |
| 774 if (folderController_ && | 772 if (folderController_ && |
| 775 ([folderController_ parentButton] == offTheSideButton_)) | 773 ([folderController_ parentButton] == offTheSideButton_)) |
| 776 [self closeAllBookmarkFolders]; | 774 [self closeAllBookmarkFolders]; |
| 777 // (And hide the button, too.) | 775 // (And hide the button, too.) |
| 778 [offTheSideButton_ setHidden:YES]; | 776 [offTheSideButton_ setHidden:YES]; |
| 779 } | 777 } |
| 780 } | 778 } |
| 781 | 779 |
| 782 // Main menubar observation code, so we can know to close our fake menus if the | |
| 783 // user clicks on the actual menubar, as multiple unconnected menus sharing | |
| 784 // the screen looks weird. | |
| 785 // Needed because the hookForEvent method doesn't see the click on the menubar. | |
| 786 | |
| 787 // Gets called when the menubar is clicked. | |
| 788 - (void)begunTracking:(NSNotification *)notification { | |
| 789 [self closeFolderAndStopTrackingMenus]; | |
| 790 } | |
| 791 | |
| 792 // Install the callback. | |
| 793 - (void)startObservingMenubar { | |
| 794 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; | |
| 795 [nc addObserver:self | |
| 796 selector:@selector(begunTracking:) | |
| 797 name:NSMenuDidBeginTrackingNotification | |
| 798 object:[NSApp mainMenu]]; | |
| 799 } | |
| 800 | |
| 801 // Remove the callback. | |
| 802 - (void)stopObservingMenubar { | |
| 803 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; | |
| 804 [nc removeObserver:self | |
| 805 name:NSMenuDidBeginTrackingNotification | |
| 806 object:[NSApp mainMenu]]; | |
| 807 } | |
| 808 | |
| 809 // End of menubar observation code. | |
| 810 | |
| 811 // Begin (or end) watching for a click outside this window. Unlike | |
| 812 // normal NSWindows, bookmark folder "fake menu" windows do not become | |
| 813 // key or main. Thus, traditional notification (e.g. WillResignKey) | |
| 814 // won't work. Our strategy is to watch (at the app level) for a | |
| 815 // "click outside" these windows to detect when they logically lose | |
| 816 // focus. | |
| 817 - (void)watchForExitEvent:(BOOL)watch { | |
| 818 CrApplication* app = static_cast<CrApplication*>([NSApplication | |
| 819 sharedApplication]); | |
| 820 DCHECK([app isKindOfClass:[CrApplication class]]); | |
| 821 if (watch) { | |
| 822 if (!watchingForExitEvent_) { | |
| 823 [app addEventHook:self]; | |
| 824 [self startObservingMenubar]; | |
| 825 } | |
| 826 } else { | |
| 827 if (watchingForExitEvent_) { | |
| 828 [app removeEventHook:self]; | |
| 829 [self stopObservingMenubar]; | |
| 830 } | |
| 831 } | |
| 832 watchingForExitEvent_ = watch; | |
| 833 } | |
| 834 | |
| 835 // Keep the "no items" label centered in response to a frame size change. | 780 // Keep the "no items" label centered in response to a frame size change. |
| 836 - (void)centerNoItemsLabel { | 781 - (void)centerNoItemsLabel { |
| 837 // Note that this computation is done in the parent's coordinate system, | 782 // Note that this computation is done in the parent's coordinate system, |
| 838 // which is unflipped. Also, we want the label to be a fixed distance from | 783 // which is unflipped. Also, we want the label to be a fixed distance from |
| 839 // the bottom, so that it slides up properly (on animating to hidden). | 784 // the bottom, so that it slides up properly (on animating to hidden). |
| 840 // The textfield sits in the itemcontainer, so to center it we maintain | 785 // The textfield sits in the itemcontainer, so to center it we maintain |
| 841 // equal vertical padding on the top and bottom. | 786 // equal vertical padding on the top and bottom. |
| 842 int yoffset = (NSHeight([[buttonView_ noItemTextfield] frame]) - | 787 int yoffset = (NSHeight([[buttonView_ noItemTextfield] frame]) - |
| 843 NSHeight([[buttonView_ noItemContainer] frame])) / 2; | 788 NSHeight([[buttonView_ noItemContainer] frame])) / 2; |
| 844 [[buttonView_ noItemContainer] setFrameOrigin:NSMakePoint(0, yoffset)]; | 789 [[buttonView_ noItemContainer] setFrameOrigin:NSMakePoint(0, yoffset)]; |
| (...skipping 851 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1696 NSColor* color = | 1641 NSColor* color = |
| 1697 themeProvider->GetNSColor(ThemeService::COLOR_BOOKMARK_TEXT, | 1642 themeProvider->GetNSColor(ThemeService::COLOR_BOOKMARK_TEXT, |
| 1698 true); | 1643 true); |
| 1699 for (BookmarkButton* button in buttons_.get()) { | 1644 for (BookmarkButton* button in buttons_.get()) { |
| 1700 BookmarkButtonCell* cell = [button cell]; | 1645 BookmarkButtonCell* cell = [button cell]; |
| 1701 [cell setTextColor:color]; | 1646 [cell setTextColor:color]; |
| 1702 } | 1647 } |
| 1703 [[otherBookmarksButton_ cell] setTextColor:color]; | 1648 [[otherBookmarksButton_ cell] setTextColor:color]; |
| 1704 } | 1649 } |
| 1705 | 1650 |
| 1706 // Return YES if the event indicates an exit from the bookmark bar | |
| 1707 // folder menus. E.g. "click outside" of the area we are watching. | |
| 1708 // At this time we are watching the area that includes all popup | |
| 1709 // bookmark folder windows. | |
| 1710 - (BOOL)isEventAnExitEvent:(NSEvent*)event { | |
| 1711 NSWindow* eventWindow = [event window]; | |
| 1712 NSWindow* myWindow = [[self view] window]; | |
| 1713 switch ([event type]) { | |
| 1714 case NSLeftMouseDown: | |
| 1715 case NSRightMouseDown: | |
| 1716 // If the click is in my window but NOT in the bookmark bar, consider | |
| 1717 // it a click 'outside'. Clicks directly on an active button (i.e. one | |
| 1718 // that is a folder and for which its folder menu is showing) are 'in'. | |
| 1719 // All other clicks on the bookmarks bar are counted as 'outside' | |
| 1720 // because they should close any open bookmark folder menu. | |
| 1721 if (eventWindow == myWindow) { | |
| 1722 NSView* hitView = | |
| 1723 [[eventWindow contentView] hitTest:[event locationInWindow]]; | |
| 1724 if (hitView == [folderController_ parentButton]) | |
| 1725 return NO; | |
| 1726 if (![hitView isDescendantOf:[self view]] || hitView == buttonView_) | |
| 1727 return YES; | |
| 1728 } | |
| 1729 break; | |
| 1730 case NSKeyDown: { | |
| 1731 // Event hooks often see the same keydown event twice due to the way key | |
| 1732 // events get dispatched and redispatched, so ignore if this keydown | |
| 1733 // event has the EXACT same timestamp as the previous keydown. | |
| 1734 static NSTimeInterval lastKeyDownEventTime; | |
| 1735 NSTimeInterval thisTime = [event timestamp]; | |
| 1736 if (lastKeyDownEventTime != thisTime) { | |
| 1737 lastKeyDownEventTime = thisTime; | |
| 1738 if ([event modifierFlags] & NSCommandKeyMask) | |
| 1739 return YES; | |
| 1740 } | |
| 1741 return NO; | |
| 1742 } | |
| 1743 case NSKeyUp: | |
| 1744 return NO; | |
| 1745 case NSLeftMouseDragged: | |
| 1746 // We can get here with the following sequence: | |
| 1747 // - open a bookmark folder | |
| 1748 // - right-click (and unclick) on it to open context menu | |
| 1749 // - move mouse to window titlebar then click-drag it by the titlebar | |
| 1750 // http://crbug.com/49333 | |
| 1751 return NO; | |
| 1752 default: | |
| 1753 break; | |
| 1754 } | |
| 1755 return NO; | |
| 1756 } | |
| 1757 | |
| 1758 #pragma mark Drag & Drop | 1651 #pragma mark Drag & Drop |
| 1759 | 1652 |
| 1760 // Find something like std::is_between<T>? I can't believe one doesn't exist. | 1653 // Find something like std::is_between<T>? I can't believe one doesn't exist. |
| 1761 static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { | 1654 static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
| 1762 return ((value >= low) && (value <= high)); | 1655 return ((value >= low) && (value <= high)); |
| 1763 } | 1656 } |
| 1764 | 1657 |
| 1765 // Return the proposed drop target for a hover open button from the | 1658 // Return the proposed drop target for a hover open button from the |
| 1766 // given array, or nil if none. We use this for distinguishing | 1659 // given array, or nil if none. We use this for distinguishing |
| 1767 // between a hover-open candidate or drop-indicator draw. | 1660 // between a hover-open candidate or drop-indicator draw. |
| (...skipping 466 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2234 [button setHidden:NO]; | 2127 [button setHidden:NO]; |
| 2235 [self resetAllButtonPositionsWithAnimation:YES]; | 2128 [self resetAllButtonPositionsWithAnimation:YES]; |
| 2236 } | 2129 } |
| 2237 | 2130 |
| 2238 | 2131 |
| 2239 #pragma mark BookmarkButtonControllerProtocol | 2132 #pragma mark BookmarkButtonControllerProtocol |
| 2240 | 2133 |
| 2241 // Close all bookmark folders. "Folder" here is the fake menu for | 2134 // Close all bookmark folders. "Folder" here is the fake menu for |
| 2242 // bookmark folders, not a button context menu. | 2135 // bookmark folders, not a button context menu. |
| 2243 - (void)closeAllBookmarkFolders { | 2136 - (void)closeAllBookmarkFolders { |
| 2244 [self watchForExitEvent:NO]; | |
| 2245 [folderController_ closeMenu]; | 2137 [folderController_ closeMenu]; |
| 2246 folderController_ = nil; | 2138 folderController_ = nil; |
| 2247 } | 2139 } |
| 2248 | 2140 |
| 2249 - (void)closeBookmarkFolder:(id)sender { | 2141 - (void)closeBookmarkFolder:(id)sender { |
| 2250 // We're the top level, so close one means close them all. | 2142 // We're the top level, so close one means close them all. |
| 2251 [self closeAllBookmarkFolders]; | 2143 [self closeAllBookmarkFolders]; |
| 2252 } | 2144 } |
| 2253 | 2145 |
| 2254 - (BookmarkModel*)bookmarkModel { | 2146 - (BookmarkModel*)bookmarkModel { |
| (...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2421 [self closeAllBookmarkFolders]; | 2313 [self closeAllBookmarkFolders]; |
| 2422 | 2314 |
| 2423 // Folder controller, like many window controllers, owns itself. | 2315 // Folder controller, like many window controllers, owns itself. |
| 2424 folderController_ = | 2316 folderController_ = |
| 2425 [[BookmarkBarFolderController alloc] initWithParentButton:parentButton | 2317 [[BookmarkBarFolderController alloc] initWithParentButton:parentButton |
| 2426 bookmarkModel:bookmarkModel_ | 2318 bookmarkModel:bookmarkModel_ |
| 2427 barController:self]; | 2319 barController:self]; |
| 2428 [folderController_ autorelease]; | 2320 [folderController_ autorelease]; |
| 2429 [folderController_ openMenu]; | 2321 [folderController_ openMenu]; |
| 2430 | 2322 |
| 2431 // Only BookmarkBarController has this; the | |
| 2432 // BookmarkBarFolderController does not. | |
| 2433 [self watchForExitEvent:YES]; | |
| 2434 | |
| 2435 // No longer need to hold the lock; the folderController_ now owns it. | 2323 // No longer need to hold the lock; the folderController_ now owns it. |
| 2436 [browserController releaseBarVisibilityForOwner:self | 2324 [browserController releaseBarVisibilityForOwner:self |
| 2437 withAnimation:NO | 2325 withAnimation:NO |
| 2438 delay:NO]; | 2326 delay:NO]; |
| 2439 } | 2327 } |
| 2440 | 2328 |
| 2441 - (void)openAll:(const BookmarkNode*)node | 2329 - (void)openAll:(const BookmarkNode*)node |
| 2442 disposition:(WindowOpenDisposition)disposition { | 2330 disposition:(WindowOpenDisposition)disposition { |
| 2443 [self closeFolderAndStopTrackingMenus]; | 2331 [self closeFolderAndStopTrackingMenus]; |
| 2444 bookmark_utils::OpenAll([[self view] window], | 2332 bookmark_utils::OpenAll([[self view] window], |
| (...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2576 | 2464 |
| 2577 - (id<BookmarkButtonControllerProtocol>)controllerForNode: | 2465 - (id<BookmarkButtonControllerProtocol>)controllerForNode: |
| 2578 (const BookmarkNode*)node { | 2466 (const BookmarkNode*)node { |
| 2579 // See if it's in the bar, then if it is in the hierarchy of visible | 2467 // See if it's in the bar, then if it is in the hierarchy of visible |
| 2580 // folder menus. | 2468 // folder menus. |
| 2581 if (bookmarkModel_->bookmark_bar_node() == node) | 2469 if (bookmarkModel_->bookmark_bar_node() == node) |
| 2582 return self; | 2470 return self; |
| 2583 return nil; | 2471 return nil; |
| 2584 } | 2472 } |
| 2585 | 2473 |
| 2586 #pragma mark BookmarkButtonControllerProtocol | |
| 2587 | |
| 2588 // NOT an override of a standard Cocoa call made to NSViewControllers. | |
| 2589 - (void)hookForEvent:(NSEvent*)theEvent { | |
| 2590 if ([self isEventAnExitEvent:theEvent]) | |
| 2591 [self closeFolderAndStopTrackingMenus]; | |
| 2592 } | |
| 2593 | |
| 2594 #pragma mark TestingAPI Only | 2474 #pragma mark TestingAPI Only |
| 2595 | 2475 |
| 2596 - (NSMenu*)buttonContextMenu { | 2476 - (NSMenu*)buttonContextMenu { |
| 2597 return buttonContextMenu_; | 2477 return buttonContextMenu_; |
| 2598 } | 2478 } |
| 2599 | 2479 |
| 2600 // Intentionally ignores ownership issues; used for testing and we try | 2480 // Intentionally ignores ownership issues; used for testing and we try |
| 2601 // to minimize touching the object passed in (likely a mock). | 2481 // to minimize touching the object passed in (likely a mock). |
| 2602 - (void)setButtonContextMenu:(id)menu { | 2482 - (void)setButtonContextMenu:(id)menu { |
| 2603 buttonContextMenu_ = menu; | 2483 buttonContextMenu_ = menu; |
| 2604 } | 2484 } |
| 2605 | 2485 |
| 2606 - (void)setIgnoreAnimations:(BOOL)ignore { | 2486 - (void)setIgnoreAnimations:(BOOL)ignore { |
| 2607 ignoreAnimations_ = ignore; | 2487 ignoreAnimations_ = ignore; |
| 2608 } | 2488 } |
| 2609 | 2489 |
| 2610 @end | 2490 @end |
| OLD | NEW |