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" |
11 #include "chrome/browser/bookmarks/bookmark_model.h" | 11 #include "chrome/browser/bookmarks/bookmark_model.h" |
12 #include "chrome/browser/bookmarks/bookmark_utils.h" | 12 #include "chrome/browser/bookmarks/bookmark_utils.h" |
13 #include "chrome/browser/extensions/extension_service.h" | 13 #include "chrome/browser/extensions/extension_service.h" |
14 #include "chrome/browser/prefs/incognito_mode_prefs.h" | 14 #include "chrome/browser/prefs/incognito_mode_prefs.h" |
15 #include "chrome/browser/prefs/pref_service.h" | 15 #include "chrome/browser/prefs/pref_service.h" |
16 #include "chrome/browser/profiles/profile.h" | 16 #include "chrome/browser/profiles/profile.h" |
17 #import "chrome/browser/themes/theme_service.h" | 17 #import "chrome/browser/themes/theme_service.h" |
18 #import "chrome/browser/themes/theme_service_factory.h" | 18 #import "chrome/browser/themes/theme_service_factory.h" |
19 #include "chrome/browser/ui/browser.h" | 19 #include "chrome/browser/ui/browser.h" |
20 #include "chrome/browser/ui/browser_list.h" | 20 #include "chrome/browser/ui/browser_list.h" |
21 #import "chrome/browser/ui/cocoa/background_gradient_view.h" | 21 #import "chrome/browser/ui/cocoa/background_gradient_view.h" |
22 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_bridge.h" | 22 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_bridge.h" |
23 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.h" | 23 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.h" |
| 24 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h" |
24 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_toolbar_view.h" | 25 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_toolbar_view.h" |
25 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view.h" | 26 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view.h" |
26 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h" | 27 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h" |
27 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.h" | 28 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.h" |
28 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_editor_controller.h" | 29 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_editor_controller.h" |
29 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h" | 30 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h" |
30 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu.h" | 31 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu.h" |
31 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.h" | 32 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.h" |
32 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_name_folder_controller.h" | 33 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_name_folder_controller.h" |
33 #import "chrome/browser/ui/cocoa/browser_window_controller.h" | 34 #import "chrome/browser/ui/cocoa/browser_window_controller.h" |
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
203 | 204 |
204 - (void)addNode:(const BookmarkNode*)child toMenu:(NSMenu*)menu; | 205 - (void)addNode:(const BookmarkNode*)child toMenu:(NSMenu*)menu; |
205 - (void)addFolderNode:(const BookmarkNode*)node toMenu:(NSMenu*)menu; | 206 - (void)addFolderNode:(const BookmarkNode*)node toMenu:(NSMenu*)menu; |
206 - (void)tagEmptyMenu:(NSMenu*)menu; | 207 - (void)tagEmptyMenu:(NSMenu*)menu; |
207 - (void)clearMenuTagMap; | 208 - (void)clearMenuTagMap; |
208 - (int)preferredHeight; | 209 - (int)preferredHeight; |
209 - (void)addNonBookmarkButtonsToView; | 210 - (void)addNonBookmarkButtonsToView; |
210 - (void)addButtonsToView; | 211 - (void)addButtonsToView; |
211 - (void)centerNoItemsLabel; | 212 - (void)centerNoItemsLabel; |
212 - (void)setNodeForBarMenu; | 213 - (void)setNodeForBarMenu; |
| 214 - (void)watchForExitEvent:(BOOL)watch; |
213 - (void)resetAllButtonPositionsWithAnimation:(BOOL)animate; | 215 - (void)resetAllButtonPositionsWithAnimation:(BOOL)animate; |
214 - (BOOL)animationEnabled; | 216 - (BOOL)animationEnabled; |
215 | 217 |
216 @end | 218 @end |
217 | 219 |
218 @implementation BookmarkBarController | 220 @implementation BookmarkBarController |
219 | 221 |
220 @synthesize visualState = visualState_; | 222 @synthesize visualState = visualState_; |
221 @synthesize lastVisualState = lastVisualState_; | 223 @synthesize lastVisualState = lastVisualState_; |
222 @synthesize delegate = delegate_; | 224 @synthesize delegate = delegate_; |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
318 | 320 |
319 // For safety, make sure the buttons can no longer call us. | 321 // For safety, make sure the buttons can no longer call us. |
320 for (BookmarkButton* button in buttons_.get()) { | 322 for (BookmarkButton* button in buttons_.get()) { |
321 [button setDelegate:nil]; | 323 [button setDelegate:nil]; |
322 [button setTarget:nil]; | 324 [button setTarget:nil]; |
323 [button setAction:nil]; | 325 [button setAction:nil]; |
324 } | 326 } |
325 | 327 |
326 bridge_.reset(NULL); | 328 bridge_.reset(NULL); |
327 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 329 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 330 [self watchForExitEvent:NO]; |
328 [super dealloc]; | 331 [super dealloc]; |
329 } | 332 } |
330 | 333 |
331 - (void)awakeFromNib { | 334 - (void)awakeFromNib { |
332 // We default to NOT open, which means height=0. | 335 // We default to NOT open, which means height=0. |
333 DCHECK([[self view] isHidden]); // Hidden so it's OK to change. | 336 DCHECK([[self view] isHidden]); // Hidden so it's OK to change. |
334 | 337 |
335 // Set our initial height to zero, since that is what the superview | 338 // Set our initial height to zero, since that is what the superview |
336 // expects. We will resize ourselves open later if needed. | 339 // expects. We will resize ourselves open later if needed. |
337 [[self view] setFrame:NSMakeRect(0, 0, initialWidth_, 0)]; | 340 [[self view] setFrame:NSMakeRect(0, 0, initialWidth_, 0)]; |
(...skipping 21 matching lines...) Expand all Loading... |
359 | 362 |
360 // When resized we may need to add new buttons, or remove them (if | 363 // When resized we may need to add new buttons, or remove them (if |
361 // no longer visible), or add/remove the "off the side" menu. | 364 // no longer visible), or add/remove the "off the side" menu. |
362 [[self view] setPostsFrameChangedNotifications:YES]; | 365 [[self view] setPostsFrameChangedNotifications:YES]; |
363 [[NSNotificationCenter defaultCenter] | 366 [[NSNotificationCenter defaultCenter] |
364 addObserver:self | 367 addObserver:self |
365 selector:@selector(frameDidChange) | 368 selector:@selector(frameDidChange) |
366 name:NSViewFrameDidChangeNotification | 369 name:NSViewFrameDidChangeNotification |
367 object:[self view]]; | 370 object:[self view]]; |
368 | 371 |
| 372 // Watch for things going to or from fullscreen. |
| 373 [[NSNotificationCenter defaultCenter] |
| 374 addObserver:self |
| 375 selector:@selector(willEnterOrLeaveFullscreen:) |
| 376 name:kWillEnterFullscreenNotification |
| 377 object:nil]; |
| 378 [[NSNotificationCenter defaultCenter] |
| 379 addObserver:self |
| 380 selector:@selector(willEnterOrLeaveFullscreen:) |
| 381 name:kWillLeaveFullscreenNotification |
| 382 object:nil]; |
| 383 |
369 // Don't pass ourself along (as 'self') until our init is completely | 384 // Don't pass ourself along (as 'self') until our init is completely |
370 // done. Thus, this call is (almost) last. | 385 // done. Thus, this call is (almost) last. |
371 bridge_.reset(new BookmarkBarBridge(self, bookmarkModel_)); | 386 bridge_.reset(new BookmarkBarBridge(self, bookmarkModel_)); |
372 } | 387 } |
373 | 388 |
374 // Called by our main view (a BookmarkBarView) when it gets moved to a | 389 // Called by our main view (a BookmarkBarView) when it gets moved to a |
375 // window. We perform operations which need to know the relevant | 390 // window. We perform operations which need to know the relevant |
376 // window (e.g. watch for a window close) so they can't be performed | 391 // window (e.g. watch for a window close) so they can't be performed |
377 // earlier (such as in awakeFromNib). | 392 // earlier (such as in awakeFromNib). |
378 - (void)viewDidMoveToWindow { | 393 - (void)viewDidMoveToWindow { |
(...skipping 10 matching lines...) Expand all Loading... |
389 [defaultCenter addObserver:self | 404 [defaultCenter addObserver:self |
390 selector:@selector(parentWindowWillClose:) | 405 selector:@selector(parentWindowWillClose:) |
391 name:NSWindowWillCloseNotification | 406 name:NSWindowWillCloseNotification |
392 object:[[self view] window]]; | 407 object:[[self view] window]]; |
393 [defaultCenter addObserver:self | 408 [defaultCenter addObserver:self |
394 selector:@selector(parentWindowDidResignMain:) | 409 selector:@selector(parentWindowDidResignMain:) |
395 name:NSWindowDidResignMainNotification | 410 name:NSWindowDidResignMainNotification |
396 object:[[self view] window]]; | 411 object:[[self view] window]]; |
397 } | 412 } |
398 | 413 |
| 414 // When going fullscreen we can run into trouble. Our view is removed |
| 415 // from the non-fullscreen window before the non-fullscreen window |
| 416 // loses key, so our parentDidResignKey: callback never gets called. |
| 417 // In addition, a bookmark folder controller needs to be autoreleased |
| 418 // (in case it's in the event chain when closed), but the release |
| 419 // implicitly needs to happen while it's connected to the original |
| 420 // (non-fullscreen) window to "unlock bar visibility". Such a |
| 421 // contract isn't honored when going fullscreen with the menu option |
| 422 // (not with the keyboard shortcut). We fake it as best we can here. |
| 423 // We have a similar problem leaving fullscreen. |
| 424 - (void)willEnterOrLeaveFullscreen:(NSNotification*)notification { |
| 425 if (folderController_) { |
| 426 [self childFolderWillClose:folderController_]; |
| 427 [self closeFolderAndStopTrackingMenus]; |
| 428 } |
| 429 } |
| 430 |
399 // NSNotificationCenter callback. | 431 // NSNotificationCenter callback. |
400 - (void)parentWindowWillClose:(NSNotification*)notification { | 432 - (void)parentWindowWillClose:(NSNotification*)notification { |
401 [self closeFolderAndStopTrackingMenus]; | 433 [self closeFolderAndStopTrackingMenus]; |
402 } | 434 } |
403 | 435 |
404 // NSNotificationCenter callback. | 436 // NSNotificationCenter callback. |
405 - (void)parentWindowDidResignMain:(NSNotification*)notification { | 437 - (void)parentWindowDidResignMain:(NSNotification*)notification { |
406 [self closeFolderAndStopTrackingMenus]; | 438 [self closeFolderAndStopTrackingMenus]; |
407 } | 439 } |
408 | 440 |
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
509 if (node == nil || | 541 if (node == nil || |
510 node == bookmarkModel_->bookmark_bar_node() || | 542 node == bookmarkModel_->bookmark_bar_node() || |
511 node == bookmarkModel_->other_node() || | 543 node == bookmarkModel_->other_node() || |
512 node == bookmarkModel_->synced_node()) | 544 node == bookmarkModel_->synced_node()) |
513 return NO; | 545 return NO; |
514 return YES; | 546 return YES; |
515 } | 547 } |
516 | 548 |
517 #pragma mark Actions | 549 #pragma mark Actions |
518 | 550 |
| 551 // Helper methods called on the main thread by runMenuFlashThread. |
| 552 |
| 553 - (void)setButtonFlashStateOn:(id)sender { |
| 554 [sender highlight:YES]; |
| 555 } |
| 556 |
| 557 - (void)setButtonFlashStateOff:(id)sender { |
| 558 [sender highlight:NO]; |
| 559 } |
| 560 |
| 561 -(void)cleanupAfterMenuFlashThread:(id)sender { |
| 562 [self closeFolderAndStopTrackingMenus]; |
| 563 |
| 564 // Items retained by doMenuFlashOnSeparateThread below. |
| 565 [sender release]; |
| 566 [self release]; |
| 567 } |
| 568 |
| 569 // End runMenuFlashThread helper methods. |
| 570 |
| 571 // This call is invoked only by doMenuFlashOnSeparateThread below. |
| 572 // It makes the selected BookmarkButton (which is masquerading as a menu item) |
| 573 // flash a few times to give confirmation feedback, then it closes the menu. |
| 574 // It spends all its time sleeping or scheduling UI work on the main thread. |
| 575 - (void)runMenuFlashThread:(id)sender { |
| 576 |
| 577 // Check this is not running on the main thread, as it sleeps. |
| 578 DCHECK(![NSThread isMainThread]); |
| 579 |
| 580 // Duration of flash phases and number of flashes designed to evoke a |
| 581 // slightly retro "more mac-like than the Mac" feel. |
| 582 // Current Cocoa UI has a barely perceptible flash,probably because Apple |
| 583 // doesn't fire the action til after the animation and so there's a hurry. |
| 584 // As this code is fully asynchronous, it can take its time. |
| 585 const float kBBOnFlashTime = 0.08; |
| 586 const float kBBOffFlashTime = 0.08; |
| 587 const int kBookmarkButtonMenuFlashes = 3; |
| 588 |
| 589 for (int count = 0 ; count < kBookmarkButtonMenuFlashes ; count++) { |
| 590 [self performSelectorOnMainThread:@selector(setButtonFlashStateOn:) |
| 591 withObject:sender |
| 592 waitUntilDone:NO]; |
| 593 [NSThread sleepForTimeInterval:kBBOnFlashTime]; |
| 594 [self performSelectorOnMainThread:@selector(setButtonFlashStateOff:) |
| 595 withObject:sender |
| 596 waitUntilDone:NO]; |
| 597 [NSThread sleepForTimeInterval:kBBOffFlashTime]; |
| 598 } |
| 599 [self performSelectorOnMainThread:@selector(cleanupAfterMenuFlashThread:) |
| 600 withObject:sender |
| 601 waitUntilDone:NO]; |
| 602 } |
| 603 |
| 604 // Non-blocking call which starts the process to make the selected menu item |
| 605 // flash a few times to give confirmation feedback, after which it closes the |
| 606 // menu. The item is of course actually a BookmarkButton masquerading as a menu |
| 607 // item). |
| 608 - (void)doMenuFlashOnSeparateThread:(id)sender { |
| 609 |
| 610 // Ensure that self and sender don't go away before the animation completes. |
| 611 // These retains are balanced in cleanupAfterMenuFlashThread above. |
| 612 [self retain]; |
| 613 [sender retain]; |
| 614 [NSThread detachNewThreadSelector:@selector(runMenuFlashThread:) |
| 615 toTarget:self |
| 616 withObject:sender]; |
| 617 } |
| 618 |
519 - (IBAction)openBookmark:(id)sender { | 619 - (IBAction)openBookmark:(id)sender { |
520 BOOL isMenuItem = [sender isFolder]; | 620 BOOL isMenuItem = [[sender cell] isFolderButtonCell]; |
521 BOOL animate = isMenuItem && [self animationEnabled]; | 621 BOOL animate = isMenuItem && [self animationEnabled]; |
| 622 if (animate) |
| 623 [self doMenuFlashOnSeparateThread:sender]; |
522 DCHECK([sender respondsToSelector:@selector(bookmarkNode)]); | 624 DCHECK([sender respondsToSelector:@selector(bookmarkNode)]); |
523 const BookmarkNode* node = [sender bookmarkNode]; | 625 const BookmarkNode* node = [sender bookmarkNode]; |
524 WindowOpenDisposition disposition = | 626 WindowOpenDisposition disposition = |
525 event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); | 627 event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); |
526 RecordAppLaunch(browser_->profile(), node->url()); | 628 RecordAppLaunch(browser_->profile(), node->url()); |
527 [self openURL:node->url() disposition:disposition]; | 629 [self openURL:node->url() disposition:disposition]; |
528 | 630 |
529 if (!animate) | 631 if (!animate) |
530 [self closeFolderAndStopTrackingMenus]; | 632 [self closeFolderAndStopTrackingMenus]; |
531 } | 633 } |
532 | 634 |
533 // Common function to open a bookmark folder of any type. | 635 // Common function to open a bookmark folder of any type. |
534 - (void)openBookmarkFolder:(id)sender { | 636 - (void)openBookmarkFolder:(id)sender { |
535 DCHECK([sender isKindOfClass:[BookmarkButton class]]); | 637 DCHECK([sender isKindOfClass:[BookmarkButton class]]); |
536 DCHECK([[sender cell] isKindOfClass:[BookmarkButtonCell class]]); | 638 DCHECK([[sender cell] isKindOfClass:[BookmarkButtonCell class]]); |
537 | 639 |
538 showFolderMenus_ = !showFolderMenus_; | 640 showFolderMenus_ = !showFolderMenus_; |
539 | 641 |
| 642 if (sender == offTheSideButton_) |
| 643 [[sender cell] setStartingChildIndex:displayedButtonCount_]; |
| 644 |
540 // Toggle presentation of bar folder menus. | 645 // Toggle presentation of bar folder menus. |
541 [folderTarget_ openBookmarkFolderFromButton:sender]; | 646 [folderTarget_ openBookmarkFolderFromButton:sender]; |
542 } | 647 } |
543 | 648 |
544 | 649 |
545 // Click on a bookmark folder button. | 650 // Click on a bookmark folder button. |
546 - (IBAction)openBookmarkFolderFromButton:(id)sender { | 651 - (IBAction)openBookmarkFolderFromButton:(id)sender { |
547 [self openBookmarkFolder:sender]; | 652 [self openBookmarkFolder:sender]; |
548 } | 653 } |
549 | 654 |
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
747 bookmarks::kBookmarkHorizontalPadding)); | 852 bookmarks::kBookmarkHorizontalPadding)); |
748 } else { | 853 } else { |
749 frame.origin.x = (NSMaxX([otherBookmarksButton_ frame]) - frame.size.width); | 854 frame.origin.x = (NSMaxX([otherBookmarksButton_ frame]) - frame.size.width); |
750 } | 855 } |
751 [offTheSideButton_ setFrame:frame]; | 856 [offTheSideButton_ setFrame:frame]; |
752 } | 857 } |
753 | 858 |
754 // Configure the off-the-side button (e.g. specify the node range, | 859 // Configure the off-the-side button (e.g. specify the node range, |
755 // check if we should enable or disable it, etc). | 860 // check if we should enable or disable it, etc). |
756 - (void)configureOffTheSideButtonContentsAndVisibility { | 861 - (void)configureOffTheSideButtonContentsAndVisibility { |
| 862 // If deleting a button while off-the-side is open, buttons may be |
| 863 // promoted from off-the-side to the bar. Accomodate. |
| 864 if (folderController_ && |
| 865 ([folderController_ parentButton] == offTheSideButton_)) { |
| 866 [folderController_ reconfigureMenu]; |
| 867 } |
| 868 |
| 869 [[offTheSideButton_ cell] setStartingChildIndex:displayedButtonCount_]; |
757 [[offTheSideButton_ cell] | 870 [[offTheSideButton_ cell] |
758 setBookmarkNode:bookmarkModel_->bookmark_bar_node()]; | 871 setBookmarkNode:bookmarkModel_->bookmark_bar_node()]; |
759 int bookmarkChildren = bookmarkModel_->bookmark_bar_node()->child_count(); | 872 int bookmarkChildren = bookmarkModel_->bookmark_bar_node()->child_count(); |
760 if (bookmarkChildren > displayedButtonCount_) { | 873 if (bookmarkChildren > displayedButtonCount_) { |
761 [offTheSideButton_ setHidden:NO]; | 874 [offTheSideButton_ setHidden:NO]; |
762 } else { | 875 } else { |
763 // If we just deleted the last item in an off-the-side menu so the | 876 // If we just deleted the last item in an off-the-side menu so the |
764 // button will be going away, make sure the menu goes away. | 877 // button will be going away, make sure the menu goes away. |
765 if (folderController_ && | 878 if (folderController_ && |
766 ([folderController_ parentButton] == offTheSideButton_)) | 879 ([folderController_ parentButton] == offTheSideButton_)) |
767 [self closeAllBookmarkFolders]; | 880 [self closeAllBookmarkFolders]; |
768 // (And hide the button, too.) | 881 // (And hide the button, too.) |
769 [offTheSideButton_ setHidden:YES]; | 882 [offTheSideButton_ setHidden:YES]; |
770 } | 883 } |
771 } | 884 } |
772 | 885 |
| 886 // Main menubar observation code, so we can know to close our fake menus if the |
| 887 // user clicks on the actual menubar, as multiple unconnected menus sharing |
| 888 // the screen looks weird. |
| 889 // Needed because the hookForEvent method doesn't see the click on the menubar. |
| 890 |
| 891 // Gets called when the menubar is clicked. |
| 892 - (void)begunTracking:(NSNotification *)notification { |
| 893 [self closeFolderAndStopTrackingMenus]; |
| 894 } |
| 895 |
| 896 // Install the callback. |
| 897 - (void)startObservingMenubar { |
| 898 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; |
| 899 [nc addObserver:self |
| 900 selector:@selector(begunTracking:) |
| 901 name:NSMenuDidBeginTrackingNotification |
| 902 object:[NSApp mainMenu]]; |
| 903 } |
| 904 |
| 905 // Remove the callback. |
| 906 - (void)stopObservingMenubar { |
| 907 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; |
| 908 [nc removeObserver:self |
| 909 name:NSMenuDidBeginTrackingNotification |
| 910 object:[NSApp mainMenu]]; |
| 911 } |
| 912 |
| 913 // End of menubar observation code. |
| 914 |
| 915 // Begin (or end) watching for a click outside this window. Unlike |
| 916 // normal NSWindows, bookmark folder "fake menu" windows do not become |
| 917 // key or main. Thus, traditional notification (e.g. WillResignKey) |
| 918 // won't work. Our strategy is to watch (at the app level) for a |
| 919 // "click outside" these windows to detect when they logically lose |
| 920 // focus. |
| 921 - (void)watchForExitEvent:(BOOL)watch { |
| 922 CrApplication* app = static_cast<CrApplication*>([NSApplication |
| 923 sharedApplication]); |
| 924 DCHECK([app isKindOfClass:[CrApplication class]]); |
| 925 if (watch) { |
| 926 if (!watchingForExitEvent_) { |
| 927 [app addEventHook:self]; |
| 928 [self startObservingMenubar]; |
| 929 } |
| 930 } else { |
| 931 if (watchingForExitEvent_) { |
| 932 [app removeEventHook:self]; |
| 933 [self stopObservingMenubar]; |
| 934 } |
| 935 } |
| 936 watchingForExitEvent_ = watch; |
| 937 } |
| 938 |
773 // Keep the "no items" label centered in response to a frame size change. | 939 // Keep the "no items" label centered in response to a frame size change. |
774 - (void)centerNoItemsLabel { | 940 - (void)centerNoItemsLabel { |
775 // Note that this computation is done in the parent's coordinate system, | 941 // Note that this computation is done in the parent's coordinate system, |
776 // which is unflipped. Also, we want the label to be a fixed distance from | 942 // which is unflipped. Also, we want the label to be a fixed distance from |
777 // the bottom, so that it slides up properly (on animating to hidden). | 943 // the bottom, so that it slides up properly (on animating to hidden). |
778 // The textfield sits in the itemcontainer, so to center it we maintain | 944 // The textfield sits in the itemcontainer, so to center it we maintain |
779 // equal vertical padding on the top and bottom. | 945 // equal vertical padding on the top and bottom. |
780 int yoffset = (NSHeight([[buttonView_ noItemTextfield] frame]) - | 946 int yoffset = (NSHeight([[buttonView_ noItemTextfield] frame]) - |
781 NSHeight([[buttonView_ noItemContainer] frame])) / 2; | 947 NSHeight([[buttonView_ noItemContainer] frame])) / 2; |
782 [[buttonView_ noItemContainer] setFrameOrigin:NSMakePoint(0, yoffset)]; | 948 [[buttonView_ noItemContainer] setFrameOrigin:NSMakePoint(0, yoffset)]; |
(...skipping 851 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1634 NSColor* color = | 1800 NSColor* color = |
1635 themeProvider->GetNSColor(ThemeService::COLOR_BOOKMARK_TEXT, | 1801 themeProvider->GetNSColor(ThemeService::COLOR_BOOKMARK_TEXT, |
1636 true); | 1802 true); |
1637 for (BookmarkButton* button in buttons_.get()) { | 1803 for (BookmarkButton* button in buttons_.get()) { |
1638 BookmarkButtonCell* cell = [button cell]; | 1804 BookmarkButtonCell* cell = [button cell]; |
1639 [cell setTextColor:color]; | 1805 [cell setTextColor:color]; |
1640 } | 1806 } |
1641 [[otherBookmarksButton_ cell] setTextColor:color]; | 1807 [[otherBookmarksButton_ cell] setTextColor:color]; |
1642 } | 1808 } |
1643 | 1809 |
| 1810 // Return YES if the event indicates an exit from the bookmark bar |
| 1811 // folder menus. E.g. "click outside" of the area we are watching. |
| 1812 // At this time we are watching the area that includes all popup |
| 1813 // bookmark folder windows. |
| 1814 - (BOOL)isEventAnExitEvent:(NSEvent*)event { |
| 1815 NSWindow* eventWindow = [event window]; |
| 1816 NSWindow* myWindow = [[self view] window]; |
| 1817 switch ([event type]) { |
| 1818 case NSLeftMouseDown: |
| 1819 case NSRightMouseDown: |
| 1820 // If the click is in my window but NOT in the bookmark bar, consider |
| 1821 // it a click 'outside'. Clicks directly on an active button (i.e. one |
| 1822 // that is a folder and for which its folder menu is showing) are 'in'. |
| 1823 // All other clicks on the bookmarks bar are counted as 'outside' |
| 1824 // because they should close any open bookmark folder menu. |
| 1825 if (eventWindow == myWindow) { |
| 1826 NSView* hitView = |
| 1827 [[eventWindow contentView] hitTest:[event locationInWindow]]; |
| 1828 if (hitView == [folderController_ parentButton]) |
| 1829 return NO; |
| 1830 if (![hitView isDescendantOf:[self view]] || hitView == buttonView_) |
| 1831 return YES; |
| 1832 } |
| 1833 // If a click in a bookmark bar folder window and that isn't |
| 1834 // one of my bookmark bar folders, YES is click outside. |
| 1835 if (![eventWindow isKindOfClass:[BookmarkBarFolderWindow |
| 1836 class]]) { |
| 1837 return YES; |
| 1838 } |
| 1839 break; |
| 1840 case NSKeyDown: { |
| 1841 // Event hooks often see the same keydown event twice due to the way key |
| 1842 // events get dispatched and redispatched, so ignore if this keydown |
| 1843 // event has the EXACT same timestamp as the previous keydown. |
| 1844 static NSTimeInterval lastKeyDownEventTime; |
| 1845 NSTimeInterval thisTime = [event timestamp]; |
| 1846 if (lastKeyDownEventTime != thisTime) { |
| 1847 lastKeyDownEventTime = thisTime; |
| 1848 if ([event modifierFlags] & NSCommandKeyMask) |
| 1849 return YES; |
| 1850 else if (folderController_) |
| 1851 return [folderController_ handleInputText:[event characters]]; |
| 1852 } |
| 1853 return NO; |
| 1854 } |
| 1855 case NSKeyUp: |
| 1856 return NO; |
| 1857 case NSLeftMouseDragged: |
| 1858 // We can get here with the following sequence: |
| 1859 // - open a bookmark folder |
| 1860 // - right-click (and unclick) on it to open context menu |
| 1861 // - move mouse to window titlebar then click-drag it by the titlebar |
| 1862 // http://crbug.com/49333 |
| 1863 return NO; |
| 1864 default: |
| 1865 break; |
| 1866 } |
| 1867 return NO; |
| 1868 } |
| 1869 |
1644 #pragma mark Drag & Drop | 1870 #pragma mark Drag & Drop |
1645 | 1871 |
1646 // Find something like std::is_between<T>? I can't believe one doesn't exist. | 1872 // Find something like std::is_between<T>? I can't believe one doesn't exist. |
1647 static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { | 1873 static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { |
1648 return ((value >= low) && (value <= high)); | 1874 return ((value >= low) && (value <= high)); |
1649 } | 1875 } |
1650 | 1876 |
1651 // Return the proposed drop target for a hover open button from the | 1877 // Return the proposed drop target for a hover open button from the |
1652 // given array, or nil if none. We use this for distinguishing | 1878 // given array, or nil if none. We use this for distinguishing |
1653 // between a hover-open candidate or drop-indicator draw. | 1879 // between a hover-open candidate or drop-indicator draw. |
(...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1972 if (cellnode == node) { | 2198 if (cellnode == node) { |
1973 [[button cell] setBookmarkCellText:[button title] | 2199 [[button cell] setBookmarkCellText:[button title] |
1974 image:[self faviconForNode:node]]; | 2200 image:[self faviconForNode:node]]; |
1975 // Adding an image means we might need more room for the | 2201 // Adding an image means we might need more room for the |
1976 // bookmark. Test for it by growing the button (if needed) | 2202 // bookmark. Test for it by growing the button (if needed) |
1977 // and shifting everything else over. | 2203 // and shifting everything else over. |
1978 [self checkForBookmarkButtonGrowth:button]; | 2204 [self checkForBookmarkButtonGrowth:button]; |
1979 return; | 2205 return; |
1980 } | 2206 } |
1981 } | 2207 } |
| 2208 |
| 2209 if (folderController_) |
| 2210 [folderController_ faviconLoadedForNode:node]; |
1982 } | 2211 } |
1983 | 2212 |
1984 // TODO(jrg): for now this is brute force. | 2213 // TODO(jrg): for now this is brute force. |
1985 - (void)nodeChildrenReordered:(BookmarkModel*)model | 2214 - (void)nodeChildrenReordered:(BookmarkModel*)model |
1986 node:(const BookmarkNode*)node { | 2215 node:(const BookmarkNode*)node { |
1987 [self loaded:model]; | 2216 [self loaded:model]; |
1988 } | 2217 } |
1989 | 2218 |
1990 #pragma mark BookmarkBarState Protocol | 2219 #pragma mark BookmarkBarState Protocol |
1991 | 2220 |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2080 // a subfolder menu. | 2309 // a subfolder menu. |
2081 if (!showFolderMenus_) | 2310 if (!showFolderMenus_) |
2082 return; | 2311 return; |
2083 | 2312 |
2084 // From here down: same logic as BookmarkBarFolderController. | 2313 // From here down: same logic as BookmarkBarFolderController. |
2085 // TODO(jrg): find a way to share these 4 non-comment lines? | 2314 // TODO(jrg): find a way to share these 4 non-comment lines? |
2086 // http://crbug.com/35966 | 2315 // http://crbug.com/35966 |
2087 // If already opened, then we exited but re-entered the button, so do nothing. | 2316 // If already opened, then we exited but re-entered the button, so do nothing. |
2088 if ([folderController_ parentButton] == sender) | 2317 if ([folderController_ parentButton] == sender) |
2089 return; | 2318 return; |
| 2319 // Else open a new one if it makes sense to do so. |
| 2320 if ([sender bookmarkNode]->is_folder()) { |
| 2321 // Update |hoverButton_| so that it corresponds to the open folder. |
| 2322 hoverButton_.reset([sender retain]); |
| 2323 [folderTarget_ openBookmarkFolderFromButton:sender]; |
| 2324 } else { |
| 2325 // We're over a non-folder bookmark so close any old folders. |
| 2326 [folderController_ close]; |
| 2327 folderController_ = nil; |
| 2328 } |
2090 } | 2329 } |
2091 | 2330 |
2092 // BookmarkButtonDelegate protocol implementation. | 2331 // BookmarkButtonDelegate protocol implementation. |
2093 - (void)mouseExitedButton:(id)sender event:(NSEvent*)event { | 2332 - (void)mouseExitedButton:(id)sender event:(NSEvent*)event { |
2094 // Don't care; do nothing. | 2333 // Don't care; do nothing. |
2095 // This is different behavior that the folder menus. | 2334 // This is different behavior that the folder menus. |
2096 } | 2335 } |
2097 | 2336 |
2098 - (NSWindow*)browserWindow { | 2337 - (NSWindow*)browserWindow { |
2099 return [[self view] window]; | 2338 return [[self view] window]; |
(...skipping 20 matching lines...) Expand all Loading... |
2120 [button setHidden:NO]; | 2359 [button setHidden:NO]; |
2121 [self resetAllButtonPositionsWithAnimation:YES]; | 2360 [self resetAllButtonPositionsWithAnimation:YES]; |
2122 } | 2361 } |
2123 | 2362 |
2124 | 2363 |
2125 #pragma mark BookmarkButtonControllerProtocol | 2364 #pragma mark BookmarkButtonControllerProtocol |
2126 | 2365 |
2127 // Close all bookmark folders. "Folder" here is the fake menu for | 2366 // Close all bookmark folders. "Folder" here is the fake menu for |
2128 // bookmark folders, not a button context menu. | 2367 // bookmark folders, not a button context menu. |
2129 - (void)closeAllBookmarkFolders { | 2368 - (void)closeAllBookmarkFolders { |
2130 [folderController_ closeMenu]; | 2369 [self watchForExitEvent:NO]; |
| 2370 [folderController_ close]; |
2131 folderController_ = nil; | 2371 folderController_ = nil; |
2132 } | 2372 } |
2133 | 2373 |
2134 - (void)closeBookmarkFolder:(id)sender { | 2374 - (void)closeBookmarkFolder:(id)sender { |
2135 // We're the top level, so close one means close them all. | 2375 // We're the top level, so close one means close them all. |
2136 [self closeAllBookmarkFolders]; | 2376 [self closeAllBookmarkFolders]; |
2137 } | 2377 } |
2138 | 2378 |
2139 - (BookmarkModel*)bookmarkModel { | 2379 - (BookmarkModel*)bookmarkModel { |
2140 return bookmarkModel_; | 2380 return bookmarkModel_; |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2175 target]]; | 2415 target]]; |
2176 // Unlike BookmarkBarFolderController, we do not delay the close | 2416 // Unlike BookmarkBarFolderController, we do not delay the close |
2177 // of the previous one. Given the lack of diagonal movement, | 2417 // of the previous one. Given the lack of diagonal movement, |
2178 // there is no need, and it feels awkward to do so. See | 2418 // there is no need, and it feels awkward to do so. See |
2179 // comments about kDragHoverCloseDelay in | 2419 // comments about kDragHoverCloseDelay in |
2180 // bookmark_bar_folder_controller.mm for more details. | 2420 // bookmark_bar_folder_controller.mm for more details. |
2181 [[hoverButton_ target] closeBookmarkFolder:hoverButton_]; | 2421 [[hoverButton_ target] closeBookmarkFolder:hoverButton_]; |
2182 hoverButton_.reset(); | 2422 hoverButton_.reset(); |
2183 } | 2423 } |
2184 hoverButton_.reset([button retain]); | 2424 hoverButton_.reset([button retain]); |
| 2425 DCHECK([[hoverButton_ target] |
| 2426 respondsToSelector:@selector(openBookmarkFolderFromButton:)]); |
| 2427 [[hoverButton_ target] |
| 2428 performSelector:@selector(openBookmarkFolderFromButton:) |
| 2429 withObject:hoverButton_ |
| 2430 afterDelay:bookmarks::kDragHoverOpenDelay |
| 2431 inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; |
2185 } | 2432 } |
2186 if (!button) { | 2433 if (!button) { |
2187 if (hoverButton_) { | 2434 if (hoverButton_) { |
| 2435 [NSObject cancelPreviousPerformRequestsWithTarget:[hoverButton_ target]]; |
2188 [[hoverButton_ target] closeBookmarkFolder:hoverButton_]; | 2436 [[hoverButton_ target] closeBookmarkFolder:hoverButton_]; |
2189 hoverButton_.reset(); | 2437 hoverButton_.reset(); |
2190 } | 2438 } |
2191 } | 2439 } |
2192 | 2440 |
2193 // Thrown away but kept to be consistent with the draggingEntered: interface. | 2441 // Thrown away but kept to be consistent with the draggingEntered: interface. |
2194 return NSDragOperationMove; | 2442 return NSDragOperationMove; |
2195 } | 2443 } |
2196 | 2444 |
2197 - (void)draggingExited:(id<NSDraggingInfo>)info { | 2445 - (void)draggingExited:(id<NSDraggingInfo>)info { |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2282 } else { | 2530 } else { |
2283 x = 0.5 * bookmarks::kBookmarkHorizontalPadding; | 2531 x = 0.5 * bookmarks::kBookmarkHorizontalPadding; |
2284 } | 2532 } |
2285 } else { | 2533 } else { |
2286 NOTREACHED(); | 2534 NOTREACHED(); |
2287 } | 2535 } |
2288 | 2536 |
2289 return x; | 2537 return x; |
2290 } | 2538 } |
2291 | 2539 |
| 2540 - (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child { |
| 2541 // If the bookmarkbar is not in detached mode, lock bar visibility, forcing |
| 2542 // the overlay to stay open when in fullscreen mode. |
| 2543 if (![self isInState:bookmarks::kDetachedState] && |
| 2544 ![self isAnimatingToState:bookmarks::kDetachedState]) { |
| 2545 BrowserWindowController* browserController = |
| 2546 [BrowserWindowController browserWindowControllerForView:[self view]]; |
| 2547 [browserController lockBarVisibilityForOwner:child |
| 2548 withAnimation:NO |
| 2549 delay:NO]; |
| 2550 } |
| 2551 } |
| 2552 |
| 2553 - (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child { |
| 2554 // Release bar visibility, allowing the overlay to close if in fullscreen |
| 2555 // mode. |
| 2556 BrowserWindowController* browserController = |
| 2557 [BrowserWindowController browserWindowControllerForView:[self view]]; |
| 2558 [browserController releaseBarVisibilityForOwner:child |
| 2559 withAnimation:NO |
| 2560 delay:NO]; |
| 2561 } |
| 2562 |
2292 // Add a new folder controller as triggered by the given folder button. | 2563 // Add a new folder controller as triggered by the given folder button. |
2293 - (void)addNewFolderControllerWithParentButton:(BookmarkButton*)parentButton { | 2564 - (void)addNewFolderControllerWithParentButton:(BookmarkButton*)parentButton { |
2294 | 2565 |
2295 // If doing a close/open, make sure the fullscreen chrome doesn't | 2566 // If doing a close/open, make sure the fullscreen chrome doesn't |
2296 // have a chance to begin animating away in the middle of things. | 2567 // have a chance to begin animating away in the middle of things. |
2297 BrowserWindowController* browserController = | 2568 BrowserWindowController* browserController = |
2298 [BrowserWindowController browserWindowControllerForView:[self view]]; | 2569 [BrowserWindowController browserWindowControllerForView:[self view]]; |
2299 // Confirm we're not re-locking with ourself as an owner before locking. | 2570 // Confirm we're not re-locking with ourself as an owner before locking. |
2300 DCHECK([browserController isBarVisibilityLockedForOwner:self] == NO); | 2571 DCHECK([browserController isBarVisibilityLockedForOwner:self] == NO); |
2301 [browserController lockBarVisibilityForOwner:self | 2572 [browserController lockBarVisibilityForOwner:self |
2302 withAnimation:NO | 2573 withAnimation:NO |
2303 delay:NO]; | 2574 delay:NO]; |
2304 | 2575 |
2305 if (folderController_) | 2576 if (folderController_) |
2306 [self closeAllBookmarkFolders]; | 2577 [self closeAllBookmarkFolders]; |
2307 | 2578 |
2308 // Folder controller, like many window controllers, owns itself. | 2579 // Folder controller, like many window controllers, owns itself. |
2309 folderController_ = | 2580 folderController_ = |
2310 [[BookmarkBarFolderController alloc] initWithParentButton:parentButton | 2581 [[BookmarkBarFolderController alloc] initWithParentButton:parentButton |
2311 bookmarkModel:bookmarkModel_ | 2582 parentController:nil |
2312 barController:self]; | 2583 barController:self]; |
2313 [folderController_ autorelease]; | 2584 [folderController_ showWindow:self]; |
2314 | 2585 |
2315 // If this is for the off-the-side menu, set the display count. | 2586 // Only BookmarkBarController has this; the |
2316 if (parentButton == offTheSideButton_) | 2587 // BookmarkBarFolderController does not. |
2317 [folderController_ setOffTheSideNodeStartIndex:displayedButtonCount_]; | 2588 [self watchForExitEvent:YES]; |
2318 | |
2319 [folderController_ openMenu]; | |
2320 | 2589 |
2321 // No longer need to hold the lock; the folderController_ now owns it. | 2590 // No longer need to hold the lock; the folderController_ now owns it. |
2322 [browserController releaseBarVisibilityForOwner:self | 2591 [browserController releaseBarVisibilityForOwner:self |
2323 withAnimation:NO | 2592 withAnimation:NO |
2324 delay:NO]; | 2593 delay:NO]; |
2325 } | 2594 } |
2326 | 2595 |
2327 - (void)openAll:(const BookmarkNode*)node | 2596 - (void)openAll:(const BookmarkNode*)node |
2328 disposition:(WindowOpenDisposition)disposition { | 2597 disposition:(WindowOpenDisposition)disposition { |
2329 [self closeFolderAndStopTrackingMenus]; | 2598 [self closeFolderAndStopTrackingMenus]; |
(...skipping 20 matching lines...) Expand all Loading... |
2350 ++displayedButtonCount_; | 2619 ++displayedButtonCount_; |
2351 [buttons_ insertObject:newButton atIndex:buttonIndex]; | 2620 [buttons_ insertObject:newButton atIndex:buttonIndex]; |
2352 [buttonView_ addSubview:newButton]; | 2621 [buttonView_ addSubview:newButton]; |
2353 [self resetAllButtonPositionsWithAnimation:NO]; | 2622 [self resetAllButtonPositionsWithAnimation:NO]; |
2354 // See if any buttons need to be pushed off to or brought in from the side. | 2623 // See if any buttons need to be pushed off to or brought in from the side. |
2355 [self reconfigureBookmarkBar]; | 2624 [self reconfigureBookmarkBar]; |
2356 } else { | 2625 } else { |
2357 // A button from somewhere else (not the bar) is being moved to the | 2626 // A button from somewhere else (not the bar) is being moved to the |
2358 // off-the-side so insure it gets redrawn if its showing. | 2627 // off-the-side so insure it gets redrawn if its showing. |
2359 [self reconfigureBookmarkBar]; | 2628 [self reconfigureBookmarkBar]; |
| 2629 [folderController_ reconfigureMenu]; |
2360 } | 2630 } |
2361 } | 2631 } |
2362 | 2632 |
2363 // TODO(mrossetti): Duplicate code with BookmarkBarFolderController. | 2633 // TODO(mrossetti): Duplicate code with BookmarkBarFolderController. |
2364 // http://crbug.com/35966 | 2634 // http://crbug.com/35966 |
2365 - (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point { | 2635 - (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point { |
2366 DCHECK([urls count] == [titles count]); | 2636 DCHECK([urls count] == [titles count]); |
2367 BOOL nodesWereAdded = NO; | 2637 BOOL nodesWereAdded = NO; |
2368 // Figure out where these new bookmarks nodes are to be added. | 2638 // Figure out where these new bookmarks nodes are to be added. |
2369 BookmarkButton* button = [self buttonForDroppingOnAtPoint:point]; | 2639 BookmarkButton* button = [self buttonForDroppingOnAtPoint:point]; |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2417 [movedButton setHidden:NO]; | 2687 [movedButton setHidden:NO]; |
2418 [self resetAllButtonPositionsWithAnimation:NO]; | 2688 [self resetAllButtonPositionsWithAnimation:NO]; |
2419 } else if (fromIndex < buttonCount) { | 2689 } else if (fromIndex < buttonCount) { |
2420 // A button is being removed from the bar and added to off-the-side. | 2690 // A button is being removed from the bar and added to off-the-side. |
2421 // By now the node has already been inserted into the model so the | 2691 // By now the node has already been inserted into the model so the |
2422 // button to be added is represented by |toIndex|. Things get | 2692 // button to be added is represented by |toIndex|. Things get |
2423 // complicated because the off-the-side is showing and must be redrawn | 2693 // complicated because the off-the-side is showing and must be redrawn |
2424 // while possibly re-laying out the bookmark bar. | 2694 // while possibly re-laying out the bookmark bar. |
2425 [self removeButton:fromIndex animate:NO]; | 2695 [self removeButton:fromIndex animate:NO]; |
2426 [self reconfigureBookmarkBar]; | 2696 [self reconfigureBookmarkBar]; |
| 2697 [folderController_ reconfigureMenu]; |
2427 } else if (toIndex < buttonCount) { | 2698 } else if (toIndex < buttonCount) { |
2428 // A button is being added to the bar and removed from off-the-side. | 2699 // A button is being added to the bar and removed from off-the-side. |
2429 // By now the node has already been inserted into the model so the | 2700 // By now the node has already been inserted into the model so the |
2430 // button to be added is represented by |toIndex|. | 2701 // button to be added is represented by |toIndex|. |
2431 const BookmarkNode* node = bookmarkModel_->bookmark_bar_node(); | 2702 const BookmarkNode* node = bookmarkModel_->bookmark_bar_node(); |
2432 const BookmarkNode* movedNode = node->GetChild(toIndex); | 2703 const BookmarkNode* movedNode = node->GetChild(toIndex); |
2433 DCHECK(movedNode); | 2704 DCHECK(movedNode); |
2434 [self addButtonForNode:movedNode atIndex:toIndex]; | 2705 [self addButtonForNode:movedNode atIndex:toIndex]; |
2435 [self reconfigureBookmarkBar]; | 2706 [self reconfigureBookmarkBar]; |
| 2707 } else { |
| 2708 // A button is being moved within the off-the-side. |
| 2709 fromIndex -= buttonCount; |
| 2710 toIndex -= buttonCount; |
| 2711 [folderController_ moveButtonFromIndex:fromIndex toIndex:toIndex]; |
2436 } | 2712 } |
2437 } | 2713 } |
2438 } | 2714 } |
2439 | 2715 |
2440 - (void)removeButton:(NSInteger)buttonIndex animate:(BOOL)animate { | 2716 - (void)removeButton:(NSInteger)buttonIndex animate:(BOOL)animate { |
2441 if (buttonIndex < (NSInteger)[buttons_ count]) { | 2717 if (buttonIndex < (NSInteger)[buttons_ count]) { |
2442 // The button being removed is showing in the bar. | 2718 // The button being removed is showing in the bar. |
2443 BookmarkButton* oldButton = [buttons_ objectAtIndex:buttonIndex]; | 2719 BookmarkButton* oldButton = [buttons_ objectAtIndex:buttonIndex]; |
2444 if (oldButton == [folderController_ parentButton]) { | 2720 if (oldButton == [folderController_ parentButton]) { |
2445 // If we are deleting a button whose folder is currently open, close it! | 2721 // If we are deleting a button whose folder is currently open, close it! |
2446 [self closeAllBookmarkFolders]; | 2722 [self closeAllBookmarkFolders]; |
2447 } | 2723 } |
2448 if (animate && !ignoreAnimations_ && [self isVisible] && | 2724 if (animate && !ignoreAnimations_ && [self isVisible] && |
2449 [[self browserWindow] isMainWindow]) { | 2725 [[self browserWindow] isMainWindow]) { |
2450 NSPoint poofPoint = [oldButton screenLocationForRemoveAnimation]; | 2726 NSPoint poofPoint = [oldButton screenLocationForRemoveAnimation]; |
2451 NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint, | 2727 NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint, |
2452 NSZeroSize, nil, nil, nil); | 2728 NSZeroSize, nil, nil, nil); |
2453 } | 2729 } |
2454 [oldButton setDelegate:nil]; | 2730 [oldButton setDelegate:nil]; |
2455 [oldButton removeFromSuperview]; | 2731 [oldButton removeFromSuperview]; |
2456 [buttons_ removeObjectAtIndex:buttonIndex]; | 2732 [buttons_ removeObjectAtIndex:buttonIndex]; |
2457 --displayedButtonCount_; | 2733 --displayedButtonCount_; |
2458 [self resetAllButtonPositionsWithAnimation:YES]; | 2734 [self resetAllButtonPositionsWithAnimation:YES]; |
2459 [self reconfigureBookmarkBar]; | 2735 [self reconfigureBookmarkBar]; |
| 2736 } else if (folderController_ && |
| 2737 [folderController_ parentButton] == offTheSideButton_) { |
| 2738 // The button being removed is in the OTS (off-the-side) and the OTS |
| 2739 // menu is showing so we need to remove the button. |
| 2740 NSInteger index = buttonIndex - displayedButtonCount_; |
| 2741 [folderController_ removeButton:index animate:YES]; |
2460 } | 2742 } |
2461 } | 2743 } |
2462 | 2744 |
2463 - (id<BookmarkButtonControllerProtocol>)controllerForNode: | 2745 - (id<BookmarkButtonControllerProtocol>)controllerForNode: |
2464 (const BookmarkNode*)node { | 2746 (const BookmarkNode*)node { |
2465 // See if it's in the bar, then if it is in the hierarchy of visible | 2747 // See if it's in the bar, then if it is in the hierarchy of visible |
2466 // folder menus. | 2748 // folder menus. |
2467 if (bookmarkModel_->bookmark_bar_node() == node) | 2749 if (bookmarkModel_->bookmark_bar_node() == node) |
2468 return self; | 2750 return self; |
2469 return nil; | 2751 return [folderController_ controllerForNode:node]; |
| 2752 } |
| 2753 |
| 2754 #pragma mark BookmarkButtonControllerProtocol |
| 2755 |
| 2756 // NOT an override of a standard Cocoa call made to NSViewControllers. |
| 2757 - (void)hookForEvent:(NSEvent*)theEvent { |
| 2758 if ([self isEventAnExitEvent:theEvent]) |
| 2759 [self closeFolderAndStopTrackingMenus]; |
2470 } | 2760 } |
2471 | 2761 |
2472 #pragma mark TestingAPI Only | 2762 #pragma mark TestingAPI Only |
2473 | 2763 |
2474 - (NSMenu*)buttonContextMenu { | 2764 - (NSMenu*)buttonContextMenu { |
2475 return buttonContextMenu_; | 2765 return buttonContextMenu_; |
2476 } | 2766 } |
2477 | 2767 |
2478 // Intentionally ignores ownership issues; used for testing and we try | 2768 // Intentionally ignores ownership issues; used for testing and we try |
2479 // to minimize touching the object passed in (likely a mock). | 2769 // to minimize touching the object passed in (likely a mock). |
2480 - (void)setButtonContextMenu:(id)menu { | 2770 - (void)setButtonContextMenu:(id)menu { |
2481 buttonContextMenu_ = menu; | 2771 buttonContextMenu_ = menu; |
2482 } | 2772 } |
2483 | 2773 |
2484 - (void)setIgnoreAnimations:(BOOL)ignore { | 2774 - (void)setIgnoreAnimations:(BOOL)ignore { |
2485 ignoreAnimations_ = ignore; | 2775 ignoreAnimations_ = ignore; |
2486 } | 2776 } |
2487 | 2777 |
2488 @end | 2778 @end |
OLD | NEW |