diff options
Diffstat (limited to 'chrome/browser/cocoa/bookmarks')
71 files changed, 0 insertions, 16684 deletions
diff --git a/chrome/browser/cocoa/bookmarks/bookmark_all_tabs_controller.h b/chrome/browser/cocoa/bookmarks/bookmark_all_tabs_controller.h deleted file mode 100644 index 4c6a57c..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_all_tabs_controller.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_ALL_TABS_CONTROLLER_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_ALL_TABS_CONTROLLER_H_ -#pragma once - -#include <utility> -#include <vector> - -#include "base/string16.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_editor_base_controller.h" - -// A list of pairs containing the name and URL associated with each -// currently active tab in the active browser window. -typedef std::pair<string16, GURL> ActiveTabNameURLPair; -typedef std::vector<ActiveTabNameURLPair> ActiveTabsNameURLPairVector; - -// A controller for the Bookmark All Tabs sheet which is presented upon -// selecting the Bookmark All Tabs... menu item shown by the contextual -// menu in the bookmarks bar. -@interface BookmarkAllTabsController : BookmarkEditorBaseController { - @private - ActiveTabsNameURLPairVector activeTabPairsVector_; -} - -- (id)initWithParentWindow:(NSWindow*)parentWindow - profile:(Profile*)profile - parent:(const BookmarkNode*)parent - configuration:(BookmarkEditor::Configuration)configuration; - -@end - -@interface BookmarkAllTabsController(TestingAPI) - -// Initializes the list of all tab names and URLs. Overridden by unit test -// to provide canned test data. -- (void)UpdateActiveTabPairs; - -// Provides testing access to tab pairs list. -- (ActiveTabsNameURLPairVector*)activeTabPairsVector; - -@end - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_ALL_TABS_CONTROLLER_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_all_tabs_controller.mm b/chrome/browser/cocoa/bookmarks/bookmark_all_tabs_controller.mm deleted file mode 100644 index 961d37a..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_all_tabs_controller.mm +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_all_tabs_controller.h" - -#include "app/l10n_util_mac.h" -#include "base/string16.h" -#include "base/sys_string_conversions.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#include "chrome/browser/tab_contents/tab_contents.h" -#include "chrome/browser/tab_contents_wrapper.h" -#include "chrome/browser/tabs/tab_strip_model.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_list.h" -#include "grit/generated_resources.h" - -@implementation BookmarkAllTabsController - -- (id)initWithParentWindow:(NSWindow*)parentWindow - profile:(Profile*)profile - parent:(const BookmarkNode*)parent - configuration:(BookmarkEditor::Configuration)configuration { - NSString* nibName = @"BookmarkAllTabs"; - if ((self = [super initWithParentWindow:parentWindow - nibName:nibName - profile:profile - parent:parent - configuration:configuration])) { - } - return self; -} - -- (void)awakeFromNib { - [self setInitialName: - l10n_util::GetNSStringWithFixup(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME)]; - [super awakeFromNib]; -} - -#pragma mark Bookmark Editing - -- (void)UpdateActiveTabPairs { - activeTabPairsVector_.clear(); - Browser* browser = BrowserList::GetLastActive(); - TabStripModel* tabstrip_model = browser->tabstrip_model(); - const int tabCount = tabstrip_model->count(); - for (int i = 0; i < tabCount; ++i) { - TabContents* tc = tabstrip_model->GetTabContentsAt(i)->tab_contents(); - const string16 tabTitle = tc->GetTitle(); - const GURL& tabURL(tc->GetURL()); - ActiveTabNameURLPair tabPair(tabTitle, tabURL); - activeTabPairsVector_.push_back(tabPair); - } -} - -// Called by -[BookmarkEditorBaseController ok:]. Creates the container -// folder for the tabs and then the bookmarks in that new folder. -// Returns a BOOL as an NSNumber indicating that the commit may proceed. -- (NSNumber*)didCommit { - NSString* name = [[self displayName] stringByTrimmingCharactersInSet: - [NSCharacterSet newlineCharacterSet]]; - std::wstring newTitle = base::SysNSStringToWide(name); - const BookmarkNode* newParentNode = [self selectedNode]; - int newIndex = newParentNode->GetChildCount(); - // Create the new folder which will contain all of the tab URLs. - NSString* newFolderName = [self displayName]; - string16 newFolderString = base::SysNSStringToUTF16(newFolderName); - BookmarkModel* model = [self bookmarkModel]; - const BookmarkNode* newFolder = model->AddGroup(newParentNode, newIndex, - newFolderString); - // Get a list of all open tabs, create nodes for them, and add - // to the new folder node. - [self UpdateActiveTabPairs]; - int i = 0; - for (ActiveTabsNameURLPairVector::const_iterator it = - activeTabPairsVector_.begin(); - it != activeTabPairsVector_.end(); ++it, ++i) { - model->AddURL(newFolder, i, it->first, it->second); - } - return [NSNumber numberWithBool:YES]; -} - -- (ActiveTabsNameURLPairVector*)activeTabPairsVector { - return &activeTabPairsVector_; -} - -@end // BookmarkAllTabsController - diff --git a/chrome/browser/cocoa/bookmarks/bookmark_all_tabs_controller_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_all_tabs_controller_unittest.mm deleted file mode 100644 index 2fb42f0..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_all_tabs_controller_unittest.mm +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#include "base/scoped_nsobject.h" -#include "base/sys_string_conversions.h" -#include "base/utf_string_conversions.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_all_tabs_controller.h" -#include "chrome/browser/cocoa/browser_test_helper.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" - -@interface BookmarkAllTabsControllerOverride : BookmarkAllTabsController -@end - -@implementation BookmarkAllTabsControllerOverride - -- (void)UpdateActiveTabPairs { - ActiveTabsNameURLPairVector* activeTabPairsVector = - [self activeTabPairsVector]; - activeTabPairsVector->clear(); - activeTabPairsVector->push_back( - ActiveTabNameURLPair(ASCIIToUTF16("at-0"), GURL("http://at-0.com"))); - activeTabPairsVector->push_back( - ActiveTabNameURLPair(ASCIIToUTF16("at-1"), GURL("http://at-1.com"))); - activeTabPairsVector->push_back( - ActiveTabNameURLPair(ASCIIToUTF16("at-2"), GURL("http://at-2.com"))); -} - -@end - -class BookmarkAllTabsControllerTest : public CocoaTest { - public: - BrowserTestHelper helper_; - const BookmarkNode* parent_node_; - BookmarkAllTabsControllerOverride* controller_; - const BookmarkNode* group_a_; - - BookmarkAllTabsControllerTest() { - BookmarkModel& model(*(helper_.profile()->GetBookmarkModel())); - const BookmarkNode* root = model.GetBookmarkBarNode(); - group_a_ = model.AddGroup(root, 0, ASCIIToUTF16("a")); - model.AddURL(group_a_, 0, ASCIIToUTF16("a-0"), GURL("http://a-0.com")); - model.AddURL(group_a_, 1, ASCIIToUTF16("a-1"), GURL("http://a-1.com")); - model.AddURL(group_a_, 2, ASCIIToUTF16("a-2"), GURL("http://a-2.com")); - } - - virtual BookmarkAllTabsControllerOverride* CreateController() { - return [[BookmarkAllTabsControllerOverride alloc] - initWithParentWindow:test_window() - profile:helper_.profile() - parent:group_a_ - configuration:BookmarkEditor::SHOW_TREE]; - } - - virtual void SetUp() { - CocoaTest::SetUp(); - controller_ = CreateController(); - [controller_ runAsModalSheet]; - } - - virtual void TearDown() { - controller_ = NULL; - CocoaTest::TearDown(); - } -}; - -TEST_F(BookmarkAllTabsControllerTest, BookmarkAllTabs) { - // OK button should always be enabled. - EXPECT_TRUE([controller_ okButtonEnabled]); - [controller_ selectTestNodeInBrowser:group_a_]; - [controller_ setDisplayName:@"ALL MY TABS"]; - [controller_ ok:nil]; - EXPECT_EQ(4, group_a_->GetChildCount()); - const BookmarkNode* folderChild = group_a_->GetChild(3); - EXPECT_EQ(folderChild->GetTitle(), ASCIIToUTF16("ALL MY TABS")); - EXPECT_EQ(3, folderChild->GetChildCount()); -} diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_bridge.h b/chrome/browser/cocoa/bookmarks/bookmark_bar_bridge.h deleted file mode 100644 index a0bc1a7..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_bridge.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// C++ bridge class between Chromium and Cocoa to connect the -// Bookmarks (model) with the Bookmark Bar (view). -// -// There is exactly one BookmarkBarBridge per BookmarkBarController / -// BrowserWindowController / Browser. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_BRIDGE_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_BRIDGE_H_ -#pragma once - -#include "base/basictypes.h" -#include "chrome/browser/bookmarks/bookmark_model_observer.h" - -class Browser; -@class BookmarkBarController; - -class BookmarkBarBridge : public BookmarkModelObserver { - public: - BookmarkBarBridge(BookmarkBarController* controller, - BookmarkModel* model); - virtual ~BookmarkBarBridge(); - - // Overridden from BookmarkModelObserver - virtual void Loaded(BookmarkModel* model); - virtual void BookmarkModelBeingDeleted(BookmarkModel* model); - virtual void BookmarkNodeMoved(BookmarkModel* model, - const BookmarkNode* old_parent, - int old_index, - const BookmarkNode* new_parent, - int new_index); - virtual void BookmarkNodeAdded(BookmarkModel* model, - const BookmarkNode* parent, - int index); - virtual void BookmarkNodeRemoved(BookmarkModel* model, - const BookmarkNode* parent, - int old_index, - const BookmarkNode* node); - virtual void BookmarkNodeChanged(BookmarkModel* model, - const BookmarkNode* node); - virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model, - const BookmarkNode* node); - virtual void BookmarkNodeChildrenReordered(BookmarkModel* model, - const BookmarkNode* node); - - virtual void BookmarkImportBeginning(BookmarkModel* model); - virtual void BookmarkImportEnding(BookmarkModel* model); - - private: - BookmarkBarController* controller_; // weak; owns me - BookmarkModel* model_; // weak; it is owned by a Profile. - bool batch_mode_; - - DISALLOW_COPY_AND_ASSIGN(BookmarkBarBridge); -}; - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_BRIDGE_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_bridge.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_bridge.mm deleted file mode 100644 index dc38b91..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_bridge.mm +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/cocoa/bookmarks/bookmark_bar_bridge.h" - -#include "chrome/browser/bookmarks/bookmark_model.h" -#include "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" - -BookmarkBarBridge::BookmarkBarBridge(BookmarkBarController* controller, - BookmarkModel* model) - : controller_(controller), - model_(model), - batch_mode_(false) { - model_->AddObserver(this); - - // Bookmark loading is async; it may may not have happened yet. - // We will be notified when that happens with the AddObserver() call. - if (model->IsLoaded()) - Loaded(model); -} - -BookmarkBarBridge::~BookmarkBarBridge() { - model_->RemoveObserver(this); -} - -void BookmarkBarBridge::Loaded(BookmarkModel* model) { - [controller_ loaded:model]; -} - -void BookmarkBarBridge::BookmarkModelBeingDeleted(BookmarkModel* model) { - [controller_ beingDeleted:model]; -} - -void BookmarkBarBridge::BookmarkNodeMoved(BookmarkModel* model, - const BookmarkNode* old_parent, - int old_index, - const BookmarkNode* new_parent, - int new_index) { - [controller_ nodeMoved:model - oldParent:old_parent oldIndex:old_index - newParent:new_parent newIndex:new_index]; -} - -void BookmarkBarBridge::BookmarkNodeAdded(BookmarkModel* model, - const BookmarkNode* parent, - int index) { - if (!batch_mode_) { - [controller_ nodeAdded:model parent:parent index:index]; - } -} - -void BookmarkBarBridge::BookmarkNodeRemoved(BookmarkModel* model, - const BookmarkNode* parent, - int old_index, - const BookmarkNode* node) { - [controller_ nodeRemoved:model parent:parent index:old_index]; -} - -void BookmarkBarBridge::BookmarkNodeChanged(BookmarkModel* model, - const BookmarkNode* node) { - [controller_ nodeChanged:model node:node]; -} - -void BookmarkBarBridge::BookmarkNodeFavIconLoaded(BookmarkModel* model, - const BookmarkNode* node) { - [controller_ nodeFavIconLoaded:model node:node]; -} - -void BookmarkBarBridge::BookmarkNodeChildrenReordered( - BookmarkModel* model, const BookmarkNode* node) { - [controller_ nodeChildrenReordered:model node:node]; -} - -void BookmarkBarBridge::BookmarkImportBeginning(BookmarkModel* model) { - batch_mode_ = true; -} - -void BookmarkBarBridge::BookmarkImportEnding(BookmarkModel* model) { - batch_mode_ = false; - [controller_ loaded:model]; -} diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_bridge_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_bridge_unittest.mm deleted file mode 100644 index 567761e..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_bridge_unittest.mm +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/cocoa/bookmarks/bookmark_bar_bridge.h" -#include "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" -#include "chrome/browser/cocoa/browser_test_helper.h" -#include "chrome/browser/cocoa/cocoa_test_helper.h" -#include "testing/gtest/include/gtest/gtest.h" -#import "testing/gtest_mac.h" -#include "testing/platform_test.h" - -// TODO(jrg): use OCMock. - -namespace { - -// Information needed to open a URL, as passed to the -// BookmarkBarController's delegate. -typedef std::pair<GURL,WindowOpenDisposition> OpenInfo; - -} // The namespace must end here -- I need to use OpenInfo in - // FakeBookmarkBarController but can't place - // FakeBookmarkBarController itself in the namespace ("error: - // Objective-C declarations may only appear in global scope") - -// Oddly, we are our own delegate. -@interface FakeBookmarkBarController : BookmarkBarController { - @public - scoped_nsobject<NSMutableArray> callbacks_; - std::vector<OpenInfo> opens_; -} -@end - -@implementation FakeBookmarkBarController - -- (id)initWithBrowser:(Browser*)browser { - if ((self = [super initWithBrowser:browser - initialWidth:100 // arbitrary - delegate:nil - resizeDelegate:nil])) { - callbacks_.reset([[NSMutableArray alloc] init]); - } - return self; -} - -- (void)loaded:(BookmarkModel*)model { - [callbacks_ addObject:[NSNumber numberWithInt:0]]; -} - -- (void)beingDeleted:(BookmarkModel*)model { - [callbacks_ addObject:[NSNumber numberWithInt:1]]; -} - -- (void)nodeMoved:(BookmarkModel*)model - oldParent:(const BookmarkNode*)oldParent oldIndex:(int)oldIndex - newParent:(const BookmarkNode*)newParent newIndex:(int)newIndex { - [callbacks_ addObject:[NSNumber numberWithInt:2]]; -} - -- (void)nodeAdded:(BookmarkModel*)model - parent:(const BookmarkNode*)oldParent index:(int)index { - [callbacks_ addObject:[NSNumber numberWithInt:3]]; -} - -- (void)nodeChanged:(BookmarkModel*)model - node:(const BookmarkNode*)node { - [callbacks_ addObject:[NSNumber numberWithInt:4]]; -} - -- (void)nodeFavIconLoaded:(BookmarkModel*)model - node:(const BookmarkNode*)node { - [callbacks_ addObject:[NSNumber numberWithInt:5]]; -} - -- (void)nodeChildrenReordered:(BookmarkModel*)model - node:(const BookmarkNode*)node { - [callbacks_ addObject:[NSNumber numberWithInt:6]]; -} - -- (void)nodeRemoved:(BookmarkModel*)model - parent:(const BookmarkNode*)oldParent index:(int)index { - [callbacks_ addObject:[NSNumber numberWithInt:7]]; -} - -// Save the request. -- (void)openBookmarkURL:(const GURL&)url - disposition:(WindowOpenDisposition)disposition { - opens_.push_back(OpenInfo(url, disposition)); -} - -@end - - -class BookmarkBarBridgeTest : public CocoaTest { - public: - BrowserTestHelper browser_test_helper_; -}; - -// Call all the callbacks; make sure they are all redirected to the objc object. -TEST_F(BookmarkBarBridgeTest, TestRedirect) { - Browser* browser = browser_test_helper_.browser(); - Profile* profile = browser_test_helper_.profile(); - BookmarkModel* model = profile->GetBookmarkModel(); - - scoped_nsobject<NSView> parentView([[NSView alloc] - initWithFrame:NSMakeRect(0,0,100,100)]); - scoped_nsobject<NSView> webView([[NSView alloc] - initWithFrame:NSMakeRect(0,0,100,100)]); - scoped_nsobject<NSView> infoBarsView( - [[NSView alloc] initWithFrame:NSMakeRect(0,0,100,100)]); - - scoped_nsobject<FakeBookmarkBarController> - controller([[FakeBookmarkBarController alloc] initWithBrowser:browser]); - EXPECT_TRUE(controller.get()); - scoped_ptr<BookmarkBarBridge> bridge(new BookmarkBarBridge(controller.get(), - model)); - EXPECT_TRUE(bridge.get()); - - bridge->Loaded(NULL); - bridge->BookmarkModelBeingDeleted(NULL); - bridge->BookmarkNodeMoved(NULL, NULL, 0, NULL, 0); - bridge->BookmarkNodeAdded(NULL, NULL, 0); - bridge->BookmarkNodeChanged(NULL, NULL); - bridge->BookmarkNodeFavIconLoaded(NULL, NULL); - bridge->BookmarkNodeChildrenReordered(NULL, NULL); - bridge->BookmarkNodeRemoved(NULL, NULL, 0, NULL); - - // 8 calls above plus an initial Loaded() in init routine makes 9 - EXPECT_TRUE([controller.get()->callbacks_ count] == 9); - - for (int x = 1; x < 9; x++) { - NSNumber* num = [NSNumber numberWithInt:x-1]; - EXPECT_NSEQ(num, [controller.get()->callbacks_ objectAtIndex:x]); - } -} diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_constants.h b/chrome/browser/cocoa/bookmarks/bookmark_bar_constants.h deleted file mode 100644 index d1be28f..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_constants.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Constants used for positioning the bookmark bar. These aren't placed in a -// different file because they're conditionally included in cross platform code -// and thus no Objective-C++ stuff. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_CONSTANTS_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_CONSTANTS_H_ -#pragma once - -namespace bookmarks { - -// Correction used for computing other values based on the height. -const int kVisualHeightOffset = 2; - -// Bar height, when opened in "always visible" mode. This is actually a little -// smaller than it should be (by |kVisualHeightOffset| points) because of the -// visual overlap with the main toolbar. When using this to compute values -// other than the actual height of the toolbar, be sure to add -// |kVisualHeightOffset|. -const int kBookmarkBarHeight = 26; - -// Our height, when visible in "new tab page" mode. -const int kNTPBookmarkBarHeight = 40; - -// The amount of space between the inner bookmark bar and the outer toolbar on -// new tab pages. -const int kNTPBookmarkBarPadding = - (kNTPBookmarkBarHeight - (kBookmarkBarHeight + kVisualHeightOffset)) / 2; - -// The height of buttons in the bookmark bar. -const int kBookmarkButtonHeight = kBookmarkBarHeight + kVisualHeightOffset; - -} // namespace bookmarks - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_CONSTANTS_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h b/chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h deleted file mode 100644 index 6d0a0f6..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h +++ /dev/null @@ -1,399 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_CONTROLLER_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_CONTROLLER_H_ -#pragma once - -#import <Cocoa/Cocoa.h> -#include <map> - -#import "base/chrome_application_mac.h" -#include "base/scoped_nsobject.h" -#include "base/scoped_ptr.h" -#include "chrome/browser/cocoa/bookmarks/bookmark_bar_bridge.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_constants.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_state.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_toolbar_view.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button.h" -#include "chrome/browser/cocoa/tab_strip_model_observer_bridge.h" -#include "webkit/glue/window_open_disposition.h" - -@class BookmarkBarController; -@class BookmarkBarFolderController; -@class BookmarkBarView; -@class BookmarkButton; -@class BookmarkButtonCell; -@class BookmarkFolderTarget; -class BookmarkModel; -@class BookmarkMenu; -class BookmarkNode; -class Browser; -class GURL; -class PrefService; -class TabContents; -@class ToolbarController; -@protocol ViewResizer; - -namespace bookmarks { - -// Magic numbers from Cole -// TODO(jrg): create an objc-friendly version of bookmark_bar_constants.h? - -// Used as a maximum width for buttons on the bar. -const CGFloat kDefaultBookmarkWidth = 150.0; - -// Horizontal frame inset for buttons in the bookmark bar. -const CGFloat kBookmarkHorizontalPadding = 1.0; - -// Vertical frame inset for buttons in the bookmark bar. -const CGFloat kBookmarkVerticalPadding = 2.0; - -// Used as a min/max width for buttons on menus (not on the bar). -const CGFloat kBookmarkMenuButtonMinimumWidth = 100.0; -const CGFloat kBookmarkMenuButtonMaximumWidth = 485.0; - -// Horizontal separation between a menu button and both edges of its menu. -const CGFloat kBookmarkSubMenuHorizontalPadding = 5.0; - -// TODO(mrossetti): Add constant (kBookmarkVerticalSeparation) for the gap -// between buttons in a folder menu. Right now we're using -// kBookmarkVerticalPadding, which is dual purpose and wrong. -// http://crbug.com/59057 - -// Convenience constant giving the vertical distance from the top extent of one -// folder button to the next button. -const CGFloat kBookmarkButtonVerticalSpan = - kBookmarkButtonHeight + kBookmarkVerticalPadding; - -// The minimum separation between a folder menu and the edge of the screen. -// If the menu gets closer to the edge of the screen (either right or left) -// then it is pops up in the opposite direction. -// (See -[BookmarkBarFolderController childFolderWindowLeftForWidth:]). -const CGFloat kBookmarkHorizontalScreenPadding = 8.0; - -// Our NSScrollView is supposed to be just barely big enough to fit its -// contentView. It is actually a hair too small. -// This turns on horizontal scrolling which, although slight, is awkward. -// Make sure our window (and NSScrollView) are wider than its documentView -// by at least this much. -const CGFloat kScrollViewContentWidthMargin = 2; - -// Make subfolder menus overlap their parent menu a bit to give a better -// perception of a menuing system. -const CGFloat kBookmarkMenuOverlap = 5.0; - -// Delay before opening a subfolder (and closing the previous one) -// when hovering over a folder button. -const NSTimeInterval kHoverOpenDelay = 0.3; - -// Delay on hover before a submenu opens when dragging. -// Experimentally a drag hover open delay needs to be bigger than a -// normal (non-drag) menu hover open such as used in the bookmark folder. -// TODO(jrg): confirm feel of this constant with ui-team. -// http://crbug.com/36276 -const NSTimeInterval kDragHoverOpenDelay = 0.7; - -// Notes on use of kDragHoverCloseDelay in -// -[BookmarkBarFolderController draggingEntered:]. -// -// We have an implicit delay on stop-hover-open before a submenu -// closes. This cannot be zero since it's nice to move the mouse in a -// direct line from "current position" to "position of item in -// submenu". However, by doing so, it's possible to overlap a -// different button on the current menu. Example: -// -// Folder1 -// Folder2 ---> Sub1 -// Folder3 Sub2 -// Sub3 -// -// If you hover over the F in Folder2 to open the sub, and then want to -// select Sub3, a direct line movement of the mouse may cross over -// Folder3. Without this delay, that'll cause Sub to be closed before -// you get there, since a "hover over" of Folder3 gets activated. -// It's subtle but without the delay it feels broken. -// -// This is only really a problem with vertical menu --> vertical menu -// movement; the bookmark bar (horizontal menu, sort of) seems fine, -// perhaps because mouse move direction is purely vertical so there is -// no opportunity for overlap. -const NSTimeInterval kDragHoverCloseDelay = 0.4; - -} // namespace bookmarks - -// The interface for the bookmark bar controller's delegate. Currently, the -// delegate is the BWC and is responsible for ensuring that the toolbar is -// displayed correctly (as specified by |-getDesiredToolbarHeightCompression| -// and |-toolbarDividerOpacity|) at the beginning and at the end of an animation -// (or after a state change). -@protocol BookmarkBarControllerDelegate - -// Sent when the state has changed (after any animation), but before the final -// display update. -- (void)bookmarkBar:(BookmarkBarController*)controller - didChangeFromState:(bookmarks::VisualState)oldState - toState:(bookmarks::VisualState)newState; - -// Sent before the animation begins. -- (void)bookmarkBar:(BookmarkBarController*)controller -willAnimateFromState:(bookmarks::VisualState)oldState - toState:(bookmarks::VisualState)newState; - -@end - -// A controller for the bookmark bar in the browser window. Handles showing -// and hiding based on the preference in the given profile. -@interface BookmarkBarController : - NSViewController<BookmarkBarState, - BookmarkBarToolbarViewController, - BookmarkButtonDelegate, - BookmarkButtonControllerProtocol, - CrApplicationEventHookProtocol, - NSUserInterfaceValidations> { - @private - // The visual state of the bookmark bar. If an animation is running, this is - // set to the "destination" and |lastVisualState_| is set to the "original" - // state. This is set to |kInvalidState| on initialization (when the - // appropriate state is not yet known). - bookmarks::VisualState visualState_; - - // The "original" state of the bookmark bar if an animation is running, - // otherwise it should be |kInvalidState|. - bookmarks::VisualState lastVisualState_; - - Browser* browser_; // weak; owned by its window - BookmarkModel* bookmarkModel_; // weak; part of the profile owned by the - // top-level Browser object. - - // Our initial view width, which is applied in awakeFromNib. - CGFloat initialWidth_; - - // BookmarkNodes have a 64bit id. NSMenuItems have a 32bit tag used - // to represent the bookmark node they refer to. This map provides - // a mapping from one to the other, so we can properly identify the - // node from the item. When adding items in, we start with seedId_. - int32 seedId_; - std::map<int32,int64> menuTagMap_; - - // Our bookmark buttons, ordered from L-->R. - scoped_nsobject<NSMutableArray> buttons_; - - // The folder image so we can use one copy for all buttons - scoped_nsobject<NSImage> folderImage_; - - // The default image, so we can use one copy for all buttons. - scoped_nsobject<NSImage> defaultImage_; - - // If the bar is disabled, we hide it and ignore show/hide commands. - // Set when using fullscreen mode. - BOOL barIsEnabled_; - - // Bridge from Chrome-style C++ notifications (e.g. derived from - // BookmarkModelObserver) - scoped_ptr<BookmarkBarBridge> bridge_; - - // Delegate that is informed about state changes in the bookmark bar. - id<BookmarkBarControllerDelegate> delegate_; // weak - - // Delegate that can resize us. - id<ViewResizer> resizeDelegate_; // weak - - // Logic for dealing with a click on a bookmark folder button. - scoped_nsobject<BookmarkFolderTarget> folderTarget_; - - // A controller for a pop-up bookmark folder window (custom menu). - // This is not a scoped_nsobject because it owns itself (when its - // window closes the controller gets autoreleased). - BookmarkBarFolderController* folderController_; - - // Are watching for a "click outside" or other event which would - // signal us to close the bookmark bar folder menus? - BOOL watchingForExitEvent_; - - IBOutlet BookmarkBarView* buttonView_; // Contains 'no items' text fields. - IBOutlet BookmarkButton* offTheSideButton_; // aka the chevron. - IBOutlet NSMenu* buttonContextMenu_; - - NSRect originalNoItemsRect_; // Original, pre-resized field rect. - NSRect originalImportBookmarksRect_; // Original, pre-resized field rect. - - // "Other bookmarks" button on the right side. - scoped_nsobject<BookmarkButton> otherBookmarksButton_; - - // We have a special menu for folder buttons. This starts as a copy - // of the bar menu. - scoped_nsobject<BookmarkMenu> buttonFolderContextMenu_; - - // When doing a drag, this is folder button "hovered over" which we - // may want to open after a short delay. There are cases where a - // mouse-enter can open a folder (e.g. if the menus are "active") - // but that doesn't use this variable or need a delay so "hover" is - // the wrong term. - scoped_nsobject<BookmarkButton> hoverButton_; - - // We save the view width when we add bookmark buttons. This lets - // us avoid a rebuild until we've grown the window bigger than our - // initial build. - CGFloat savedFrameWidth_; - - // The number of buttons we display in the bookmark bar. This does - // not include the "off the side" chevron or the "Other Bookmarks" - // button. We use this number to determine if we need to display - // the chevron, and to know what to place in the chevron's menu. - // Since we create everything before doing layout we can't be sure - // that all bookmark buttons we create will be visible. Thus, - // [buttons_ count] isn't a definitive check. - int displayedButtonCount_; - - // A state flag which tracks when the bar's folder menus should be shown. - // An initial click in any of the folder buttons turns this on and - // one of the following will turn it off: another click in the button, - // the window losing focus, a click somewhere other than in the bar - // or a folder menu. - BOOL showFolderMenus_; - - // Set to YES to prevent any node animations. Useful for unit testing so that - // incomplete animations do not cause valgrind complaints. - BOOL ignoreAnimations_; -} - -@property(readonly, nonatomic) bookmarks::VisualState visualState; -@property(readonly, nonatomic) bookmarks::VisualState lastVisualState; -@property(assign, nonatomic) id<BookmarkBarControllerDelegate> delegate; - -// Initializes the bookmark bar controller with the given browser -// profile and delegates. -- (id)initWithBrowser:(Browser*)browser - initialWidth:(CGFloat)initialWidth - delegate:(id<BookmarkBarControllerDelegate>)delegate - resizeDelegate:(id<ViewResizer>)resizeDelegate; - -// Updates the bookmark bar (from its current, possibly in-transition) state to -// the one appropriate for the new conditions. -- (void)updateAndShowNormalBar:(BOOL)showNormalBar - showDetachedBar:(BOOL)showDetachedBar - withAnimation:(BOOL)animate; - -// Update the visible state of the bookmark bar. -- (void)updateVisibility; - -// Turn on or off the bookmark bar and prevent or reallow its appearance. On -// disable, toggle off if shown. On enable, show only if needed. App and popup -// windows do not show a bookmark bar. -- (void)setBookmarkBarEnabled:(BOOL)enabled; - -// Returns the amount by which the toolbar above should be compressed. -- (CGFloat)getDesiredToolbarHeightCompression; - -// Gets the appropriate opacity for the toolbar's divider; 0 means that it -// shouldn't be shown. -- (CGFloat)toolbarDividerOpacity; - -// Updates the sizes and positions of the subviews. -// TODO(viettrungluu): I'm not convinced this should be public, but I currently -// need it for animations. Try not to propagate its use. -- (void)layoutSubviews; - -// Called by our view when it is moved to a window. -- (void)viewDidMoveToWindow; - -// Import bookmarks from another browser. -- (IBAction)importBookmarks:(id)sender; - -// Provide a favIcon for a bookmark node. May return nil. -- (NSImage*)favIconForNode:(const BookmarkNode*)node; - -// Used for situations where the bookmark bar folder menus should no longer -// be actively popping up. Called when the window loses focus, a click has -// occured outside the menus or a bookmark has been activated. (Note that this -// differs from the behavior of the -[BookmarkButtonControllerProtocol -// closeAllBookmarkFolders] method in that the latter does not terminate menu -// tracking since it may be being called in response to actions (such as -// dragging) where a 'stale' menu presentation should first be collapsed before -// presenting a new menu.) -- (void)closeFolderAndStopTrackingMenus; - -// Checks if operations such as edit or delete are allowed. -- (BOOL)canEditBookmark:(const BookmarkNode*)node; - -// Actions for manipulating bookmarks. -// Open a normal bookmark or folder from a button, ... -- (IBAction)openBookmark:(id)sender; -- (IBAction)openBookmarkFolderFromButton:(id)sender; -// From the "off the side" button, ... -- (IBAction)openOffTheSideFolderFromButton:(id)sender; -// From a context menu over the button, ... -- (IBAction)openBookmarkInNewForegroundTab:(id)sender; -- (IBAction)openBookmarkInNewWindow:(id)sender; -- (IBAction)openBookmarkInIncognitoWindow:(id)sender; -- (IBAction)editBookmark:(id)sender; -- (IBAction)cutBookmark:(id)sender; -- (IBAction)copyBookmark:(id)sender; -- (IBAction)pasteBookmark:(id)sender; -- (IBAction)deleteBookmark:(id)sender; -// From a context menu over the bar, ... -- (IBAction)openAllBookmarks:(id)sender; -- (IBAction)openAllBookmarksNewWindow:(id)sender; -- (IBAction)openAllBookmarksIncognitoWindow:(id)sender; -// Or from a context menu over either the bar or a button. -- (IBAction)addPage:(id)sender; -- (IBAction)addFolder:(id)sender; - -@end - -// Redirects from BookmarkBarBridge, the C++ object which glues us to -// the rest of Chromium. Internal to BookmarkBarController. -@interface BookmarkBarController(BridgeRedirect) -- (void)loaded:(BookmarkModel*)model; -- (void)beingDeleted:(BookmarkModel*)model; -- (void)nodeAdded:(BookmarkModel*)model - parent:(const BookmarkNode*)oldParent index:(int)index; -- (void)nodeChanged:(BookmarkModel*)model - node:(const BookmarkNode*)node; -- (void)nodeMoved:(BookmarkModel*)model - oldParent:(const BookmarkNode*)oldParent oldIndex:(int)oldIndex - newParent:(const BookmarkNode*)newParent newIndex:(int)newIndex; -- (void)nodeRemoved:(BookmarkModel*)model - parent:(const BookmarkNode*)oldParent index:(int)index; -- (void)nodeFavIconLoaded:(BookmarkModel*)model - node:(const BookmarkNode*)node; -- (void)nodeChildrenReordered:(BookmarkModel*)model - node:(const BookmarkNode*)node; -@end - -// These APIs should only be used by unit tests (or used internally). -@interface BookmarkBarController(InternalOrTestingAPI) -- (BookmarkBarView*)buttonView; -- (NSMutableArray*)buttons; -- (NSMenu*)offTheSideMenu; -- (NSButton*)offTheSideButton; -- (BOOL)offTheSideButtonIsHidden; -- (BookmarkButton*)otherBookmarksButton; -- (BookmarkBarFolderController*)folderController; -- (id)folderTarget; -- (int)displayedButtonCount; -- (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition; -- (void)clearBookmarkBar; -- (BookmarkButtonCell*)cellForBookmarkNode:(const BookmarkNode*)node; -- (NSRect)frameForBookmarkButtonFromCell:(NSCell*)cell xOffset:(int*)xOffset; -- (void)checkForBookmarkButtonGrowth:(NSButton*)button; -- (void)frameDidChange; -- (int64)nodeIdFromMenuTag:(int32)tag; -- (int32)menuTagFromNodeId:(int64)menuid; -- (const BookmarkNode*)nodeFromMenuItem:(id)sender; -- (void)updateTheme:(ThemeProvider*)themeProvider; -- (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point; -- (BOOL)isEventAnExitEvent:(NSEvent*)event; -- (BOOL)shrinkOrHideView:(NSView*)view forMaxX:(CGFloat)maxViewX; - -// The following are for testing purposes only and are not used internally. -- (NSMenu *)menuForFolderNode:(const BookmarkNode*)node; -- (NSMenu*)buttonContextMenu; -- (void)setButtonContextMenu:(id)menu; -// Set to YES in order to prevent animations. -- (void)setIgnoreAnimations:(BOOL)ignore; -@end - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_CONTROLLER_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_controller.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_controller.mm deleted file mode 100644 index 3ba0fc9..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_controller.mm +++ /dev/null @@ -1,2497 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" - -#include "app/l10n_util_mac.h" -#include "app/resource_bundle.h" -#include "base/mac_util.h" -#include "base/sys_string_conversions.h" -#include "chrome/browser/bookmarks/bookmark_editor.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#include "chrome/browser/bookmarks/bookmark_utils.h" -#import "chrome/browser/cocoa/background_gradient_view.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_bridge.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_window.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_toolbar_view.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_view.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button_cell.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_editor_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_folder_target.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_menu.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_name_folder_controller.h" -#import "chrome/browser/cocoa/browser_window_controller.h" -#import "chrome/browser/cocoa/event_utils.h" -#import "chrome/browser/cocoa/fullscreen_controller.h" -#import "chrome/browser/cocoa/import_settings_dialog.h" -#import "chrome/browser/cocoa/menu_button.h" -#import "chrome/browser/cocoa/themed_window.h" -#import "chrome/browser/cocoa/toolbar_controller.h" -#import "chrome/browser/cocoa/view_id_util.h" -#import "chrome/browser/cocoa/view_resizer.h" -#include "chrome/browser/metrics/user_metrics.h" -#include "chrome/browser/prefs/pref_service.h" -#include "chrome/browser/profile.h" -#include "chrome/browser/tab_contents/tab_contents.h" -#include "chrome/browser/tab_contents/tab_contents_view.h" -#import "chrome/browser/themes/browser_theme_provider.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_list.h" -#include "chrome/common/pref_names.h" -#include "grit/app_resources.h" -#include "grit/generated_resources.h" -#include "grit/theme_resources.h" -#include "skia/ext/skia_utils_mac.h" - -// Bookmark bar state changing and animations -// -// The bookmark bar has three real states: "showing" (a normal bar attached to -// the toolbar), "hidden", and "detached" (pretending to be part of the web -// content on the NTP). It can, or at least should be able to, animate between -// these states. There are several complications even without animation: -// - The placement of the bookmark bar is done by the BWC, and it needs to know -// the state in order to place the bookmark bar correctly (immediately below -// the toolbar when showing, below the infobar when detached). -// - The "divider" (a black line) needs to be drawn by either the toolbar (when -// the bookmark bar is hidden or detached) or by the bookmark bar (when it is -// showing). It should not be drawn by both. -// - The toolbar needs to vertically "compress" when the bookmark bar is -// showing. This ensures the proper display of both the bookmark bar and the -// toolbar, and gives a padded area around the bookmark bar items for right -// clicks, etc. -// -// Our model is that the BWC controls us and also the toolbar. We try not to -// talk to the browser nor the toolbar directly, instead centralizing control in -// the BWC. The key method by which the BWC controls us is -// |-updateAndShowNormalBar:showDetachedBar:withAnimation:|. This invokes state -// changes, and at appropriate times we request that the BWC do things for us -// via either the resize delegate or our general delegate. If the BWC needs any -// information about what it should do, or tell the toolbar to do, it can then -// query us back (e.g., |-isShownAs...|, |-getDesiredToolbarHeightCompression|, -// |-toolbarDividerOpacity|, etc.). -// -// Animation-related complications: -// - Compression of the toolbar is touchy during animation. It must not be -// compressed while the bookmark bar is animating to/from showing (from/to -// hidden), otherwise it would look like the bookmark bar's contents are -// sliding out of the controls inside the toolbar. As such, we have to make -// sure that the bookmark bar is shown at the right location and at the -// right height (at various points in time). -// - Showing the divider is also complicated during animation between hidden -// and showing. We have to make sure that the toolbar does not show the -// divider despite the fact that it's not compressed. The exception to this -// is at the beginning/end of the animation when the toolbar is still -// uncompressed but the bookmark bar has height 0. If we're not careful, we -// get a flicker at this point. -// - We have to ensure that we do the right thing if we're told to change state -// while we're running an animation. The generic/easy thing to do is to jump -// to the end state of our current animation, and (if the new state change -// again involves an animation) begin the new animation. We can do better -// than that, however, and sometimes just change the current animation to go -// to the new end state (e.g., by "reversing" the animation in the showing -> -// hidden -> showing case). We also have to ensure that demands to -// immediately change state are always honoured. -// -// Pointers to animation logic: -// - |-moveToVisualState:withAnimation:| starts animations, deciding which ones -// we know how to handle. -// - |-doBookmarkBarAnimation| has most of the actual logic. -// - |-getDesiredToolbarHeightCompression| and |-toolbarDividerOpacity| contain -// related logic. -// - The BWC's |-layoutSubviews| needs to know how to position things. -// - The BWC should implement |-bookmarkBar:didChangeFromState:toState:| and -// |-bookmarkBar:willAnimateFromState:toState:| in order to inform the -// toolbar of required changes. - -namespace { - -// Overlap (in pixels) between the toolbar and the bookmark bar (when showing in -// normal mode). -const CGFloat kBookmarkBarOverlap = 3.0; - -// Duration of the bookmark bar animations. -const NSTimeInterval kBookmarkBarAnimationDuration = 0.12; - -} // namespace - -@interface BookmarkBarController(Private) - -// Determines the appropriate state for the given situation. -+ (bookmarks::VisualState)visualStateToShowNormalBar:(BOOL)showNormalBar - showDetachedBar:(BOOL)showDetachedBar; - -// Moves to the given next state (from the current state), possibly animating. -// If |animate| is NO, it will stop any running animation and jump to the given -// state. If YES, it may either (depending on implementation) jump to the end of -// the current animation and begin the next one, or stop the current animation -// mid-flight and animate to the next state. -- (void)moveToVisualState:(bookmarks::VisualState)nextVisualState - withAnimation:(BOOL)animate; - -// Return the backdrop to the bookmark bar as various types. -- (BackgroundGradientView*)backgroundGradientView; -- (AnimatableView*)animatableView; - -// Create buttons for all items in the given bookmark node tree. -// 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; - -// Stops any current animation in its tracks (midway). -- (void)stopCurrentAnimation; - -// Show/hide the bookmark bar. -// if |animate| is YES, the changes are made using the animator; otherwise they -// are made immediately. -- (void)showBookmarkBarWithAnimation:(BOOL)animate; - -// Handles animating the resize of the content view. Returns YES if it handled -// the animation, NO if not (and hence it should be done instantly). -- (BOOL)doBookmarkBarAnimation; - -// |point| is in the base coordinate system of the destination window; -// it comes from an id<NSDraggingInfo>. |copy| is YES if a copy is to be -// made and inserted into the new location while leaving the bookmark in -// the old location, otherwise move the bookmark by removing from its old -// location and inserting into the new location. -- (BOOL)dragBookmark:(const BookmarkNode*)sourceNode - to:(NSPoint)point - copy:(BOOL)copy; - -// Returns the index in the model for a drag to the location given by -// |point|. This is determined by finding the first button before the center -// of which |point| falls, scanning left to right. Note that, currently, only -// the x-coordinate of |point| is considered. Though not currently implemented, -// we may check for errors, in which case this would return negative value; -// callers should check for this. -- (int)indexForDragToPoint:(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; -- (void)clearMenuTagMap; -- (int)preferredHeight; -- (void)addNonBookmarkButtonsToView; -- (void)addButtonsToView; -- (void)centerNoItemsLabel; -- (void)setNodeForBarMenu; - -- (void)watchForExitEvent:(BOOL)watch; - -@end - -@implementation BookmarkBarController - -@synthesize visualState = visualState_; -@synthesize lastVisualState = lastVisualState_; -@synthesize delegate = delegate_; - -- (id)initWithBrowser:(Browser*)browser - initialWidth:(float)initialWidth - delegate:(id<BookmarkBarControllerDelegate>)delegate - resizeDelegate:(id<ViewResizer>)resizeDelegate { - if ((self = [super initWithNibName:@"BookmarkBar" - bundle:mac_util::MainAppBundle()])) { - // Initialize to an invalid state. - visualState_ = bookmarks::kInvalidState; - lastVisualState_ = bookmarks::kInvalidState; - - browser_ = browser; - initialWidth_ = initialWidth; - bookmarkModel_ = browser_->profile()->GetBookmarkModel(); - buttons_.reset([[NSMutableArray alloc] init]); - delegate_ = delegate; - resizeDelegate_ = resizeDelegate; - folderTarget_.reset([[BookmarkFolderTarget alloc] initWithController:self]); - - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - folderImage_.reset( - [rb.GetNativeImageNamed(IDR_BOOKMARK_BAR_FOLDER) retain]); - defaultImage_.reset([rb.GetNativeImageNamed(IDR_DEFAULT_FAVICON) retain]); - - // Register for theme changes, bookmark button pulsing, ... - NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter]; - [defaultCenter addObserver:self - selector:@selector(themeDidChangeNotification:) - name:kBrowserThemeDidChangeNotification - object:nil]; - [defaultCenter addObserver:self - selector:@selector(pulseBookmarkNotification:) - name:bookmark_button::kPulseBookmarkButtonNotification - object:nil]; - - // This call triggers an awakeFromNib, which builds the bar, which - // might uses folderImage_. So make sure it happens after - // folderImage_ is loaded. - [[self animatableView] setResizeDelegate:resizeDelegate]; - } - return self; -} - -- (void)pulseBookmarkNotification:(NSNotification*)notification { - NSDictionary* dict = [notification userInfo]; - const BookmarkNode* node = NULL; - NSValue *value = [dict objectForKey:bookmark_button::kBookmarkKey]; - DCHECK(value); - if (value) - node = static_cast<const BookmarkNode*>([value pointerValue]); - NSNumber* number = [dict - objectForKey:bookmark_button::kBookmarkPulseFlagKey]; - DCHECK(number); - BOOL doPulse = number ? [number boolValue] : NO; - - // 3 cases: - // button on the bar: flash it - // button in "other bookmarks" folder: flash other bookmarks - // button in "off the side" folder: flash the chevron - for (BookmarkButton* button in [self buttons]) { - if ([button bookmarkNode] == node) { - [button setIsContinuousPulsing:doPulse]; - return; - } - } - if ([otherBookmarksButton_ bookmarkNode] == node) { - [otherBookmarksButton_ setIsContinuousPulsing:doPulse]; - return; - } - if (node->GetParent() == bookmarkModel_->GetBookmarkBarNode()) { - [offTheSideButton_ setIsContinuousPulsing:doPulse]; - return; - } - - NOTREACHED() << "no bookmark button found to pulse!"; -} - -- (void)dealloc { - // We better stop any in-flight animation if we're being killed. - [[self animatableView] stopAnimation]; - - // Remove our view from its superview so it doesn't attempt to reference - // it when the controller is gone. - //TODO(dmaclach): Remove -- http://crbug.com/25845 - [[self view] removeFromSuperview]; - - // Be sure there is no dangling pointer. - if ([[self view] respondsToSelector:@selector(setController:)]) - [[self view] performSelector:@selector(setController:) withObject:nil]; - - // For safety, make sure the buttons can no longer call us. - for (BookmarkButton* button in buttons_.get()) { - [button setDelegate:nil]; - [button setTarget:nil]; - [button setAction:nil]; - } - - bridge_.reset(NULL); - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [self watchForExitEvent:NO]; - [super dealloc]; -} - -- (void)awakeFromNib { - // We default to NOT open, which means height=0. - DCHECK([[self view] isHidden]); // Hidden so it's OK to change. - - // Set our initial height to zero, since that is what the superview - // expects. We will resize ourselves open later if needed. - [[self view] setFrame:NSMakeRect(0, 0, initialWidth_, 0)]; - - // Complete init of the "off the side" button, as much as we can. - [offTheSideButton_ setDraggable:NO]; - - // We are enabled by default. - barIsEnabled_ = YES; - - // Remember the original sizes of the 'no items' and 'import bookmarks' - // fields to aid in resizing when the window frame changes. - originalNoItemsRect_ = [[buttonView_ noItemTextfield] frame]; - originalImportBookmarksRect_ = [[buttonView_ importBookmarksButton] frame]; - - // To make life happier when the bookmark bar is floating, the chevron is a - // child of the button view. - [offTheSideButton_ removeFromSuperview]; - [buttonView_ addSubview:offTheSideButton_]; - - // Copy the bar menu so we know if it's from the bar or a folder. - // Then we set its represented item to be the bookmark bar. - buttonFolderContextMenu_.reset([[[self view] menu] copy]); - - // When resized we may need to add new buttons, or remove them (if - // no longer visible), or add/remove the "off the side" menu. - [[self view] setPostsFrameChangedNotifications:YES]; - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(frameDidChange) - name:NSViewFrameDidChangeNotification - object:[self view]]; - - // Watch for things going to or from fullscreen. - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(willEnterOrLeaveFullscreen:) - name:kWillEnterFullscreenNotification - object:nil]; - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(willEnterOrLeaveFullscreen:) - name:kWillLeaveFullscreenNotification - object:nil]; - - // Don't pass ourself along (as 'self') until our init is completely - // done. Thus, this call is (almost) last. - bridge_.reset(new BookmarkBarBridge(self, bookmarkModel_)); -} - -// Called by our main view (a BookmarkBarView) when it gets moved to a -// window. We perform operations which need to know the relevant -// window (e.g. watch for a window close) so they can't be performed -// earlier (such as in awakeFromNib). -- (void)viewDidMoveToWindow { - NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter]; - - // Remove any existing notifications before registering for new ones. - [defaultCenter removeObserver:self - name:NSWindowWillCloseNotification - object:nil]; - [defaultCenter removeObserver:self - name:NSWindowDidResignKeyNotification - object:nil]; - - [defaultCenter addObserver:self - selector:@selector(parentWindowWillClose:) - name:NSWindowWillCloseNotification - object:[[self view] window]]; - [defaultCenter addObserver:self - selector:@selector(parentWindowDidResignKey:) - name:NSWindowDidResignKeyNotification - object:[[self view] window]]; -} - -// When going fullscreen we can run into trouble. Our view is removed -// from the non-fullscreen window before the non-fullscreen window -// loses key, so our parentDidResignKey: callback never gets called. -// In addition, a bookmark folder controller needs to be autoreleased -// (in case it's in the event chain when closed), but the release -// implicitly needs to happen while it's connected to the original -// (non-fullscreen) window to "unlock bar visibility". Such a -// contract isn't honored when going fullscreen with the menu option -// (not with the keyboard shortcut). We fake it as best we can here. -// We have a similar problem leaving fullscreen. -- (void)willEnterOrLeaveFullscreen:(NSNotification*)notification { - if (folderController_) { - [self childFolderWillClose:folderController_]; - [self closeFolderAndStopTrackingMenus]; - } -} - -// NSNotificationCenter callback. -- (void)parentWindowWillClose:(NSNotification*)notification { - [self closeFolderAndStopTrackingMenus]; -} - -// NSNotificationCenter callback. -- (void)parentWindowDidResignKey:(NSNotification*)notification { - [self closeFolderAndStopTrackingMenus]; -} - -// Change the layout of the bookmark bar's subviews in response to a visibility -// change (e.g., show or hide the bar) or style change (attached or floating). -- (void)layoutSubviews { - NSRect frame = [[self view] frame]; - NSRect buttonViewFrame = NSMakeRect(0, 0, NSWidth(frame), NSHeight(frame)); - - // The state of our morph (if any); 1 is total bubble, 0 is the regular bar. - CGFloat morph = [self detachedMorphProgress]; - - // Add padding to the detached bookmark bar. - buttonViewFrame = NSInsetRect(buttonViewFrame, - morph * bookmarks::kNTPBookmarkBarPadding, - morph * bookmarks::kNTPBookmarkBarPadding); - - [buttonView_ setFrame:buttonViewFrame]; -} - -// We don't change a preference; we only change visibility. Preference changing -// (global state) is handled in |BrowserWindowCocoa::ToggleBookmarkBar()|. We -// simply update based on what we're told. -- (void)updateVisibility { - [self showBookmarkBarWithAnimation:NO]; -} - -- (void)setBookmarkBarEnabled:(BOOL)enabled { - if (enabled != barIsEnabled_) { - barIsEnabled_ = enabled; - [self updateVisibility]; - } -} - -- (CGFloat)getDesiredToolbarHeightCompression { - // Some special cases.... - if (!barIsEnabled_) - return 0; - - if ([self isAnimationRunning]) { - // No toolbar compression when animating between hidden and showing, nor - // between showing and detached. - if ([self isAnimatingBetweenState:bookmarks::kHiddenState - andState:bookmarks::kShowingState] || - [self isAnimatingBetweenState:bookmarks::kShowingState - andState:bookmarks::kDetachedState]) - return 0; - - // If we ever need any other animation cases, code would go here. - } - - return [self isInState:bookmarks::kShowingState] ? kBookmarkBarOverlap : 0; -} - -- (CGFloat)toolbarDividerOpacity { - // Some special cases.... - if ([self isAnimationRunning]) { - // In general, the toolbar shouldn't show a divider while we're animating - // between showing and hidden. The exception is when our height is < 1, in - // which case we can't draw it. It's all-or-nothing (no partial opacity). - if ([self isAnimatingBetweenState:bookmarks::kHiddenState - andState:bookmarks::kShowingState]) - return (NSHeight([[self view] frame]) < 1) ? 1 : 0; - - // The toolbar should show the divider when animating between showing and - // detached (but opacity will vary). - if ([self isAnimatingBetweenState:bookmarks::kShowingState - andState:bookmarks::kDetachedState]) - return static_cast<CGFloat>([self detachedMorphProgress]); - - // If we ever need any other animation cases, code would go here. - } - - // In general, only show the divider when it's in the normal showing state. - return [self isInState:bookmarks::kShowingState] ? 0 : 1; -} - -- (NSImage*)favIconForNode:(const BookmarkNode*)node { - if (!node) - return defaultImage_; - - if (node->is_folder()) - return folderImage_; - - const SkBitmap& favIcon = bookmarkModel_->GetFavIcon(node); - if (!favIcon.isNull()) - return gfx::SkBitmapToNSImage(favIcon); - - return defaultImage_; -} - -- (void)closeFolderAndStopTrackingMenus { - showFolderMenus_ = NO; - [self closeAllBookmarkFolders]; -} - -- (BOOL)canEditBookmark:(const BookmarkNode*)node { - // Don't allow edit/delete of the bar node, or of "Other Bookmarks" - if ((node == nil) || - (node == bookmarkModel_->other_node()) || - (node == bookmarkModel_->GetBookmarkBarNode())) - return NO; - return YES; -} - -#pragma mark Actions - -- (IBAction)openBookmark:(id)sender { - [self closeFolderAndStopTrackingMenus]; - DCHECK([sender respondsToSelector:@selector(bookmarkNode)]); - const BookmarkNode* node = [sender bookmarkNode]; - WindowOpenDisposition disposition = - event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); - [self openURL:node->GetURL() disposition:disposition]; -} - -// Redirect to our logic shared with BookmarkBarFolderController. -- (IBAction)openBookmarkFolderFromButton:(id)sender { - if (sender != offTheSideButton_) { - // Toggle presentation of bar folder menus. - showFolderMenus_ = !showFolderMenus_; - [folderTarget_ openBookmarkFolderFromButton:sender]; - } else { - // Off-the-side requires special handling. - [self openOffTheSideFolderFromButton:sender]; - } -} - -// The button that sends this one is special; the "off the side" -// button (chevron) opens like a folder button but isn't exactly a -// parent folder. -- (IBAction)openOffTheSideFolderFromButton:(id)sender { - DCHECK([sender isKindOfClass:[BookmarkButton class]]); - DCHECK([[sender cell] isKindOfClass:[BookmarkButtonCell class]]); - [[sender cell] setStartingChildIndex:displayedButtonCount_]; - [folderTarget_ openBookmarkFolderFromButton:sender]; -} - -- (IBAction)openBookmarkInNewForegroundTab:(id)sender { - const BookmarkNode* node = [self nodeFromMenuItem:sender]; - if (node) - [self openURL:node->GetURL() disposition:NEW_FOREGROUND_TAB]; - [self closeAllBookmarkFolders]; -} - -- (IBAction)openBookmarkInNewWindow:(id)sender { - const BookmarkNode* node = [self nodeFromMenuItem:sender]; - if (node) - [self openURL:node->GetURL() disposition:NEW_WINDOW]; -} - -- (IBAction)openBookmarkInIncognitoWindow:(id)sender { - const BookmarkNode* node = [self nodeFromMenuItem:sender]; - if (node) - [self openURL:node->GetURL() disposition:OFF_THE_RECORD]; -} - -- (IBAction)editBookmark:(id)sender { - const BookmarkNode* node = [self nodeFromMenuItem:sender]; - if (!node) - return; - - if (node->is_folder()) { - BookmarkNameFolderController* controller = - [[BookmarkNameFolderController alloc] - initWithParentWindow:[[self view] window] - profile:browser_->profile() - node:node]; - [controller runAsModalSheet]; - return; - } - - // There is no real need to jump to a platform-common routine at - // this point (which just jumps back to objc) other than consistency - // across platforms. - // - // TODO(jrg): identify when we NO_TREE. I can see it in the code - // for the other platforms but can't find a way to trigger it in the - // UI. - BookmarkEditor::Show([[self view] window], - browser_->profile(), - node->GetParent(), - BookmarkEditor::EditDetails(node), - BookmarkEditor::SHOW_TREE); -} - -- (IBAction)cutBookmark:(id)sender { - const BookmarkNode* node = [self nodeFromMenuItem:sender]; - if (node) { - std::vector<const BookmarkNode*> nodes; - nodes.push_back(node); - bookmark_utils::CopyToClipboard(bookmarkModel_, nodes, true); - } -} - -- (IBAction)copyBookmark:(id)sender { - const BookmarkNode* node = [self nodeFromMenuItem:sender]; - if (node) { - std::vector<const BookmarkNode*> nodes; - nodes.push_back(node); - bookmark_utils::CopyToClipboard(bookmarkModel_, nodes, false); - } -} - -// Paste the copied node immediately after the node for which the context -// menu has been presented if the node is a non-folder bookmark, otherwise -// past at the end of the folder node. -- (IBAction)pasteBookmark:(id)sender { - const BookmarkNode* node = [self nodeFromMenuItem:sender]; - if (node) { - int index = -1; - if (node != bookmarkModel_->GetBookmarkBarNode() && !node->is_folder()) { - const BookmarkNode* parent = node->GetParent(); - index = parent->IndexOfChild(node) + 1; - if (index > parent->GetChildCount()) - index = -1; - node = parent; - } - bookmark_utils::PasteFromClipboard(bookmarkModel_, node, index); - } -} - -- (IBAction)deleteBookmark:(id)sender { - const BookmarkNode* node = [self nodeFromMenuItem:sender]; - if (node) { - bookmarkModel_->Remove(node->GetParent(), - node->GetParent()->IndexOfChild(node)); - } -} - -- (IBAction)openAllBookmarks:(id)sender { - const BookmarkNode* node = [self nodeFromMenuItem:sender]; - if (node) { - [self openAll:node disposition:NEW_FOREGROUND_TAB]; - UserMetrics::RecordAction(UserMetricsAction("OpenAllBookmarks"), - browser_->profile()); - } -} - -- (IBAction)openAllBookmarksNewWindow:(id)sender { - const BookmarkNode* node = [self nodeFromMenuItem:sender]; - if (node) { - [self openAll:node disposition:NEW_WINDOW]; - UserMetrics::RecordAction(UserMetricsAction("OpenAllBookmarksNewWindow"), - browser_->profile()); - } -} - -- (IBAction)openAllBookmarksIncognitoWindow:(id)sender { - const BookmarkNode* node = [self nodeFromMenuItem:sender]; - if (node) { - [self openAll:node disposition:OFF_THE_RECORD]; - UserMetrics::RecordAction( - UserMetricsAction("OpenAllBookmarksIncognitoWindow"), - browser_->profile()); - } -} - -// May be called from the bar or from a folder button. -// If called from a button, that button becomes the parent. -- (IBAction)addPage:(id)sender { - const BookmarkNode* parent = [self nodeFromMenuItem:sender]; - if (!parent) - parent = bookmarkModel_->GetBookmarkBarNode(); - BookmarkEditor::Show([[self view] window], - browser_->profile(), - parent, - BookmarkEditor::EditDetails(), - BookmarkEditor::SHOW_TREE); -} - -// Might be called from the context menu over the bar OR over a -// button. If called from a button, that button becomes a sibling of -// the new node. If called from the bar, add to the end of the bar. -- (IBAction)addFolder:(id)sender { - const BookmarkNode* senderNode = [self nodeFromMenuItem:sender]; - const BookmarkNode* parent = NULL; - int newIndex = 0; - // If triggered from the bar, folder or "others" folder - add as a child to - // the end. - // If triggered from a bookmark, add as next sibling. - BookmarkNode::Type type = senderNode->type(); - if (type == BookmarkNode::BOOKMARK_BAR || - type == BookmarkNode::OTHER_NODE || - type == BookmarkNode::FOLDER) { - parent = senderNode; - newIndex = parent->GetChildCount(); - } else { - parent = senderNode->GetParent(); - newIndex = parent->IndexOfChild(senderNode) + 1; - } - BookmarkNameFolderController* controller = - [[BookmarkNameFolderController alloc] - initWithParentWindow:[[self view] window] - profile:browser_->profile() - parent:parent - newIndex:newIndex]; - [controller runAsModalSheet]; -} - -- (IBAction)importBookmarks:(id)sender { - [ImportSettingsDialogController showImportSettingsDialogForProfile: - browser_->profile()]; -} - -#pragma mark Private Methods - -// Called after the current theme has changed. -- (void)themeDidChangeNotification:(NSNotification*)aNotification { - ThemeProvider* themeProvider = - static_cast<ThemeProvider*>([[aNotification object] pointerValue]); - [self updateTheme:themeProvider]; -} - -// (Private) Method is the same as [self view], but is provided to be explicit. -- (BackgroundGradientView*)backgroundGradientView { - DCHECK([[self view] isKindOfClass:[BackgroundGradientView class]]); - return (BackgroundGradientView*)[self view]; -} - -// (Private) Method is the same as [self view], but is provided to be explicit. -- (AnimatableView*)animatableView { - DCHECK([[self view] isKindOfClass:[AnimatableView class]]); - return (AnimatableView*)[self view]; -} - -// Position the off-the-side chevron to the left of the otherBookmarks button. -- (void)positionOffTheSideButton { - NSRect frame = [offTheSideButton_ frame]; - if (otherBookmarksButton_.get()) { - frame.origin.x = ([otherBookmarksButton_ frame].origin.x - - (frame.size.width + - bookmarks::kBookmarkHorizontalPadding)); - [offTheSideButton_ setFrame:frame]; - } -} - -// Configure the off-the-side button (e.g. specify the node range, -// check if we should enable or disable it, etc). -- (void)configureOffTheSideButtonContentsAndVisibility { - // If deleting a button while off-the-side is open, buttons may be - // promoted from off-the-side to the bar. Accomodate. - if (folderController_ && - ([folderController_ parentButton] == offTheSideButton_)) { - [folderController_ reconfigureMenu]; - } - - [[offTheSideButton_ cell] setStartingChildIndex:displayedButtonCount_]; - [[offTheSideButton_ cell] - setBookmarkNode:bookmarkModel_->GetBookmarkBarNode()]; - int bookmarkChildren = bookmarkModel_->GetBookmarkBarNode()->GetChildCount(); - if (bookmarkChildren > displayedButtonCount_) { - [offTheSideButton_ setHidden:NO]; - } else { - // If we just deleted the last item in an off-the-side menu so the - // button will be going away, make sure the menu goes away. - if (folderController_ && - ([folderController_ parentButton] == offTheSideButton_)) - [self closeAllBookmarkFolders]; - // (And hide the button, too.) - [offTheSideButton_ setHidden:YES]; - } -} - -// Begin (or end) watching for a click outside this window. Unlike -// normal NSWindows, bookmark folder "fake menu" windows do not become -// key or main. Thus, traditional notification (e.g. WillResignKey) -// won't work. Our strategy is to watch (at the app level) for a -// "click outside" these windows to detect when they logically lose -// focus. -- (void)watchForExitEvent:(BOOL)watch { - CrApplication* app = static_cast<CrApplication*>([NSApplication - sharedApplication]); - DCHECK([app isKindOfClass:[CrApplication class]]); - if (watch) { - if (!watchingForExitEvent_) - [app addEventHook:self]; - } else { - if (watchingForExitEvent_) - [app removeEventHook:self]; - } - watchingForExitEvent_ = watch; -} - -// Keep the "no items" label centered in response to a frame size change. -- (void)centerNoItemsLabel { - // Note that this computation is done in the parent's coordinate system, - // which is unflipped. Also, we want the label to be a fixed distance from - // the bottom, so that it slides up properly (on animating to hidden). - // The textfield sits in the itemcontainer, so to center it we maintain - // equal vertical padding on the top and bottom. - int yoffset = (NSHeight([[buttonView_ noItemTextfield] frame]) - - NSHeight([[buttonView_ noItemContainer] frame])) / 2; - [[buttonView_ noItemContainer] setFrameOrigin:NSMakePoint(0, yoffset)]; -} - -// (Private) -- (void)showBookmarkBarWithAnimation:(BOOL)animate { - if (animate && !ignoreAnimations_) { - // If |-doBookmarkBarAnimation| does the animation, we're done. - if ([self doBookmarkBarAnimation]) - return; - - // Else fall through and do the change instantly. - } - - // Set our height. - [resizeDelegate_ resizeView:[self view] - newHeight:[self preferredHeight]]; - - // Only show the divider if showing the normal bookmark bar. - BOOL showsDivider = [self isInState:bookmarks::kShowingState]; - [[self backgroundGradientView] setShowsDivider:showsDivider]; - - // Make sure we're shown. - [[self view] setHidden:![self isVisible]]; - - // Update everything else. - [self layoutSubviews]; - [self frameDidChange]; -} - -// (Private) -- (BOOL)doBookmarkBarAnimation { - if ([self isAnimatingFromState:bookmarks::kHiddenState - toState:bookmarks::kShowingState]) { - [[self backgroundGradientView] setShowsDivider:YES]; - [[self view] setHidden:NO]; - AnimatableView* view = [self animatableView]; - // Height takes into account the extra height we have since the toolbar - // only compresses when we're done. - [view animateToNewHeight:(bookmarks::kBookmarkBarHeight - - kBookmarkBarOverlap) - duration:kBookmarkBarAnimationDuration]; - } else if ([self isAnimatingFromState:bookmarks::kShowingState - toState:bookmarks::kHiddenState]) { - [[self backgroundGradientView] setShowsDivider:YES]; - [[self view] setHidden:NO]; - AnimatableView* view = [self animatableView]; - [view animateToNewHeight:0 - duration:kBookmarkBarAnimationDuration]; - } else if ([self isAnimatingFromState:bookmarks::kShowingState - toState:bookmarks::kDetachedState]) { - [[self backgroundGradientView] setShowsDivider:YES]; - [[self view] setHidden:NO]; - AnimatableView* view = [self animatableView]; - [view animateToNewHeight:bookmarks::kNTPBookmarkBarHeight - duration:kBookmarkBarAnimationDuration]; - } else if ([self isAnimatingFromState:bookmarks::kDetachedState - toState:bookmarks::kShowingState]) { - [[self backgroundGradientView] setShowsDivider:YES]; - [[self view] setHidden:NO]; - AnimatableView* view = [self animatableView]; - // Height takes into account the extra height we have since the toolbar - // only compresses when we're done. - [view animateToNewHeight:(bookmarks::kBookmarkBarHeight - - kBookmarkBarOverlap) - duration:kBookmarkBarAnimationDuration]; - } else { - // Oops! An animation we don't know how to handle. - return NO; - } - - return YES; -} - -// Enable or disable items. We are the menu delegate for both the bar -// and for bookmark folder buttons. -- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)anItem { - // NSUserInterfaceValidations says that the passed-in object has type - // |id<NSValidatedUserInterfaceItem>|, but this function needs to call the - // NSObject method -isKindOfClass: on the parameter. In theory, this is not - // correct, but this is probably a bug in the method signature. - NSMenuItem* item = static_cast<NSMenuItem*>(anItem); - // Yes for everything we don't explicitly deny. - if (![item isKindOfClass:[NSMenuItem class]]) - return YES; - - // Yes if we're not a special BookmarkMenu. - if (![[item menu] isKindOfClass:[BookmarkMenu class]]) - return YES; - - // No if we think it's a special BookmarkMenu but have trouble. - const BookmarkNode* node = [self nodeFromMenuItem:item]; - if (!node) - return NO; - - // If this is the bar menu, we only have things to do if there are - // buttons. If this is a folder button menu, we only have things to - // do if the folder has items. - NSMenu* menu = [item menu]; - BOOL thingsToDo = NO; - if (menu == [[self view] menu]) { - thingsToDo = [buttons_ count] ? YES : NO; - } else { - if (node && node->is_folder() && node->GetChildCount()) { - thingsToDo = YES; - } - } - - // Disable openAll* if we have nothing to do. - SEL action = [item action]; - if ((!thingsToDo) && - ((action == @selector(openAllBookmarks:)) || - (action == @selector(openAllBookmarksNewWindow:)) || - (action == @selector(openAllBookmarksIncognitoWindow:)))) { - return NO; - } - - if ((action == @selector(editBookmark:)) || - (action == @selector(deleteBookmark:)) || - (action == @selector(cutBookmark:)) || - (action == @selector(copyBookmark:))) { - if (![self canEditBookmark:node]) { - return NO; - } - } - - if (action == @selector(pasteBookmark:) && - !bookmark_utils::CanPasteFromClipboard(node)) - return NO; - - // If this is an incognito window, don't allow "open in incognito". - if ((action == @selector(openBookmarkInIncognitoWindow:)) || - (action == @selector(openAllBookmarksIncognitoWindow:))) { - if (browser_->profile()->IsOffTheRecord()) { - return NO; - } - } - - // Enabled by default. - return YES; -} - -// Actually open the URL. This is the last chance for a unit test to -// override. -- (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition { - browser_->OpenURL(url, GURL(), disposition, PageTransition::AUTO_BOOKMARK); -} - -- (void)clearMenuTagMap { - seedId_ = 0; - menuTagMap_.clear(); -} - -- (int)preferredHeight { - DCHECK(![self isAnimationRunning]); - - if (!barIsEnabled_) - return 0; - - switch (visualState_) { - case bookmarks::kShowingState: - return bookmarks::kBookmarkBarHeight; - case bookmarks::kDetachedState: - return bookmarks::kNTPBookmarkBarHeight; - case bookmarks::kHiddenState: - return 0; - case bookmarks::kInvalidState: - default: - NOTREACHED(); - return 0; - } -} - -// Recursively add the given bookmark node and all its children to -// menu, one menu item per node. -- (void)addNode:(const BookmarkNode*)child toMenu:(NSMenu*)menu { - NSString* title = [BookmarkMenuCocoaController menuTitleForNode:child]; - NSMenuItem* item = [[[NSMenuItem alloc] initWithTitle:title - action:nil - keyEquivalent:@""] autorelease]; - [menu addItem:item]; - [item setImage:[self favIconForNode:child]]; - if (child->is_folder()) { - NSMenu* submenu = [[[NSMenu alloc] initWithTitle:title] autorelease]; - [menu setSubmenu:submenu forItem:item]; - if (child->GetChildCount()) { - [self addFolderNode:child toMenu:submenu]; // potentially recursive - } else { - [self tagEmptyMenu:submenu]; - } - } else { - [item setTarget:self]; - [item setAction:@selector(openBookmarkMenuItem:)]; - [item setTag:[self menuTagFromNodeId:child->id()]]; - // Add a tooltip - std::string url_string = child->GetURL().possibly_invalid_spec(); - NSString* tooltip = [NSString stringWithFormat:@"%@\n%s", - base::SysUTF16ToNSString(child->GetTitle()), - url_string.c_str()]; - [item setToolTip:tooltip]; - } -} - -// Empty menus are odd; if empty, add something to look at. -// Matches windows behavior. -- (void)tagEmptyMenu:(NSMenu*)menu { - NSString* empty_menu_title = l10n_util::GetNSString(IDS_MENU_EMPTY_SUBMENU); - [menu addItem:[[[NSMenuItem alloc] initWithTitle:empty_menu_title - action:NULL - keyEquivalent:@""] autorelease]]; -} - -// Add the children of the given bookmark node (and their children...) -// to menu, one menu item per node. -- (void)addFolderNode:(const BookmarkNode*)node toMenu:(NSMenu*)menu { - for (int i = 0; i < node->GetChildCount(); i++) { - const BookmarkNode* child = node->GetChild(i); - [self addNode:child toMenu:menu]; - } -} - -// Return an autoreleased NSMenu that represents the given bookmark -// folder node. -- (NSMenu *)menuForFolderNode:(const BookmarkNode*)node { - if (!node->is_folder()) - return nil; - NSString* title = base::SysUTF16ToNSString(node->GetTitle()); - NSMenu* menu = [[[NSMenu alloc] initWithTitle:title] autorelease]; - [self addFolderNode:node toMenu:menu]; - - if (![menu numberOfItems]) { - [self tagEmptyMenu:menu]; - } - return menu; -} - -// 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 -// too small. For "FBL" it is 2 pixels too small. -// For a bookmark named "SFGateFooWoo", it is just fine. -- (CGFloat)widthForBookmarkButtonCell:(NSCell*)cell { - CGFloat desired = [cell cellSize].width + 2; - return std::min(desired, bookmarks::kDefaultBookmarkWidth); -} - -- (IBAction)openBookmarkMenuItem:(id)sender { - int64 tag = [self nodeIdFromMenuTag:[sender tag]]; - const BookmarkNode* node = bookmarkModel_->GetNodeByID(tag); - WindowOpenDisposition disposition = - event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); - [self openURL:node->GetURL() disposition:disposition]; -} - -// For the given root node of the bookmark bar, show or hide (as -// appropriate) the "no items" container (text which says "bookmarks -// go here"). -- (void)showOrHideNoItemContainerForNode:(const BookmarkNode*)node { - BOOL hideNoItemWarning = node->GetChildCount() > 0; - [[buttonView_ noItemContainer] setHidden:hideNoItemWarning]; -} - -// TODO(jrg): write a "build bar" so there is a nice spot for things -// like the contextual menu which is invoked when not over a -// bookmark. On Safari that menu has a "new folder" option. -- (void)addNodesToButtonList:(const BookmarkNode*)node { - [self showOrHideNoItemContainerForNode:node]; - - CGFloat maxViewX = NSMaxX([[self view] bounds]); - 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]; - } -} - -- (BookmarkButton*)buttonForNode:(const BookmarkNode*)node - xOffset:(int*)xOffset { - BookmarkButtonCell* cell = [self cellForBookmarkNode:node]; - NSRect frame = [self frameForBookmarkButtonFromCell:cell xOffset:xOffset]; - - scoped_nsobject<BookmarkButton> - button([[BookmarkButton alloc] initWithFrame:frame]); - DCHECK(button.get()); - - // [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]; - - // We cannot set the button cell's text color until it is placed in - // the button (e.g. the [button setCell:cell] call right above). We - // also cannot set the cell's text color until the view is added to - // the hierarchy. If that second part is now true, set the color. - // (If not we'll set the color on the 1st themeChanged: - // notification.) - ThemeProvider* themeProvider = [[[self view] window] themeProvider]; - if (themeProvider) { - NSColor* color = - themeProvider->GetNSColor(BrowserThemeProvider::COLOR_BOOKMARK_TEXT, - true); - [cell setTextColor:color]; - } - - 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::SysUTF16ToNSString(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 -// and the "other bookmarks" button. Technically "other bookmarks" is -// a bookmark button but it is treated specially. Only needs to be -// called when these buttons are new or when the bookmark bar is -// cleared (e.g. on a loaded: call). Unlike addButtonsToView below, -// we don't need to add/remove these dynamically in response to window -// resize. -- (void)addNonBookmarkButtonsToView { - [buttonView_ addSubview:otherBookmarksButton_.get()]; - [buttonView_ addSubview:offTheSideButton_]; -} - -// 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 the first time. -- (void)addButtonsToView { - 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]; - } -} - -// Create the button for "Other Bookmarks" on the right of the bar. -- (void)createOtherBookmarksButton { - // Can't create this until the model is loaded, but only need to - // create it once. - if (otherBookmarksButton_.get()) - return; - - // TODO(jrg): remove duplicate code - NSCell* cell = [self cellForBookmarkNode:bookmarkModel_->other_node()]; - int ignored = 0; - NSRect frame = [self frameForBookmarkButtonFromCell:cell xOffset:&ignored]; - frame.origin.x = [[self buttonView] bounds].size.width - frame.size.width; - frame.origin.x -= bookmarks::kBookmarkHorizontalPadding; - BookmarkButton* button = [[BookmarkButton alloc] initWithFrame:frame]; - [button setDraggable:NO]; - otherBookmarksButton_.reset(button); - view_id_util::SetID(button, VIEW_ID_OTHER_BOOKMARKS); - - // Make sure this button, like all other BookmarkButtons, lives - // until the end of the current event loop. - [[button retain] autorelease]; - - // Peg at right; keep same height as bar. - [button setAutoresizingMask:(NSViewMinXMargin)]; - [button setCell:cell]; - [button setDelegate:self]; - [button setTarget:self]; - [button setAction:@selector(openBookmarkFolderFromButton:)]; - [buttonView_ addSubview:button]; - - // Now that it's here, move the chevron over. - [self positionOffTheSideButton]; -} - -// Now that the model is loaded, set the bookmark bar root as the node -// represented by the bookmark bar (default, background) menu. -- (void)setNodeForBarMenu { - const BookmarkNode* node = bookmarkModel_->GetBookmarkBarNode(); - BookmarkMenu* menu = static_cast<BookmarkMenu*>([[self view] menu]); - - // Make sure types are compatible - DCHECK(sizeof(long long) == sizeof(int64)); - [menu setRepresentedObject:[NSNumber numberWithLongLong:node->id()]]; -} - -// To avoid problems with sync, changes that may impact the current -// bookmark (e.g. deletion) make sure context menus are closed. This -// prevents deleting a node which no longer exists. -- (void)cancelMenuTracking { - [buttonContextMenu_ cancelTracking]; - [buttonFolderContextMenu_ cancelTracking]; -} - -// Determines the appropriate state for the given situation. -+ (bookmarks::VisualState)visualStateToShowNormalBar:(BOOL)showNormalBar - showDetachedBar:(BOOL)showDetachedBar { - if (showNormalBar) - return bookmarks::kShowingState; - if (showDetachedBar) - return bookmarks::kDetachedState; - return bookmarks::kHiddenState; -} - -- (void)moveToVisualState:(bookmarks::VisualState)nextVisualState - withAnimation:(BOOL)animate { - BOOL isAnimationRunning = [self isAnimationRunning]; - - // No-op if the next state is the same as the "current" one, subject to the - // following conditions: - // - no animation is running; or - // - an animation is running and |animate| is YES ([*] if it's NO, we'd want - // to cancel the animation and jump to the final state). - if ((nextVisualState == visualState_) && (!isAnimationRunning || animate)) - return; - - // If an animation is running, we want to finalize it. Otherwise we'd have to - // be able to animate starting from the middle of one type of animation. We - // assume that animations that we know about can be "reversed". - if (isAnimationRunning) { - // Don't cancel if we're going to reverse the animation. - if (nextVisualState != lastVisualState_) { - [self stopCurrentAnimation]; - [self finalizeVisualState]; - } - - // If we're in case [*] above, we can stop here. - if (nextVisualState == visualState_) - return; - } - - // Now update with the new state change. - lastVisualState_ = visualState_; - visualState_ = nextVisualState; - - // Animate only if told to and if bar is enabled. - if (animate && !ignoreAnimations_ && barIsEnabled_) { - [self closeAllBookmarkFolders]; - // Take care of any animation cases we know how to handle. - - // We know how to handle hidden <-> normal, normal <-> detached.... - if ([self isAnimatingBetweenState:bookmarks::kHiddenState - andState:bookmarks::kShowingState] || - [self isAnimatingBetweenState:bookmarks::kShowingState - andState:bookmarks::kDetachedState]) { - [delegate_ bookmarkBar:self willAnimateFromState:lastVisualState_ - toState:visualState_]; - [self showBookmarkBarWithAnimation:YES]; - return; - } - - // If we ever need any other animation cases, code would go here. - // Let any animation cases which we don't know how to handle fall through to - // the unanimated case. - } - - // Just jump to the state. - [self finalizeVisualState]; -} - -// N.B.: |-moveToVisualState:...| will check if this should be a no-op or not. -- (void)updateAndShowNormalBar:(BOOL)showNormalBar - showDetachedBar:(BOOL)showDetachedBar - withAnimation:(BOOL)animate { - bookmarks::VisualState newVisualState = - [BookmarkBarController visualStateToShowNormalBar:showNormalBar - showDetachedBar:showDetachedBar]; - [self moveToVisualState:newVisualState - withAnimation:animate && !ignoreAnimations_]; -} - -// (Private) -- (void)finalizeVisualState { - // We promise that our delegate that the variables will be finalized before - // the call to |-bookmarkBar:didChangeFromState:toState:|. - bookmarks::VisualState oldVisualState = lastVisualState_; - lastVisualState_ = bookmarks::kInvalidState; - - // Notify our delegate. - [delegate_ bookmarkBar:self didChangeFromState:oldVisualState - toState:visualState_]; - - // Update ourselves visually. - [self updateVisibility]; -} - -// (Private) -- (void)stopCurrentAnimation { - [[self animatableView] stopAnimation]; -} - -// Delegate method for |AnimatableView| (a superclass of -// |BookmarkBarToolbarView|). -- (void)animationDidEnd:(NSAnimation*)animation { - [self finalizeVisualState]; -} - -- (void)reconfigureBookmarkBar { - [self redistributeButtonsOnBarAsNeeded]; - [self positionOffTheSideButton]; - [self configureOffTheSideButtonContentsAndVisibility]; - [self centerNoItemsLabel]; -} - -// Determine if the given |view| can completely fit within the constraint of -// maximum x, given by |maxViewX|, and, if not, narrow the view up to a minimum -// width. If the minimum width is not achievable then hide the view. Return YES -// if the view was hidden. -- (BOOL)shrinkOrHideView:(NSView*)view forMaxX:(CGFloat)maxViewX { - BOOL wasHidden = NO; - // See if the view needs to be narrowed. - NSRect frame = [view frame]; - if (NSMaxX(frame) > maxViewX) { - // Resize if more than 30 pixels are showing, otherwise hide. - if (NSMinX(frame) + 30.0 < maxViewX) { - frame.size.width = maxViewX - NSMinX(frame); - [view setFrame:frame]; - } else { - [view setHidden:YES]; - wasHidden = YES; - } - } - return wasHidden; -} - -// Adjust the horizontal width and the visibility of the "For quick access" -// text field and "Import bookmarks..." button based on the current width -// of the containing |buttonView_| (which is affected by window width). -- (void)adjustNoItemContainerWidthsForMaxX:(CGFloat)maxViewX { - if (![[buttonView_ noItemContainer] isHidden]) { - // Reset initial frames for the two items, then adjust as necessary. - NSTextField* noItemTextfield = [buttonView_ noItemTextfield]; - [noItemTextfield setFrame:originalNoItemsRect_]; - [noItemTextfield setHidden:NO]; - NSButton* importBookmarksButton = [buttonView_ importBookmarksButton]; - [importBookmarksButton setFrame:originalImportBookmarksRect_]; - [importBookmarksButton setHidden:NO]; - // Check each to see if they need to be shrunk or hidden. - if ([self shrinkOrHideView:importBookmarksButton forMaxX:maxViewX]) - [self shrinkOrHideView:noItemTextfield forMaxX:maxViewX]; - } -} - -- (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 setDelegate:nil]; - [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]; - } - - // While we're here, adjust the horizontal width and the visibility - // of the "For quick access" and "Import bookmarks..." text fields. - if (![buttons_ count]) - [self adjustNoItemContainerWidthsForMaxX:maxViewX]; -} - -#pragma mark Private Methods Exposed for Testing - -- (BookmarkBarView*)buttonView { - return buttonView_; -} - -- (NSMutableArray*)buttons { - return buttons_.get(); -} - -- (NSButton*)offTheSideButton { - return offTheSideButton_; -} - -- (BOOL)offTheSideButtonIsHidden { - return [offTheSideButton_ isHidden]; -} - -- (BookmarkButton*)otherBookmarksButton { - return otherBookmarksButton_.get(); -} - -- (BookmarkBarFolderController*)folderController { - return folderController_; -} - -- (id)folderTarget { - return folderTarget_.get(); -} - -- (int)displayedButtonCount { - return displayedButtonCount_; -} - -// Delete all buttons (bookmarks, chevron, "other bookmarks") from the -// bookmark bar; reset knowledge of bookmarks. -- (void)clearBookmarkBar { - for (BookmarkButton* button in buttons_.get()) { - [button setDelegate:nil]; - [button removeFromSuperview]; - } - [buttons_ removeAllObjects]; - [self clearMenuTagMap]; - displayedButtonCount_ = 0; - - // Make sure there are no stale pointers in the pasteboard. This - // can be important if a bookmark is deleted (via bookmark sync) - // while in the middle of a drag. The "drag completed" code - // (e.g. [BookmarkBarView performDragOperationForBookmarkButton:]) is - // careful enough to bail if there is no data found at "drop" time. - // - // Unfortunately the clearContents selector is 10.6 only. The best - // we can do is make sure something else is present in place of the - // stale bookmark. - NSPasteboard* pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; - [pboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:self]; - [pboard setString:@"" forType:NSStringPboardType]; -} - -// Return an autoreleased NSCell suitable for a bookmark button. -// TODO(jrg): move much of the cell config into the BookmarkButtonCell class. -- (BookmarkButtonCell*)cellForBookmarkNode:(const BookmarkNode*)node { - NSImage* image = node ? [self favIconForNode:node] : nil; - NSMenu* menu = node && node->is_folder() ? buttonFolderContextMenu_ : - buttonContextMenu_; - BookmarkButtonCell* cell = [BookmarkButtonCell buttonCellForNode:node - contextMenu:menu - cellText:nil - cellImage:image]; - [cell setTag:kStandardButtonTypeWithLimitedClickFeedback]; - - // Note: a quirk of setting a cell's text color is that it won't work - // until the cell is associated with a button, so we can't theme the cell yet. - - return cell; -} - -// Returns a frame appropriate for the given bookmark cell, suitable -// for creating an NSButton that will contain it. |xOffset| is the X -// offset for the frame; it is increased to be an appropriate X offset -// for the next button. -- (NSRect)frameForBookmarkButtonFromCell:(NSCell*)cell - xOffset:(int*)xOffset { - DCHECK(xOffset); - NSRect bounds = [buttonView_ bounds]; - bounds.size.height = bookmarks::kBookmarkButtonHeight; - - NSRect frame = NSInsetRect(bounds, - bookmarks::kBookmarkHorizontalPadding, - bookmarks::kBookmarkVerticalPadding); - frame.size.width = [self widthForBookmarkButtonCell:cell]; - - // Add an X offset based on what we've already done - frame.origin.x += *xOffset; - - // And up the X offset for next time. - *xOffset = NSMaxX(frame); - - return frame; -} - -// A bookmark button's contents changed. Check for growth -// (e.g. increase the width up to the maximum). If we grew, move -// other bookmark buttons over. -- (void)checkForBookmarkButtonGrowth:(NSButton*)button { - NSRect frame = [button frame]; - CGFloat desiredSize = [self widthForBookmarkButtonCell:[button cell]]; - CGFloat delta = desiredSize - frame.size.width; - if (delta) { - frame.size.width = desiredSize; - [button setFrame:frame]; - for (NSButton* button in buttons_.get()) { - NSRect buttonFrame = [button frame]; - if (buttonFrame.origin.x > frame.origin.x) { - buttonFrame.origin.x += delta; - [button setFrame:buttonFrame]; - } - } - } - // We may have just crossed a threshold to enable the off-the-side - // button. - [self configureOffTheSideButtonContentsAndVisibility]; -} - -// Called when our controlled frame has changed size. -- (void)frameDidChange { - if (!bookmarkModel_->IsLoaded()) - return; - [self updateTheme:[[[self view] window] themeProvider]]; - [self reconfigureBookmarkBar]; -} - -// Given a NSMenuItem tag, return the appropriate bookmark node id. -- (int64)nodeIdFromMenuTag:(int32)tag { - return menuTagMap_[tag]; -} - -// Create and return a new tag for the given node id. -- (int32)menuTagFromNodeId:(int64)menuid { - int tag = seedId_++; - menuTagMap_[tag] = menuid; - return tag; -} - -// Return the BookmarkNode associated with the given NSMenuItem. Can -// return NULL which means "do nothing". One case where it would -// return NULL is if the bookmark model gets modified while you have a -// context menu open. -- (const BookmarkNode*)nodeFromMenuItem:(id)sender { - const BookmarkNode* node = NULL; - BookmarkMenu* menu = (BookmarkMenu*)[sender menu]; - if ([menu isKindOfClass:[BookmarkMenu class]]) { - int64 id = [menu id]; - node = bookmarkModel_->GetNodeByID(id); - } - return node; -} - -// Adapt appearance of buttons to the current theme. Called after -// theme changes, or when our view is added to the view hierarchy. -// Oddly, the view pings us instead of us pinging our view. This is -// because our trigger is an [NSView viewWillMoveToWindow:], which the -// controller doesn't normally know about. Otherwise we don't have -// access to the theme before we know what window we will be on. -- (void)updateTheme:(ThemeProvider*)themeProvider { - if (!themeProvider) - return; - NSColor* color = - themeProvider->GetNSColor(BrowserThemeProvider::COLOR_BOOKMARK_TEXT, - true); - for (BookmarkButton* button in buttons_.get()) { - BookmarkButtonCell* cell = [button cell]; - [cell setTextColor:color]; - } - [[otherBookmarksButton_ cell] setTextColor:color]; -} - -// Return YES if the event indicates an exit from the bookmark bar -// folder menus. E.g. "click outside" of the area we are watching. -// At this time we are watching the area that includes all popup -// bookmark folder windows. -- (BOOL)isEventAnExitEvent:(NSEvent*)event { - NSWindow* eventWindow = [event window]; - NSWindow* myWindow = [[self view] window]; - switch ([event type]) { - case NSLeftMouseDown: - case NSRightMouseDown: - // If the click is in my window but NOT in the bookmark bar, consider - // it a click 'outside'. Clicks directly on an active button (i.e. one - // that is a folder and for which its folder menu is showing) are 'in'. - // All other clicks on the bookmarks bar are counted as 'outside' - // because they should close any open bookmark folder menu. - if (eventWindow == myWindow) { - NSView* hitView = - [[eventWindow contentView] hitTest:[event locationInWindow]]; - if (hitView == [folderController_ parentButton]) - return NO; - if (![hitView isDescendantOf:[self view]] || hitView == buttonView_) - return YES; - } - // If a click in a bookmark bar folder window and that isn't - // one of my bookmark bar folders, YES is click outside. - if (![eventWindow isKindOfClass:[BookmarkBarFolderWindow - class]]) { - return YES; - } - break; - case NSKeyDown: - case NSKeyUp: - // Any key press ends things. - return YES; - case NSLeftMouseDragged: - // We can get here with the following sequence: - // - open a bookmark folder - // - right-click (and unclick) on it to open context menu - // - move mouse to window titlebar then click-drag it by the titlebar - // http://crbug.com/49333 - return YES; - default: - break; - } - return NO; -} - -#pragma mark Drag & Drop - -// Find something like std::is_between<T>? I can't believe one doesn't exist. -static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { - return ((value >= low) && (value <= high)); -} - -// Return the proposed drop target for a hover open button from the -// given array, or nil if none. We use this for distinguishing -// between a hover-open candidate or drop-indicator draw. -// Helper for buttonForDroppingOnAtPoint:. -// Get UI review on "middle half" ness. -// http://crbug.com/36276 -- (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point - fromArray:(NSArray*)array { - for (BookmarkButton* button in array) { - // Break early if we've gone too far. - if ((NSMinX([button frame]) > point.x) || (![button superview])) - return nil; - // Careful -- this only applies to the bar with horiz buttons. - // Intentionally NOT using NSPointInRect() so that scrolling into - // a submenu doesn't cause it to be closed. - if (ValueInRangeInclusive(NSMinX([button frame]), - point.x, - NSMaxX([button frame]))) { - // Over a button but let's be a little more specific (make sure - // it's over the middle half, not just over it). - NSRect frame = [button frame]; - NSRect middleHalfOfButton = NSInsetRect(frame, frame.size.width / 4, 0); - if (ValueInRangeInclusive(NSMinX(middleHalfOfButton), - point.x, - NSMaxX(middleHalfOfButton))) { - // It makes no sense to drop on a non-folder; there is no hover. - if (![button isFolder]) - return nil; - // Got it! - return button; - } else { - // Over a button but not over the middle half. - return nil; - } - } - } - // Not hovering over a button. - return nil; -} - -// Return the proposed drop target for a hover open button, or nil if -// none. Works with both the bookmark buttons and the "Other -// Bookmarks" button. Point is in [self view] coordinates. -- (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point { - point = [[self view] convertPoint:point - fromView:[[[self view] window] contentView]]; - BookmarkButton* button = [self buttonForDroppingOnAtPoint:point - fromArray:buttons_.get()]; - // One more chance -- try "Other Bookmarks" and "off the side" (if visible). - // This is different than BookmarkBarFolderController. - if (!button) { - NSMutableArray* array = [NSMutableArray array]; - if (![self offTheSideButtonIsHidden]) - [array addObject:offTheSideButton_]; - [array addObject:otherBookmarksButton_]; - button = [self buttonForDroppingOnAtPoint:point - fromArray:array]; - } - return button; -} - -- (int)indexForDragToPoint:(NSPoint)point { - // TODO(jrg): revisit position info based on UI team feedback. - // dropLocation is in bar local coordinates. - NSPoint dropLocation = - [[self view] convertPoint:point - fromView:[[[self view] window] contentView]]; - BookmarkButton* buttonToTheRightOfDraggedButton = nil; - for (BookmarkButton* button in buttons_.get()) { - CGFloat midpoint = NSMidX([button frame]); - if (dropLocation.x <= midpoint) { - buttonToTheRightOfDraggedButton = button; - break; - } - } - if (buttonToTheRightOfDraggedButton) { - const BookmarkNode* afterNode = - [buttonToTheRightOfDraggedButton bookmarkNode]; - DCHECK(afterNode); - int index = afterNode->GetParent()->IndexOfChild(afterNode); - // Make sure we don't get confused by buttons which aren't visible. - return std::min(index, displayedButtonCount_); - } - - // If nothing is to my right I am at the end! - return displayedButtonCount_; -} - -// TODO(mrossetti,jrg): Yet more duplicated code. -// http://crbug.com/35966 -- (BOOL)dragBookmark:(const BookmarkNode*)sourceNode - to:(NSPoint)point - copy:(BOOL)copy { - DCHECK(sourceNode); - // Drop destination. - const BookmarkNode* destParent = NULL; - int destIndex = 0; - - // First check if we're dropping on a button. If we have one, and - // it's a folder, drop in it. - BookmarkButton* button = [self buttonForDroppingOnAtPoint:point]; - if ([button isFolder]) { - destParent = [button bookmarkNode]; - // Drop it at the end. - destIndex = [button bookmarkNode]->GetChildCount(); - } else { - // Else we're dropping somewhere on the bar, so find the right spot. - destParent = bookmarkModel_->GetBookmarkBarNode(); - destIndex = [self indexForDragToPoint:point]; - } - - // Be sure we don't try and drop a folder into itself. - if (sourceNode != destParent) { - if (copy) - bookmarkModel_->Copy(sourceNode, destParent, destIndex); - else - bookmarkModel_->Move(sourceNode, destParent, destIndex); - } - - [self closeFolderAndStopTrackingMenus]; - - // Movement of a node triggers observers (like us) to rebuild the - // bar so we don't have to do so explicitly. - - return YES; -} - -- (void)draggingEnded:(id<NSDraggingInfo>)info { - [self closeFolderAndStopTrackingMenus]; -} - -#pragma mark Bridge Notification Handlers - -// TODO(jrg): for now this is brute force. -- (void)loaded:(BookmarkModel*)model { - DCHECK(model == bookmarkModel_); - if (!model->IsLoaded()) - return; - - // If this is a rebuild request while we have a folder open, close it. - // TODO(mrossetti): Eliminate the need for this because it causes the folder - // menu to disappear after a cut/copy/paste/delete change. - // See: http://crbug.com/36614 - if (folderController_) - [self closeAllBookmarkFolders]; - - // Brute force nuke and build. - savedFrameWidth_ = NSWidth([[self view] frame]); - const BookmarkNode* node = model->GetBookmarkBarNode(); - [self clearBookmarkBar]; - [self addNodesToButtonList:node]; - [self createOtherBookmarksButton]; - [self updateTheme:[[[self view] window] themeProvider]]; - [self positionOffTheSideButton]; - [self addNonBookmarkButtonsToView]; - [self addButtonsToView]; - [self configureOffTheSideButtonContentsAndVisibility]; - [self setNodeForBarMenu]; -} - -- (void)beingDeleted:(BookmarkModel*)model { - // The browser may be being torn down; little is safe to do. As an - // example, it may not be safe to clear the pasteboard. - // http://crbug.com/38665 -} - -- (void)nodeAdded:(BookmarkModel*)model - parent:(const BookmarkNode*)newParent index:(int)newIndex { - // If a context menu is open, close it. - [self cancelMenuTracking]; - - const BookmarkNode* newNode = newParent->GetChild(newIndex); - id<BookmarkButtonControllerProtocol> newController = - [self controllerForNode:newParent]; - [newController addButtonForNode:newNode atIndex:newIndex]; - // If we go from 0 --> 1 bookmarks we may need to hide the - // "bookmarks go here" text container. - [self showOrHideNoItemContainerForNode:model->GetBookmarkBarNode()]; -} - -// TODO(jrg): for now this is brute force. -- (void)nodeChanged:(BookmarkModel*)model - node:(const BookmarkNode*)node { - [self loaded:model]; -} - -- (void)nodeMoved:(BookmarkModel*)model - oldParent:(const BookmarkNode*)oldParent oldIndex:(int)oldIndex - newParent:(const BookmarkNode*)newParent newIndex:(int)newIndex { - const BookmarkNode* movedNode = newParent->GetChild(newIndex); - id<BookmarkButtonControllerProtocol> oldController = - [self controllerForNode:oldParent]; - id<BookmarkButtonControllerProtocol> newController = - [self controllerForNode:newParent]; - if (newController == oldController) { - [oldController moveButtonFromIndex:oldIndex toIndex:newIndex]; - } else { - [oldController removeButton:oldIndex animate:NO]; - [newController addButtonForNode:movedNode atIndex:newIndex]; - } - // If the bar is one of the parents we may need to update the visibility - // of the "bookmarks go here" presentation. - [self showOrHideNoItemContainerForNode:model->GetBookmarkBarNode()]; - // If we moved the only item on the "off the side" menu somewhere - // else, we may no longer need to show it. - [self configureOffTheSideButtonContentsAndVisibility]; -} - -- (void)nodeRemoved:(BookmarkModel*)model - parent:(const BookmarkNode*)oldParent index:(int)index { - // If a context menu is open, close it. - [self cancelMenuTracking]; - - // Locate the parent node. The parent may not be showing, in which case - // we do nothing. - id<BookmarkButtonControllerProtocol> parentController = - [self controllerForNode:oldParent]; - [parentController removeButton:index animate:YES]; - // If we go from 1 --> 0 bookmarks we may need to show the - // "bookmarks go here" text container. - [self showOrHideNoItemContainerForNode:model->GetBookmarkBarNode()]; - // If we deleted the only item on the "off the side" menu we no - // longer need to show it. - [self configureOffTheSideButtonContentsAndVisibility]; -} - -// TODO(jrg): linear searching is bad. -// Need a BookmarkNode-->NSCell mapping. -// -// TODO(jrg): if the bookmark bar is open on launch, we see the -// buttons all placed, then "scooted over" as the favicons load. If -// this looks bad I may need to change widthForBookmarkButtonCell to -// add space for an image even if not there on the assumption that -// favicons will eventually load. -- (void)nodeFavIconLoaded:(BookmarkModel*)model - node:(const BookmarkNode*)node { - for (BookmarkButton* button in buttons_.get()) { - const BookmarkNode* cellnode = [button bookmarkNode]; - if (cellnode == node) { - [[button cell] setBookmarkCellText:[button title] - image:[self favIconForNode:node]]; - // Adding an image means we might need more room for the - // bookmark. Test for it by growing the button (if needed) - // and shifting everything else over. - [self checkForBookmarkButtonGrowth:button]; - } - } -} - -// TODO(jrg): for now this is brute force. -- (void)nodeChildrenReordered:(BookmarkModel*)model - node:(const BookmarkNode*)node { - [self loaded:model]; -} - -#pragma mark BookmarkBarState Protocol - -// (BookmarkBarState protocol) -- (BOOL)isVisible { - return barIsEnabled_ && (visualState_ == bookmarks::kShowingState || - visualState_ == bookmarks::kDetachedState || - lastVisualState_ == bookmarks::kShowingState || - lastVisualState_ == bookmarks::kDetachedState); -} - -// (BookmarkBarState protocol) -- (BOOL)isAnimationRunning { - return lastVisualState_ != bookmarks::kInvalidState; -} - -// (BookmarkBarState protocol) -- (BOOL)isInState:(bookmarks::VisualState)state { - return visualState_ == state && - lastVisualState_ == bookmarks::kInvalidState; -} - -// (BookmarkBarState protocol) -- (BOOL)isAnimatingToState:(bookmarks::VisualState)state { - return visualState_ == state && - lastVisualState_ != bookmarks::kInvalidState; -} - -// (BookmarkBarState protocol) -- (BOOL)isAnimatingFromState:(bookmarks::VisualState)state { - return lastVisualState_ == state; -} - -// (BookmarkBarState protocol) -- (BOOL)isAnimatingFromState:(bookmarks::VisualState)fromState - toState:(bookmarks::VisualState)toState { - return lastVisualState_ == fromState && visualState_ == toState; -} - -// (BookmarkBarState protocol) -- (BOOL)isAnimatingBetweenState:(bookmarks::VisualState)fromState - andState:(bookmarks::VisualState)toState { - return (lastVisualState_ == fromState && visualState_ == toState) || - (visualState_ == fromState && lastVisualState_ == toState); -} - -// (BookmarkBarState protocol) -- (CGFloat)detachedMorphProgress { - if ([self isInState:bookmarks::kDetachedState]) { - return 1; - } - if ([self isAnimatingToState:bookmarks::kDetachedState]) { - return static_cast<CGFloat>( - [[self animatableView] currentAnimationProgress]); - } - if ([self isAnimatingFromState:bookmarks::kDetachedState]) { - return static_cast<CGFloat>( - 1 - [[self animatableView] currentAnimationProgress]); - } - return 0; -} - -#pragma mark BookmarkBarToolbarViewController Protocol - -- (int)currentTabContentsHeight { - TabContents* tc = browser_->GetSelectedTabContents(); - return tc ? tc->view()->GetContainerSize().height() : 0; -} - -- (ThemeProvider*)themeProvider { - return browser_->profile()->GetThemeProvider(); -} - -#pragma mark BookmarkButtonDelegate Protocol - -- (void)fillPasteboard:(NSPasteboard*)pboard - forDragOfButton:(BookmarkButton*)button { - [[self folderTarget] fillPasteboard:pboard forDragOfButton:button]; -} - -// BookmarkButtonDelegate protocol implementation. When menus are -// "active" (e.g. you clicked to open one), moving the mouse over -// another folder button should close the 1st and open the 2nd (like -// real menus). We detect and act here. -- (void)mouseEnteredButton:(id)sender event:(NSEvent*)event { - DCHECK([sender isKindOfClass:[BookmarkButton class]]); - - // If folder menus are not being shown, do nothing. This is different from - // BookmarkBarFolderController's implementation because the bar should NOT - // automatically open folder menus when the mouse passes over a folder - // button while the BookmarkBarFolderController DOES automically open - // a subfolder menu. - if (!showFolderMenus_) - return; - - // From here down: same logic as BookmarkBarFolderController. - // TODO(jrg): find a way to share these 4 non-comment lines? - // http://crbug.com/35966 - // If already opened, then we exited but re-entered the button, so do nothing. - if ([folderController_ parentButton] == sender) - return; - // Else open a new one if it makes sense to do so. - if ([sender bookmarkNode]->is_folder()) { - [folderTarget_ openBookmarkFolderFromButton:sender]; - } else { - // We're over a non-folder bookmark so close any old folders. - [folderController_ close]; - folderController_ = nil; - } -} - -// BookmarkButtonDelegate protocol implementation. -- (void)mouseExitedButton:(id)sender event:(NSEvent*)event { - // Don't care; do nothing. - // This is different behavior that the folder menus. -} - -- (NSWindow*)browserWindow { - return [[self view] window]; -} - -- (BOOL)canDragBookmarkButtonToTrash:(BookmarkButton*)button { - return [self canEditBookmark:[button bookmarkNode]]; -} - -- (void)didDragBookmarkToTrash:(BookmarkButton*)button { - // TODO(mrossetti): Refactor BookmarkBarFolder common code. - // http://crbug.com/35966 - const BookmarkNode* node = [button bookmarkNode]; - if (node) { - const BookmarkNode* parent = node->GetParent(); - bookmarkModel_->Remove(parent, - parent->IndexOfChild(node)); - } -} - -#pragma mark BookmarkButtonControllerProtocol - -// Close all bookmark folders. "Folder" here is the fake menu for -// bookmark folders, not a button context menu. -- (void)closeAllBookmarkFolders { - [self watchForExitEvent:NO]; - [folderController_ close]; - folderController_ = nil; -} - -- (void)closeBookmarkFolder:(id)sender { - // We're the top level, so close one means close them all. - [self closeAllBookmarkFolders]; -} - -- (BookmarkModel*)bookmarkModel { - return bookmarkModel_; -} - -// TODO(jrg): much of this logic is duped with -// [BookmarkBarFolderController draggingEntered:] except when noted. -// http://crbug.com/35966 -- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info { - NSPoint point = [info draggingLocation]; - BookmarkButton* button = [self buttonForDroppingOnAtPoint:point]; - - // Don't allow drops that would result in cycles. - if (button) { - NSData* data = [[info draggingPasteboard] - dataForType:kBookmarkButtonDragType]; - if (data && [info draggingSource]) { - BookmarkButton* sourceButton = nil; - [data getBytes:&sourceButton length:sizeof(sourceButton)]; - const BookmarkNode* sourceNode = [sourceButton bookmarkNode]; - const BookmarkNode* destNode = [button bookmarkNode]; - if (destNode->HasAncestor(sourceNode)) - button = nil; - } - } - - if ([button isFolder]) { - if (hoverButton_ == button) { - return NSDragOperationMove; // already open or timed to open - } - if (hoverButton_) { - // Oops, another one triggered or open. - [NSObject cancelPreviousPerformRequestsWithTarget:[hoverButton_ - target]]; - // Unlike BookmarkBarFolderController, we do not delay the close - // of the previous one. Given the lack of diagonal movement, - // there is no need, and it feels awkward to do so. See - // comments about kDragHoverCloseDelay in - // bookmark_bar_folder_controller.mm for more details. - [[hoverButton_ target] closeBookmarkFolder:hoverButton_]; - hoverButton_.reset(); - } - hoverButton_.reset([button retain]); - DCHECK([[hoverButton_ target] - respondsToSelector:@selector(openBookmarkFolderFromButton:)]); - [[hoverButton_ target] - performSelector:@selector(openBookmarkFolderFromButton:) - withObject:hoverButton_ - afterDelay:bookmarks::kDragHoverOpenDelay]; - } - if (!button) { - if (hoverButton_) { - [NSObject cancelPreviousPerformRequestsWithTarget:[hoverButton_ target]]; - [[hoverButton_ target] closeBookmarkFolder:hoverButton_]; - hoverButton_.reset(); - } - } - - // Thrown away but kept to be consistent with the draggingEntered: interface. - return NSDragOperationMove; -} - -- (void)draggingExited:(id<NSDraggingInfo>)info { - // NOT the same as a cancel --> we may have moved the mouse into the submenu. - if (hoverButton_) { - [NSObject cancelPreviousPerformRequestsWithTarget:[hoverButton_ target]]; - hoverButton_.reset(); - } -} - -- (BOOL)dragShouldLockBarVisibility { - return ![self isInState:bookmarks::kDetachedState] && - ![self isAnimatingToState:bookmarks::kDetachedState]; -} - -// TODO(mrossetti,jrg): Yet more code dup with BookmarkBarFolderController. -// http://crbug.com/35966 -- (BOOL)dragButton:(BookmarkButton*)sourceButton - to:(NSPoint)point - copy:(BOOL)copy { - DCHECK([sourceButton isKindOfClass:[BookmarkButton class]]); - const BookmarkNode* sourceNode = [sourceButton bookmarkNode]; - return [self dragBookmark:sourceNode to:point copy:copy]; -} - -- (BOOL)dragBookmarkData:(id<NSDraggingInfo>)info { - BOOL dragged = NO; - std::vector<const BookmarkNode*> nodes([self retrieveBookmarkNodeData]); - if (nodes.size()) { - BOOL copy = !([info draggingSourceOperationMask] & NSDragOperationMove); - NSPoint dropPoint = [info draggingLocation]; - for (std::vector<const BookmarkNode*>::const_iterator it = nodes.begin(); - it != nodes.end(); ++it) { - const BookmarkNode* sourceNode = *it; - dragged = [self dragBookmark:sourceNode to:dropPoint copy:copy]; - } - } - return dragged; -} - -- (std::vector<const BookmarkNode*>)retrieveBookmarkNodeData { - std::vector<const BookmarkNode*> dragDataNodes; - BookmarkNodeData dragData; - if(dragData.ReadFromDragClipboard()) { - BookmarkModel* bookmarkModel = [self bookmarkModel]; - Profile* profile = bookmarkModel->profile(); - std::vector<const BookmarkNode*> nodes(dragData.GetNodes(profile)); - dragDataNodes.assign(nodes.begin(), nodes.end()); - } - return dragDataNodes; -} - -// Return YES if we should show the drop indicator, else NO. -- (BOOL)shouldShowIndicatorShownForPoint:(NSPoint)point { - return ![self buttonForDroppingOnAtPoint:point]; -} - -// Return the x position for a drop indicator. -- (CGFloat)indicatorPosForDragToPoint:(NSPoint)point { - CGFloat x = 0; - int destIndex = [self indexForDragToPoint:point]; - int numButtons = displayedButtonCount_; - - // If it's a drop strictly between existing buttons ... - if (destIndex >= 0 && destIndex < numButtons) { - // ... put the indicator right between the buttons. - BookmarkButton* button = - [buttons_ objectAtIndex:static_cast<NSUInteger>(destIndex)]; - DCHECK(button); - NSRect buttonFrame = [button frame]; - x = buttonFrame.origin.x - 0.5 * bookmarks::kBookmarkHorizontalPadding; - - // If it's a drop at the end (past the last button, if there are any) ... - } else if (destIndex == numButtons) { - // and if it's past the last button ... - if (numButtons > 0) { - // ... find the last button, and put the indicator to its right. - BookmarkButton* button = - [buttons_ objectAtIndex:static_cast<NSUInteger>(destIndex - 1)]; - DCHECK(button); - NSRect buttonFrame = [button frame]; - x = NSMaxX(buttonFrame) + 0.5 * bookmarks::kBookmarkHorizontalPadding; - - // Otherwise, put it right at the beginning. - } else { - x = 0.5 * bookmarks::kBookmarkHorizontalPadding; - } - } else { - NOTREACHED(); - } - - return x; -} - -- (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child { - // If the bookmarkbar is not in detached mode, lock bar visibility, forcing - // the overlay to stay open when in fullscreen mode. - if (![self isInState:bookmarks::kDetachedState] && - ![self isAnimatingToState:bookmarks::kDetachedState]) { - BrowserWindowController* browserController = - [BrowserWindowController browserWindowControllerForView:[self view]]; - [browserController lockBarVisibilityForOwner:child - withAnimation:NO - delay:NO]; - } -} - -- (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child { - // Release bar visibility, allowing the overlay to close if in fullscreen - // mode. - BrowserWindowController* browserController = - [BrowserWindowController browserWindowControllerForView:[self view]]; - [browserController releaseBarVisibilityForOwner:child - withAnimation:NO - delay:NO]; -} - -// Add a new folder controller as triggered by the given folder button. -- (void)addNewFolderControllerWithParentButton:(BookmarkButton*)parentButton { - - // If doing a close/open, make sure the fullscreen chrome doesn't - // have a chance to begin animating away in the middle of things. - BrowserWindowController* browserController = - [BrowserWindowController browserWindowControllerForView:[self view]]; - // Confirm we're not re-locking with ourself as an owner before locking. - DCHECK([browserController isBarVisibilityLockedForOwner:self] == NO); - [browserController lockBarVisibilityForOwner:self - withAnimation:NO - delay:NO]; - - if (folderController_) - [self closeAllBookmarkFolders]; - - // Folder controller, like many window controllers, owns itself. - folderController_ = - [[BookmarkBarFolderController alloc] initWithParentButton:parentButton - parentController:nil - barController:self]; - [folderController_ showWindow:self]; - - // Only BookmarkBarController has this; the - // BookmarkBarFolderController does not. - [self watchForExitEvent:YES]; - - // No longer need to hold the lock; the folderController_ now owns it. - [browserController releaseBarVisibilityForOwner:self - withAnimation:NO - delay:NO]; -} - -- (void)openAll:(const BookmarkNode*)node - disposition:(WindowOpenDisposition)disposition { - [self closeFolderAndStopTrackingMenus]; - bookmark_utils::OpenAll([[self view] window], - browser_->profile(), - browser_, - node, - disposition); -} - -- (void)addButtonForNode:(const BookmarkNode*)node - atIndex:(NSInteger)buttonIndex { - 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): Duplicate code with BookmarkBarFolderController. -// http://crbug.com/35966 -- (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point { - DCHECK([urls count] == [titles count]); - BOOL nodesWereAdded = NO; - // Figure out where these new bookmarks nodes are to be added. - BookmarkButton* button = [self buttonForDroppingOnAtPoint:point]; - const BookmarkNode* destParent = NULL; - int destIndex = 0; - if ([button isFolder]) { - destParent = [button bookmarkNode]; - // Drop it at the end. - destIndex = [button bookmarkNode]->GetChildCount(); - } else { - // Else we're dropping somewhere on the bar, so find the right spot. - destParent = bookmarkModel_->GetBookmarkBarNode(); - destIndex = [self indexForDragToPoint:point]; - } - - // Don't add the bookmarks if the destination index shows an error. - if (destIndex >= 0) { - // Create and add the new bookmark nodes. - size_t urlCount = [urls count]; - for (size_t i = 0; i < urlCount; ++i) { - GURL gurl; - const char* string = [[urls objectAtIndex:i] UTF8String]; - if (string) - gurl = GURL(string); - // We only expect to receive valid URLs. - DCHECK(gurl.is_valid()); - if (gurl.is_valid()) { - bookmarkModel_->AddURL(destParent, - destIndex++, - base::SysNSStringToUTF16( - [titles objectAtIndex:i]), - gurl); - nodesWereAdded = YES; - } - } - } - return nodesWereAdded; -} - -// TODO(mrossetti): jrg wants this broken up into smaller functions. -- (void)moveButtonFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex { - if (fromIndex != toIndex) { - 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]) { - // The button being removed is showing in the bar. - BookmarkButton* oldButton = [buttons_ objectAtIndex:buttonIndex]; - if (oldButton == [folderController_ parentButton]) { - // If we are deleting a button whose folder is currently open, close it! - [self closeAllBookmarkFolders]; - } - NSPoint poofPoint = [oldButton screenLocationForRemoveAnimation]; - NSRect oldFrame = [oldButton frame]; - [oldButton setDelegate:nil]; - [oldButton removeFromSuperview]; - if (animate && !ignoreAnimations_ && [self isVisible]) - 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]; - } else if (folderController_ && - [folderController_ parentButton] == offTheSideButton_) { - // The button being removed is in the OTS (off-the-side) and the OTS - // menu is showing so we need to remove the button. - NSInteger index = buttonIndex - displayedButtonCount_; - [folderController_ removeButton:index animate:YES]; - } -} - -- (id<BookmarkButtonControllerProtocol>)controllerForNode: - (const BookmarkNode*)node { - // See if it's in the bar, then if it is in the hierarchy of visible - // folder menus. - if (bookmarkModel_->GetBookmarkBarNode() == node) - return self; - return [folderController_ controllerForNode:node]; -} - -#pragma mark BookmarkButtonControllerProtocol - -// NOT an override of a standard Cocoa call made to NSViewControllers. -- (void)hookForEvent:(NSEvent*)theEvent { - if ([self isEventAnExitEvent:theEvent]) - [self closeFolderAndStopTrackingMenus]; -} - -#pragma mark TestingAPI Only - -- (NSMenu*)buttonContextMenu { - return buttonContextMenu_; -} - -// Intentionally ignores ownership issues; used for testing and we try -// to minimize touching the object passed in (likely a mock). -- (void)setButtonContextMenu:(id)menu { - buttonContextMenu_ = menu; -} - -- (void)setIgnoreAnimations:(BOOL)ignore { - ignoreAnimations_ = ignore; -} - -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_controller_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_controller_unittest.mm deleted file mode 100644 index 2d72804..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_controller_unittest.mm +++ /dev/null @@ -1,2169 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#include "app/theme_provider.h" -#include "base/basictypes.h" -#include "base/scoped_nsobject.h" -#include "base/string16.h" -#include "base/string_util.h" -#include "base/sys_string_conversions.h" -#include "base/utf_string_conversions.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_constants.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_window.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_unittest_helper.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_view.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button_cell.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_menu.h" -#include "chrome/browser/cocoa/browser_test_helper.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" -#include "chrome/browser/cocoa/test_event_utils.h" -#import "chrome/browser/cocoa/view_resizer_pong.h" -#include "chrome/common/pref_names.h" -#include "chrome/test/model_test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" -#import "testing/gtest_mac.h" -#include "testing/platform_test.h" -#import "third_party/ocmock/OCMock/OCMock.h" - -// Just like a BookmarkBarController but openURL: is stubbed out. -@interface BookmarkBarControllerNoOpen : BookmarkBarController { - @public - std::vector<GURL> urls_; - std::vector<WindowOpenDisposition> dispositions_; -} -@end - -@implementation BookmarkBarControllerNoOpen -- (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition { - urls_.push_back(url); - dispositions_.push_back(disposition); -} -- (void)clear { - urls_.clear(); - dispositions_.clear(); -} -@end - - -// NSCell that is pre-provided with a desired size that becomes the -// return value for -(NSSize)cellSize:. -@interface CellWithDesiredSize : NSCell { - @private - NSSize cellSize_; -} -@property (nonatomic, readonly) NSSize cellSize; -@end - -@implementation CellWithDesiredSize - -@synthesize cellSize = cellSize_; - -- (id)initTextCell:(NSString*)string desiredSize:(NSSize)size { - if ((self = [super initTextCell:string])) { - cellSize_ = size; - } - return self; -} - -@end - -// Remember the number of times we've gotten a frameDidChange notification. -@interface BookmarkBarControllerTogglePong : BookmarkBarControllerNoOpen { - @private - int toggles_; -} -@property (nonatomic, readonly) int toggles; -@end - -@implementation BookmarkBarControllerTogglePong - -@synthesize toggles = toggles_; - -- (void)frameDidChange { - toggles_++; -} - -@end - -// Remembers if a notification callback was called. -@interface BookmarkBarControllerNotificationPong : BookmarkBarControllerNoOpen { - BOOL windowWillCloseReceived_; - BOOL windowDidResignKeyReceived_; -} -@property (nonatomic, readonly) BOOL windowWillCloseReceived; -@property (nonatomic, readonly) BOOL windowDidResignKeyReceived; -@end - -@implementation BookmarkBarControllerNotificationPong -@synthesize windowWillCloseReceived = windowWillCloseReceived_; -@synthesize windowDidResignKeyReceived = windowDidResignKeyReceived_; - -// Override NSNotificationCenter callback. -- (void)parentWindowWillClose:(NSNotification*)notification { - windowWillCloseReceived_ = YES; -} - -// NSNotificationCenter callback. -- (void)parentWindowDidResignKey:(NSNotification*)notification { - windowDidResignKeyReceived_ = YES; -} -@end - -// Remembers if and what kind of openAll was performed. -@interface BookmarkBarControllerOpenAllPong : BookmarkBarControllerNoOpen { - WindowOpenDisposition dispositionDetected_; -} -@property (nonatomic) WindowOpenDisposition dispositionDetected; -@end - -@implementation BookmarkBarControllerOpenAllPong -@synthesize dispositionDetected = dispositionDetected_; - -// Intercede for the openAll:disposition: method. -- (void)openAll:(const BookmarkNode*)node - disposition:(WindowOpenDisposition)disposition { - [self setDispositionDetected:disposition]; -} - -@end - -// Just like a BookmarkBarController but intercedes when providing -// pasteboard drag data. -@interface BookmarkBarControllerDragData : BookmarkBarController { - const BookmarkNode* dragDataNode_; // Weak -} -- (void)setDragDataNode:(const BookmarkNode*)node; -@end - -@implementation BookmarkBarControllerDragData - -- (id)initWithBrowser:(Browser*)browser - initialWidth:(CGFloat)initialWidth - delegate:(id<BookmarkBarControllerDelegate>)delegate - resizeDelegate:(id<ViewResizer>)resizeDelegate { - if ((self = [super initWithBrowser:browser - initialWidth:initialWidth - delegate:delegate - resizeDelegate:resizeDelegate])) { - dragDataNode_ = NULL; - } - return self; -} - -- (void)setDragDataNode:(const BookmarkNode*)node { - dragDataNode_ = node; -} - -- (std::vector<const BookmarkNode*>)retrieveBookmarkNodeData { - std::vector<const BookmarkNode*> dragDataNodes; - if(dragDataNode_) { - dragDataNodes.push_back(dragDataNode_); - } - return dragDataNodes; -} - -@end - - -class FakeTheme : public ThemeProvider { - public: - FakeTheme(NSColor* color) : color_(color) { } - scoped_nsobject<NSColor> color_; - - virtual void Init(Profile* profile) { } - virtual SkBitmap* GetBitmapNamed(int id) const { return nil; } - virtual SkColor GetColor(int id) const { return SkColor(); } - virtual bool GetDisplayProperty(int id, int* result) const { return false; } - virtual bool ShouldUseNativeFrame() const { return false; } - virtual bool HasCustomImage(int id) const { return false; } - virtual RefCountedMemory* GetRawData(int id) const { return NULL; } - virtual NSImage* GetNSImageNamed(int id, bool allow_default) const { - return nil; - } - virtual NSColor* GetNSImageColorNamed(int id, bool allow_default) const { - return nil; - } - virtual NSColor* GetNSColor(int id, bool allow_default) const { - return color_.get(); - } - virtual NSColor* GetNSColorTint(int id, bool allow_default) const { - return nil; - } - virtual NSGradient* GetNSGradient(int id) const { - return nil; - } -}; - - -@interface FakeDragInfo : NSObject { - @public - NSPoint dropLocation_; - NSDragOperation sourceMask_; -} -@property (nonatomic, assign) NSPoint dropLocation; -- (void)setDraggingSourceOperationMask:(NSDragOperation)mask; -@end - -@implementation FakeDragInfo - -@synthesize dropLocation = dropLocation_; - -- (id)init { - if ((self = [super init])) { - dropLocation_ = NSZeroPoint; - sourceMask_ = NSDragOperationMove; - } - return self; -} - -// NSDraggingInfo protocol functions. - -- (id)draggingPasteboard { - return self; -} - -- (id)draggingSource { - return self; -} - -- (NSDragOperation)draggingSourceOperationMask { - return sourceMask_; -} - -- (NSPoint)draggingLocation { - return dropLocation_; -} - -// Other functions. - -- (void)setDraggingSourceOperationMask:(NSDragOperation)mask { - sourceMask_ = mask; -} - -@end - - -namespace { - -class BookmarkBarControllerTestBase : public CocoaTest { - public: - BrowserTestHelper helper_; - scoped_nsobject<NSView> parent_view_; - scoped_nsobject<ViewResizerPong> resizeDelegate_; - - BookmarkBarControllerTestBase() { - resizeDelegate_.reset([[ViewResizerPong alloc] init]); - NSRect parent_frame = NSMakeRect(0, 0, 800, 50); - parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]); - [parent_view_ setHidden:YES]; - } - - void InstallAndToggleBar(BookmarkBarController* bar) { - // Force loading of the nib. - [bar view]; - // Awkwardness to look like we've been installed. - for (NSView* subView in [parent_view_ subviews]) - [subView removeFromSuperview]; - [parent_view_ addSubview:[bar view]]; - NSRect frame = [[[bar view] superview] frame]; - frame.origin.y = 100; - [[[bar view] superview] setFrame:frame]; - - // Make sure it's on in a window so viewDidMoveToWindow is called - NSView* contentView = [test_window() contentView]; - if (![parent_view_ isDescendantOf:contentView]) - [contentView addSubview:parent_view_]; - - // Make sure it's open so certain things aren't no-ops. - [bar updateAndShowNormalBar:YES - showDetachedBar:NO - withAnimation:NO]; - } -}; - -class BookmarkBarControllerTest : public BookmarkBarControllerTestBase { - public: - scoped_nsobject<BookmarkMenu> menu_; - scoped_nsobject<NSMenuItem> menu_item_; - scoped_nsobject<NSButtonCell> cell_; - scoped_nsobject<BookmarkBarControllerNoOpen> bar_; - - BookmarkBarControllerTest() { - bar_.reset( - [[BookmarkBarControllerNoOpen alloc] - initWithBrowser:helper_.browser() - initialWidth:NSWidth([parent_view_ frame]) - delegate:nil - resizeDelegate:resizeDelegate_.get()]); - - InstallAndToggleBar(bar_.get()); - - // Create a menu/item to act like a sender - menu_.reset([[BookmarkMenu alloc] initWithTitle:@"I_dont_care"]); - menu_item_.reset([[NSMenuItem alloc] - initWithTitle:@"still_dont_care" - action:NULL - keyEquivalent:@""]); - cell_.reset([[NSButtonCell alloc] init]); - [menu_item_ setMenu:menu_.get()]; - [menu_ setDelegate:cell_.get()]; - } - - // Return a menu item that points to the given URL. - NSMenuItem* ItemForBookmarkBarMenu(GURL& gurl) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - const BookmarkNode* node = model->AddURL(parent, parent->GetChildCount(), - ASCIIToUTF16("A title"), gurl); - [menu_ setRepresentedObject:[NSNumber numberWithLongLong:node->id()]]; - return menu_item_; - } - - // Does NOT take ownership of node. - NSMenuItem* ItemForBookmarkBarMenu(const BookmarkNode* node) { - [menu_ setRepresentedObject:[NSNumber numberWithLongLong:node->id()]]; - return menu_item_; - } - - BookmarkBarControllerNoOpen* noOpenBar() { - return (BookmarkBarControllerNoOpen*)bar_.get(); - } -}; - -TEST_F(BookmarkBarControllerTest, ShowWhenShowBookmarkBarTrue) { - [bar_ updateAndShowNormalBar:YES - showDetachedBar:NO - withAnimation:NO]; - EXPECT_TRUE([bar_ isInState:bookmarks::kShowingState]); - EXPECT_FALSE([bar_ isInState:bookmarks::kDetachedState]); - EXPECT_TRUE([bar_ isVisible]); - EXPECT_FALSE([bar_ isAnimationRunning]); - EXPECT_FALSE([[bar_ view] isHidden]); - EXPECT_GT([resizeDelegate_ height], 0); - EXPECT_GT([[bar_ view] frame].size.height, 0); -} - -TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarFalse) { - [bar_ updateAndShowNormalBar:NO - showDetachedBar:NO - withAnimation:NO]; - EXPECT_FALSE([bar_ isInState:bookmarks::kShowingState]); - EXPECT_FALSE([bar_ isInState:bookmarks::kDetachedState]); - EXPECT_FALSE([bar_ isVisible]); - EXPECT_FALSE([bar_ isAnimationRunning]); - EXPECT_TRUE([[bar_ view] isHidden]); - EXPECT_EQ(0, [resizeDelegate_ height]); - EXPECT_EQ(0, [[bar_ view] frame].size.height); -} - -TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarTrueButDisabled) { - [bar_ setBookmarkBarEnabled:NO]; - [bar_ updateAndShowNormalBar:YES - showDetachedBar:NO - withAnimation:NO]; - EXPECT_TRUE([bar_ isInState:bookmarks::kShowingState]); - EXPECT_FALSE([bar_ isInState:bookmarks::kDetachedState]); - EXPECT_FALSE([bar_ isVisible]); - EXPECT_FALSE([bar_ isAnimationRunning]); - EXPECT_TRUE([[bar_ view] isHidden]); - EXPECT_EQ(0, [resizeDelegate_ height]); - EXPECT_EQ(0, [[bar_ view] frame].size.height); -} - -TEST_F(BookmarkBarControllerTest, ShowOnNewTabPage) { - [bar_ updateAndShowNormalBar:NO - showDetachedBar:YES - withAnimation:NO]; - EXPECT_FALSE([bar_ isInState:bookmarks::kShowingState]); - EXPECT_TRUE([bar_ isInState:bookmarks::kDetachedState]); - EXPECT_TRUE([bar_ isVisible]); - EXPECT_FALSE([bar_ isAnimationRunning]); - EXPECT_FALSE([[bar_ view] isHidden]); - EXPECT_GT([resizeDelegate_ height], 0); - EXPECT_GT([[bar_ view] frame].size.height, 0); - - // Make sure no buttons fall off the bar, either now or when resized - // bigger or smaller. - CGFloat sizes[] = { 300.0, -100.0, 200.0, -420.0 }; - CGFloat previousX = 0.0; - for (unsigned x = 0; x < arraysize(sizes); x++) { - // Confirm the buttons moved from the last check (which may be - // init but that's fine). - CGFloat newX = [[bar_ offTheSideButton] frame].origin.x; - EXPECT_NE(previousX, newX); - previousX = newX; - - // Confirm the buttons have a reasonable bounds. Recall that |-frame| - // returns rectangles in the superview's coordinates. - NSRect buttonViewFrame = - [[bar_ buttonView] convertRect:[[bar_ buttonView] frame] - fromView:[[bar_ buttonView] superview]]; - EXPECT_EQ([bar_ buttonView], [[bar_ offTheSideButton] superview]); - EXPECT_TRUE(NSContainsRect(buttonViewFrame, - [[bar_ offTheSideButton] frame])); - EXPECT_EQ([bar_ buttonView], [[bar_ otherBookmarksButton] superview]); - EXPECT_TRUE(NSContainsRect(buttonViewFrame, - [[bar_ otherBookmarksButton] frame])); - - // Now move them implicitly. - // We confirm FrameChangeNotification works in the next unit test; - // we simply assume it works here to resize or reposition the - // buttons above. - NSRect frame = [[bar_ view] frame]; - frame.size.width += sizes[x]; - [[bar_ view] setFrame:frame]; - } -} - -// Test whether |-updateAndShowNormalBar:...| sets states as we expect. Make -// sure things don't crash. -TEST_F(BookmarkBarControllerTest, StateChanges) { - // First, go in one-at-a-time cycle. - [bar_ updateAndShowNormalBar:NO - showDetachedBar:NO - withAnimation:NO]; - EXPECT_EQ(bookmarks::kHiddenState, [bar_ visualState]); - EXPECT_FALSE([bar_ isVisible]); - EXPECT_FALSE([bar_ isAnimationRunning]); - [bar_ updateAndShowNormalBar:YES - showDetachedBar:NO - withAnimation:NO]; - EXPECT_EQ(bookmarks::kShowingState, [bar_ visualState]); - EXPECT_TRUE([bar_ isVisible]); - EXPECT_FALSE([bar_ isAnimationRunning]); - [bar_ updateAndShowNormalBar:YES - showDetachedBar:YES - withAnimation:NO]; - EXPECT_EQ(bookmarks::kShowingState, [bar_ visualState]); - EXPECT_TRUE([bar_ isVisible]); - EXPECT_FALSE([bar_ isAnimationRunning]); - [bar_ updateAndShowNormalBar:NO - showDetachedBar:YES - withAnimation:NO]; - EXPECT_EQ(bookmarks::kDetachedState, [bar_ visualState]); - EXPECT_TRUE([bar_ isVisible]); - EXPECT_FALSE([bar_ isAnimationRunning]); - - // Now try some "jumps". - for (int i = 0; i < 2; i++) { - [bar_ updateAndShowNormalBar:NO - showDetachedBar:NO - withAnimation:NO]; - EXPECT_EQ(bookmarks::kHiddenState, [bar_ visualState]); - EXPECT_FALSE([bar_ isVisible]); - EXPECT_FALSE([bar_ isAnimationRunning]); - [bar_ updateAndShowNormalBar:YES - showDetachedBar:YES - withAnimation:NO]; - EXPECT_EQ(bookmarks::kShowingState, [bar_ visualState]); - EXPECT_TRUE([bar_ isVisible]); - EXPECT_FALSE([bar_ isAnimationRunning]); - } - - // Now try some "jumps". - for (int i = 0; i < 2; i++) { - [bar_ updateAndShowNormalBar:YES - showDetachedBar:NO - withAnimation:NO]; - EXPECT_EQ(bookmarks::kShowingState, [bar_ visualState]); - EXPECT_TRUE([bar_ isVisible]); - EXPECT_FALSE([bar_ isAnimationRunning]); - [bar_ updateAndShowNormalBar:NO - showDetachedBar:YES - withAnimation:NO]; - EXPECT_EQ(bookmarks::kDetachedState, [bar_ visualState]); - EXPECT_TRUE([bar_ isVisible]); - EXPECT_FALSE([bar_ isAnimationRunning]); - } -} - -// Make sure we're watching for frame change notifications. -TEST_F(BookmarkBarControllerTest, FrameChangeNotification) { - scoped_nsobject<BookmarkBarControllerTogglePong> bar; - bar.reset( - [[BookmarkBarControllerTogglePong alloc] - initWithBrowser:helper_.browser() - initialWidth:100 // arbitrary - delegate:nil - resizeDelegate:resizeDelegate_.get()]); - InstallAndToggleBar(bar.get()); - - // Send a frame did change notification for the pong's view. - [[NSNotificationCenter defaultCenter] - postNotificationName:NSViewFrameDidChangeNotification - object:[bar view]]; - - EXPECT_GT([bar toggles], 0); -} - -// Confirm our "no items" container goes away when we add the 1st -// bookmark, and comes back when we delete the bookmark. -TEST_F(BookmarkBarControllerTest, NoItemContainerGoesAway) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* bar = model->GetBookmarkBarNode(); - - [bar_ loaded:model]; - BookmarkBarView* view = [bar_ buttonView]; - DCHECK(view); - NSView* noItemContainer = [view noItemContainer]; - DCHECK(noItemContainer); - - EXPECT_FALSE([noItemContainer isHidden]); - const BookmarkNode* node = model->AddURL(bar, bar->GetChildCount(), - ASCIIToUTF16("title"), - GURL("http://www.google.com")); - EXPECT_TRUE([noItemContainer isHidden]); - model->Remove(bar, bar->IndexOfChild(node)); - EXPECT_FALSE([noItemContainer isHidden]); - - // Now try it using a bookmark from the Other Bookmarks. - const BookmarkNode* otherBookmarks = model->other_node(); - node = model->AddURL(otherBookmarks, otherBookmarks->GetChildCount(), - ASCIIToUTF16("TheOther"), - GURL("http://www.other.com")); - EXPECT_FALSE([noItemContainer isHidden]); - // Move it from Other Bookmarks to the bar. - model->Move(node, bar, 0); - EXPECT_TRUE([noItemContainer isHidden]); - // Move it back to Other Bookmarks from the bar. - model->Move(node, otherBookmarks, 0); - EXPECT_FALSE([noItemContainer isHidden]); -} - -// Confirm off the side button only enabled when reasonable. -TEST_F(BookmarkBarControllerTest, OffTheSideButtonHidden) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - [bar_ setIgnoreAnimations:YES]; - - [bar_ loaded:model]; - EXPECT_TRUE([bar_ offTheSideButtonIsHidden]); - - for (int i = 0; i < 2; i++) { - model->SetURLStarred(GURL("http://www.foo.com"), ASCIIToUTF16("small"), - true); - EXPECT_TRUE([bar_ offTheSideButtonIsHidden]); - } - - const BookmarkNode* parent = model->GetBookmarkBarNode(); - for (int i = 0; i < 20; i++) { - model->AddURL(parent, parent->GetChildCount(), - ASCIIToUTF16("super duper wide title"), - GURL("http://superfriends.hall-of-justice.edu")); - } - EXPECT_FALSE([bar_ offTheSideButtonIsHidden]); - - // Open the "off the side" and start deleting nodes. Make sure - // deletion of the last node in "off the side" causes the folder to - // close. - EXPECT_FALSE([bar_ offTheSideButtonIsHidden]); - NSButton* offTheSideButton = [bar_ offTheSideButton]; - // Open "off the side" menu. - [bar_ openOffTheSideFolderFromButton:offTheSideButton]; - BookmarkBarFolderController* bbfc = [bar_ folderController]; - EXPECT_TRUE(bbfc); - [bbfc setIgnoreAnimations:YES]; - while (parent->GetChildCount()) { - // We've completed the job so we're done. - if ([bar_ offTheSideButtonIsHidden]) - break; - // Delete the last button. - model->Remove(parent, parent->GetChildCount()-1); - // If last one make sure the menu is closed and the button is hidden. - // Else make sure menu stays open. - if ([bar_ offTheSideButtonIsHidden]) { - EXPECT_FALSE([bar_ folderController]); - } else { - EXPECT_TRUE([bar_ folderController]); - } - } -} - -// http://crbug.com/46175 is a crash when deleting bookmarks from the -// off-the-side menu while it is open. This test tries to bang hard -// in this area to reproduce the crash. -TEST_F(BookmarkBarControllerTest, DeleteFromOffTheSideWhileItIsOpen) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - [bar_ setIgnoreAnimations:YES]; - [bar_ loaded:model]; - - // Add a lot of bookmarks (per the bug). - const BookmarkNode* parent = model->GetBookmarkBarNode(); - for (int i = 0; i < 100; i++) { - std::ostringstream title; - title << "super duper wide title " << i; - model->AddURL(parent, parent->GetChildCount(), ASCIIToUTF16(title.str()), - GURL("http://superfriends.hall-of-justice.edu")); - } - EXPECT_FALSE([bar_ offTheSideButtonIsHidden]); - - // Open "off the side" menu. - NSButton* offTheSideButton = [bar_ offTheSideButton]; - [bar_ openOffTheSideFolderFromButton:offTheSideButton]; - BookmarkBarFolderController* bbfc = [bar_ folderController]; - EXPECT_TRUE(bbfc); - [bbfc setIgnoreAnimations:YES]; - - // Start deleting items; try and delete randomish ones in case it - // makes a difference. - int indices[] = { 2, 4, 5, 1, 7, 9, 2, 0, 10, 9 }; - while (parent->GetChildCount()) { - for (unsigned int i = 0; i < arraysize(indices); i++) { - if (indices[i] < parent->GetChildCount()) { - // First we mouse-enter the button to make things harder. - NSArray* buttons = [bbfc buttons]; - for (BookmarkButton* button in buttons) { - if ([button bookmarkNode] == parent->GetChild(indices[i])) { - [bbfc mouseEnteredButton:button event:nil]; - break; - } - } - // Then we remove the node. This triggers the button to get - // deleted. - model->Remove(parent, indices[i]); - // Force visual update which is otherwise delayed. - [[bbfc window] displayIfNeeded]; - } - } - } -} - -// Test whether |-dragShouldLockBarVisibility| returns NO iff the bar is -// detached. -TEST_F(BookmarkBarControllerTest, TestDragShouldLockBarVisibility) { - [bar_ updateAndShowNormalBar:NO - showDetachedBar:NO - withAnimation:NO]; - EXPECT_TRUE([bar_ dragShouldLockBarVisibility]); - - [bar_ updateAndShowNormalBar:YES - showDetachedBar:NO - withAnimation:NO]; - EXPECT_TRUE([bar_ dragShouldLockBarVisibility]); - - [bar_ updateAndShowNormalBar:YES - showDetachedBar:YES - withAnimation:NO]; - EXPECT_TRUE([bar_ dragShouldLockBarVisibility]); - - [bar_ updateAndShowNormalBar:NO - showDetachedBar:YES - withAnimation:NO]; - EXPECT_FALSE([bar_ dragShouldLockBarVisibility]); -} - -TEST_F(BookmarkBarControllerTest, TagMap) { - int64 ids[] = { 1, 3, 4, 40, 400, 4000, 800000000, 2, 123456789 }; - std::vector<int32> tags; - - // Generate some tags - for (unsigned int i = 0; i < arraysize(ids); i++) { - tags.push_back([bar_ menuTagFromNodeId:ids[i]]); - } - - // Confirm reverse mapping. - for (unsigned int i = 0; i < arraysize(ids); i++) { - EXPECT_EQ(ids[i], [bar_ nodeIdFromMenuTag:tags[i]]); - } - - // Confirm uniqueness. - std::sort(tags.begin(), tags.end()); - for (unsigned int i=0; i<(tags.size()-1); i++) { - EXPECT_NE(tags[i], tags[i+1]); - } -} - -TEST_F(BookmarkBarControllerTest, MenuForFolderNode) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - - // First make sure something (e.g. "(empty)" string) is always present. - NSMenu* menu = [bar_ menuForFolderNode:model->GetBookmarkBarNode()]; - EXPECT_GT([menu numberOfItems], 0); - - // Test two bookmarks. - GURL gurl("http://www.foo.com"); - model->SetURLStarred(gurl, ASCIIToUTF16("small"), true); - model->SetURLStarred(GURL("http://www.cnn.com"), ASCIIToUTF16("bigger title"), - true); - menu = [bar_ menuForFolderNode:model->GetBookmarkBarNode()]; - EXPECT_EQ([menu numberOfItems], 2); - NSMenuItem *item = [menu itemWithTitle:@"bigger title"]; - EXPECT_TRUE(item); - item = [menu itemWithTitle:@"small"]; - EXPECT_TRUE(item); - if (item) { - int64 tag = [bar_ nodeIdFromMenuTag:[item tag]]; - const BookmarkNode* node = model->GetNodeByID(tag); - EXPECT_TRUE(node); - EXPECT_EQ(gurl, node->GetURL()); - } - - // Test with an actual folder as well - const BookmarkNode* parent = model->GetBookmarkBarNode(); - const BookmarkNode* folder = model->AddGroup(parent, - parent->GetChildCount(), - ASCIIToUTF16("group")); - model->AddURL(folder, folder->GetChildCount(), - ASCIIToUTF16("f1"), GURL("http://framma-lamma.com")); - model->AddURL(folder, folder->GetChildCount(), - ASCIIToUTF16("f2"), GURL("http://framma-lamma-ding-dong.com")); - menu = [bar_ menuForFolderNode:model->GetBookmarkBarNode()]; - EXPECT_EQ([menu numberOfItems], 3); - - item = [menu itemWithTitle:@"group"]; - EXPECT_TRUE(item); - EXPECT_TRUE([item hasSubmenu]); - NSMenu *submenu = [item submenu]; - EXPECT_TRUE(submenu); - EXPECT_EQ(2, [submenu numberOfItems]); - EXPECT_TRUE([submenu itemWithTitle:@"f1"]); - EXPECT_TRUE([submenu itemWithTitle:@"f2"]); -} - -// Confirm openBookmark: forwards the request to the controller's delegate -TEST_F(BookmarkBarControllerTest, OpenBookmark) { - GURL gurl("http://walla.walla.ding.dong.com"); - scoped_ptr<BookmarkNode> node(new BookmarkNode(gurl)); - - scoped_nsobject<BookmarkButtonCell> cell([[BookmarkButtonCell alloc] init]); - [cell setBookmarkNode:node.get()]; - scoped_nsobject<BookmarkButton> button([[BookmarkButton alloc] init]); - [button setCell:cell.get()]; - [cell setRepresentedObject:[NSValue valueWithPointer:node.get()]]; - - [bar_ openBookmark:button]; - EXPECT_EQ(noOpenBar()->urls_[0], node->GetURL()); - EXPECT_EQ(noOpenBar()->dispositions_[0], CURRENT_TAB); -} - -// Confirm opening of bookmarks works from the menus (different -// dispositions than clicking on the button). -TEST_F(BookmarkBarControllerTest, OpenBookmarkFromMenus) { - const char* urls[] = { "http://walla.walla.ding.dong.com", - "http://i_dont_know.com", - "http://cee.enn.enn.dot.com" }; - SEL selectors[] = { @selector(openBookmarkInNewForegroundTab:), - @selector(openBookmarkInNewWindow:), - @selector(openBookmarkInIncognitoWindow:) }; - WindowOpenDisposition dispositions[] = { NEW_FOREGROUND_TAB, - NEW_WINDOW, - OFF_THE_RECORD }; - for (unsigned int i = 0; i < arraysize(dispositions); i++) { - GURL gurl(urls[i]); - [bar_ performSelector:selectors[i] - withObject:ItemForBookmarkBarMenu(gurl)]; - EXPECT_EQ(noOpenBar()->urls_[0], gurl); - EXPECT_EQ(noOpenBar()->dispositions_[0], dispositions[i]); - [bar_ clear]; - } -} - -TEST_F(BookmarkBarControllerTest, TestAddRemoveAndClear) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - NSView* buttonView = [bar_ buttonView]; - EXPECT_EQ(0U, [[bar_ buttons] count]); - unsigned int initial_subview_count = [[buttonView subviews] count]; - - // Make sure a redundant call doesn't choke - [bar_ clearBookmarkBar]; - EXPECT_EQ(0U, [[bar_ buttons] count]); - EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]); - - GURL gurl1("http://superfriends.hall-of-justice.edu"); - // Short titles increase the chances of this test succeeding if the view is - // narrow. - // TODO(viettrungluu): make the test independent of window/view size, font - // metrics, button size and spacing, and everything else. - string16 title1(ASCIIToUTF16("x")); - model->SetURLStarred(gurl1, title1, true); - EXPECT_EQ(1U, [[bar_ buttons] count]); - EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]); - - GURL gurl2("http://legion-of-doom.gov"); - string16 title2(ASCIIToUTF16("y")); - model->SetURLStarred(gurl2, title2, true); - EXPECT_EQ(2U, [[bar_ buttons] count]); - EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]); - - for (int i = 0; i < 3; i++) { - // is_starred=false --> remove the bookmark - model->SetURLStarred(gurl2, title2, false); - EXPECT_EQ(1U, [[bar_ buttons] count]); - EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]); - - // and bring it back - model->SetURLStarred(gurl2, title2, true); - EXPECT_EQ(2U, [[bar_ buttons] count]); - EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]); - } - - [bar_ clearBookmarkBar]; - EXPECT_EQ(0U, [[bar_ buttons] count]); - EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]); - - // Explicit test of loaded: since this is a convenient spot - [bar_ loaded:model]; - EXPECT_EQ(2U, [[bar_ buttons] count]); - EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]); -} - -// Make sure we don't create too many buttons; we only really need -// ones that will be visible. -TEST_F(BookmarkBarControllerTest, TestButtonLimits) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - EXPECT_EQ(0U, [[bar_ buttons] count]); - // Add one; make sure we see it. - const BookmarkNode* parent = model->GetBookmarkBarNode(); - model->AddURL(parent, parent->GetChildCount(), - ASCIIToUTF16("title"), GURL("http://www.google.com")); - EXPECT_EQ(1U, [[bar_ buttons] count]); - - // Add 30 which we expect to be 'too many'. Make sure we don't see - // 30 buttons. - model->Remove(parent, 0); - EXPECT_EQ(0U, [[bar_ buttons] count]); - for (int i=0; i<30; i++) { - model->AddURL(parent, parent->GetChildCount(), - ASCIIToUTF16("title"), GURL("http://www.google.com")); - } - int count = [[bar_ buttons] count]; - EXPECT_LT(count, 30L); - - // Add 10 more (to the front of the list so the on-screen buttons - // would change) and make sure the count stays the same. - for (int i=0; i<10; i++) { - model->AddURL(parent, 0, /* index is 0, so front, not end */ - ASCIIToUTF16("title"), GURL("http://www.google.com")); - } - - // Finally, grow the view and make sure the button count goes up. - NSRect frame = [[bar_ view] frame]; - frame.size.width += 600; - [[bar_ view] setFrame:frame]; - int finalcount = [[bar_ buttons] count]; - EXPECT_GT(finalcount, count); -} - -// Make sure that each button we add marches to the right and does not -// overlap with the previous one. -TEST_F(BookmarkBarControllerTest, TestButtonMarch) { - scoped_nsobject<NSMutableArray> cells([[NSMutableArray alloc] init]); - - CGFloat widths[] = { 10, 10, 100, 10, 500, 500, 80000, 60000, 1, 345 }; - for (unsigned int i = 0; i < arraysize(widths); i++) { - NSCell* cell = [[CellWithDesiredSize alloc] - initTextCell:@"foo" - desiredSize:NSMakeSize(widths[i], 30)]; - [cells addObject:cell]; - [cell release]; - } - - int x_offset = 0; - CGFloat x_end = x_offset; // end of the previous button - for (unsigned int i = 0; i < arraysize(widths); i++) { - NSRect r = [bar_ frameForBookmarkButtonFromCell:[cells objectAtIndex:i] - xOffset:&x_offset]; - EXPECT_GE(r.origin.x, x_end); - x_end = NSMaxX(r); - } -} - -TEST_F(BookmarkBarControllerTest, CheckForGrowth) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - GURL gurl1("http://www.google.com"); - string16 title1(ASCIIToUTF16("x")); - model->SetURLStarred(gurl1, title1, true); - - GURL gurl2("http://www.google.com/blah"); - string16 title2(ASCIIToUTF16("y")); - model->SetURLStarred(gurl2, title2, true); - - EXPECT_EQ(2U, [[bar_ buttons] count]); - CGFloat width_1 = [[[bar_ buttons] objectAtIndex:0] frame].size.width; - CGFloat x_2 = [[[bar_ buttons] objectAtIndex:1] frame].origin.x; - - NSButton* first = [[bar_ buttons] objectAtIndex:0]; - [[first cell] setTitle:@"This is a really big title; watch out mom!"]; - [bar_ checkForBookmarkButtonGrowth:first]; - - // Make sure the 1st button is now wider, the 2nd one is moved over, - // and they don't overlap. - NSRect frame_1 = [[[bar_ buttons] objectAtIndex:0] frame]; - NSRect frame_2 = [[[bar_ buttons] objectAtIndex:1] frame]; - EXPECT_GT(frame_1.size.width, width_1); - EXPECT_GT(frame_2.origin.x, x_2); - EXPECT_GE(frame_2.origin.x, frame_1.origin.x + frame_1.size.width); -} - -TEST_F(BookmarkBarControllerTest, DeleteBookmark) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - - const char* urls[] = { "https://secret.url.com", - "http://super.duper.web.site.for.doodz.gov", - "http://www.foo-bar-baz.com/" }; - const BookmarkNode* parent = model->GetBookmarkBarNode(); - for (unsigned int i = 0; i < arraysize(urls); i++) { - model->AddURL(parent, parent->GetChildCount(), - ASCIIToUTF16("title"), GURL(urls[i])); - } - EXPECT_EQ(3, parent->GetChildCount()); - const BookmarkNode* middle_node = parent->GetChild(1); - - NSMenuItem* item = ItemForBookmarkBarMenu(middle_node); - [bar_ deleteBookmark:item]; - EXPECT_EQ(2, parent->GetChildCount()); - EXPECT_EQ(parent->GetChild(0)->GetURL(), GURL(urls[0])); - // node 2 moved into spot 1 - EXPECT_EQ(parent->GetChild(1)->GetURL(), GURL(urls[2])); -} - -// TODO(jrg): write a test to confirm that nodeFavIconLoaded calls -// checkForBookmarkButtonGrowth:. - -TEST_F(BookmarkBarControllerTest, Cell) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - [bar_ loaded:model]; - - const BookmarkNode* parent = model->GetBookmarkBarNode(); - model->AddURL(parent, parent->GetChildCount(), - ASCIIToUTF16("supertitle"), - GURL("http://superfriends.hall-of-justice.edu")); - const BookmarkNode* node = parent->GetChild(0); - - NSCell* cell = [bar_ cellForBookmarkNode:node]; - EXPECT_TRUE(cell); - EXPECT_NSEQ(@"supertitle", [cell title]); - EXPECT_EQ(node, [[cell representedObject] pointerValue]); - EXPECT_TRUE([cell menu]); - - // Empty cells have no menu. - cell = [bar_ cellForBookmarkNode:nil]; - EXPECT_FALSE([cell menu]); - // Even empty cells have a title (of "(empty)") - EXPECT_TRUE([cell title]); - - // cell is autoreleased; no need to release here -} - -// Test drawing, mostly to ensure nothing leaks or crashes. -TEST_F(BookmarkBarControllerTest, Display) { - [[bar_ view] display]; -} - -// Test that middle clicking on a bookmark button results in an open action. -TEST_F(BookmarkBarControllerTest, MiddleClick) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - GURL gurl1("http://www.google.com/"); - string16 title1(ASCIIToUTF16("x")); - model->SetURLStarred(gurl1, title1, true); - - EXPECT_EQ(1U, [[bar_ buttons] count]); - NSButton* first = [[bar_ buttons] objectAtIndex:0]; - EXPECT_TRUE(first); - - [first otherMouseUp:test_event_utils::MakeMouseEvent(NSOtherMouseUp, 0)]; - EXPECT_EQ(noOpenBar()->urls_.size(), 1U); -} - -TEST_F(BookmarkBarControllerTest, DisplaysHelpMessageOnEmpty) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - [bar_ loaded:model]; - EXPECT_FALSE([[[bar_ buttonView] noItemContainer] isHidden]); -} - -TEST_F(BookmarkBarControllerTest, HidesHelpMessageWithBookmark) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - - const BookmarkNode* parent = model->GetBookmarkBarNode(); - model->AddURL(parent, parent->GetChildCount(), - ASCIIToUTF16("title"), GURL("http://one.com")); - - [bar_ loaded:model]; - EXPECT_TRUE([[[bar_ buttonView] noItemContainer] isHidden]); -} - -TEST_F(BookmarkBarControllerTest, BookmarkButtonSizing) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - - const BookmarkNode* parent = model->GetBookmarkBarNode(); - model->AddURL(parent, parent->GetChildCount(), - ASCIIToUTF16("title"), GURL("http://one.com")); - - [bar_ loaded:model]; - - // Make sure the internal bookmark button also is the correct height. - NSArray* buttons = [bar_ buttons]; - EXPECT_GT([buttons count], 0u); - for (NSButton* button in buttons) { - EXPECT_FLOAT_EQ( - (bookmarks::kBookmarkBarHeight + bookmarks::kVisualHeightOffset) - 2 * - bookmarks::kBookmarkVerticalPadding, - [button frame].size.height); - } -} - -TEST_F(BookmarkBarControllerTest, DropBookmarks) { - const char* urls[] = { - "http://qwantz.com", - "http://xkcd.com", - "javascript:alert('lolwut')", - "file://localhost/tmp/local-file.txt" // As if dragged from the desktop. - }; - const char* titles[] = { - "Philosophoraptor", - "Can't draw", - "Inspiration", - "Frum stuf" - }; - EXPECT_EQ(arraysize(urls), arraysize(titles)); - - NSMutableArray* nsurls = [NSMutableArray array]; - NSMutableArray* nstitles = [NSMutableArray array]; - for (size_t i = 0; i < arraysize(urls); ++i) { - [nsurls addObject:base::SysUTF8ToNSString(urls[i])]; - [nstitles addObject:base::SysUTF8ToNSString(titles[i])]; - } - - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - [bar_ addURLs:nsurls withTitles:nstitles at:NSZeroPoint]; - EXPECT_EQ(4, parent->GetChildCount()); - for (int i = 0; i < parent->GetChildCount(); ++i) { - GURL gurl = parent->GetChild(i)->GetURL(); - if (gurl.scheme() == "http" || - gurl.scheme() == "javascript") { - EXPECT_EQ(parent->GetChild(i)->GetURL(), GURL(urls[i])); - } else { - // Be flexible if the scheme needed to be added. - std::string gurl_string = gurl.spec(); - std::string my_string = parent->GetChild(i)->GetURL().spec(); - EXPECT_NE(gurl_string.find(my_string), std::string::npos); - } - EXPECT_EQ(parent->GetChild(i)->GetTitle(), ASCIIToUTF16(titles[i])); - } -} - -TEST_F(BookmarkBarControllerTest, TestButtonOrBar) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - GURL gurl1("http://www.google.com"); - string16 title1(ASCIIToUTF16("x")); - model->SetURLStarred(gurl1, title1, true); - - GURL gurl2("http://www.google.com/gurl_power"); - string16 title2(ASCIIToUTF16("gurl power")); - model->SetURLStarred(gurl2, title2, true); - - NSButton* first = [[bar_ buttons] objectAtIndex:0]; - NSButton* second = [[bar_ buttons] objectAtIndex:1]; - EXPECT_TRUE(first && second); - - NSMenuItem* menuItem = [[[first cell] menu] itemAtIndex:0]; - const BookmarkNode* node = [bar_ nodeFromMenuItem:menuItem]; - EXPECT_TRUE(node); - EXPECT_EQ(node, model->GetBookmarkBarNode()->GetChild(0)); - - menuItem = [[[second cell] menu] itemAtIndex:0]; - node = [bar_ nodeFromMenuItem:menuItem]; - EXPECT_TRUE(node); - EXPECT_EQ(node, model->GetBookmarkBarNode()->GetChild(1)); - - menuItem = [[[bar_ view] menu] itemAtIndex:0]; - node = [bar_ nodeFromMenuItem:menuItem]; - EXPECT_TRUE(node); - EXPECT_EQ(node, model->GetBookmarkBarNode()); -} - -TEST_F(BookmarkBarControllerTest, TestMenuNodeAndDisable) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - const BookmarkNode* folder = model->AddGroup(parent, - parent->GetChildCount(), - ASCIIToUTF16("group")); - NSButton* button = [[bar_ buttons] objectAtIndex:0]; - EXPECT_TRUE(button); - - // Confirm the menu knows which node it is talking about - BookmarkMenu* menu = static_cast<BookmarkMenu*>([[button cell] menu]); - EXPECT_TRUE(menu); - EXPECT_TRUE([menu isKindOfClass:[BookmarkMenu class]]); - EXPECT_EQ(folder->id(), [menu id]); - - // Make sure "Open All" is disabled (nothing to open -- no children!) - // (Assumes "Open All" is the 1st item) - NSMenuItem* item = [menu itemAtIndex:0]; - EXPECT_FALSE([bar_ validateUserInterfaceItem:item]); - - // Now add a child and make sure the item would be enabled. - model->AddURL(folder, folder->GetChildCount(), - ASCIIToUTF16("super duper wide title"), - GURL("http://superfriends.hall-of-justice.edu")); - EXPECT_TRUE([bar_ validateUserInterfaceItem:item]); -} - -TEST_F(BookmarkBarControllerTest, TestDragButton) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - - GURL gurls[] = { GURL("http://www.google.com/a"), - GURL("http://www.google.com/b"), - GURL("http://www.google.com/c") }; - string16 titles[] = { ASCIIToUTF16("a"), - ASCIIToUTF16("b"), - ASCIIToUTF16("c") }; - for (unsigned i = 0; i < arraysize(titles); i++) { - model->SetURLStarred(gurls[i], titles[i], true); - } - - EXPECT_EQ([[bar_ buttons] count], arraysize(titles)); - EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]); - - [bar_ dragButton:[[bar_ buttons] objectAtIndex:2] - to:NSMakePoint(0, 0) - copy:NO]; - EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]); - // Make sure a 'copy' did not happen. - EXPECT_EQ([[bar_ buttons] count], arraysize(titles)); - - [bar_ dragButton:[[bar_ buttons] objectAtIndex:1] - to:NSMakePoint(1000, 0) - copy:NO]; - EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]); - EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]); - EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]); - EXPECT_EQ([[bar_ buttons] count], arraysize(titles)); - - // A drop of the 1st between the next 2. - CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]); - x += [[bar_ view] frame].origin.x; - [bar_ dragButton:[[bar_ buttons] objectAtIndex:0] - to:NSMakePoint(x, 0) - copy:NO]; - EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:0] title]); - EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:1] title]); - EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]); - EXPECT_EQ([[bar_ buttons] count], arraysize(titles)); - - // A drop on a non-folder button. (Shouldn't try and go in it.) - x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]); - x += [[bar_ view] frame].origin.x; - [bar_ dragButton:[[bar_ buttons] objectAtIndex:2] - to:NSMakePoint(x, 0) - copy:NO]; - EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]); - - // A drop on a folder button. - const BookmarkNode* folder = model->AddGroup(model->GetBookmarkBarNode(), - 0, - ASCIIToUTF16("awesome group")); - DCHECK(folder); - model->AddURL(folder, 0, ASCIIToUTF16("already"), - GURL("http://www.google.com")); - EXPECT_EQ(arraysize(titles) + 1, [[bar_ buttons] count]); - EXPECT_EQ(1, folder->GetChildCount()); - x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]); - x += [[bar_ view] frame].origin.x; - string16 title = [[[bar_ buttons] objectAtIndex:2] bookmarkNode]->GetTitle(); - [bar_ dragButton:[[bar_ buttons] objectAtIndex:2] - to:NSMakePoint(x, 0) - copy:NO]; - // Gone from the bar - EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]); - // In the folder - EXPECT_EQ(2, folder->GetChildCount()); - // At the end - EXPECT_EQ(title, folder->GetChild(1)->GetTitle()); -} - -TEST_F(BookmarkBarControllerTest, TestCopyButton) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - - GURL gurls[] = { GURL("http://www.google.com/a"), - GURL("http://www.google.com/b"), - GURL("http://www.google.com/c") }; - string16 titles[] = { ASCIIToUTF16("a"), - ASCIIToUTF16("b"), - ASCIIToUTF16("c") }; - for (unsigned i = 0; i < arraysize(titles); i++) { - model->SetURLStarred(gurls[i], titles[i], true); - } - EXPECT_EQ([[bar_ buttons] count], arraysize(titles)); - EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]); - - // Drag 'a' between 'b' and 'c'. - CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]); - x += [[bar_ view] frame].origin.x; - [bar_ dragButton:[[bar_ buttons] objectAtIndex:0] - to:NSMakePoint(x, 0) - copy:YES]; - EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]); - EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]); - EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]); - EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:3] title]); - EXPECT_EQ([[bar_ buttons] count], 4U); -} - -// Fake a theme with colored text. Apply it and make sure bookmark -// buttons have the same colored text. Repeat more than once. -TEST_F(BookmarkBarControllerTest, TestThemedButton) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - model->SetURLStarred(GURL("http://www.foo.com"), ASCIIToUTF16("small"), true); - BookmarkButton* button = [[bar_ buttons] objectAtIndex:0]; - EXPECT_TRUE(button); - - NSArray* colors = [NSArray arrayWithObjects:[NSColor redColor], - [NSColor blueColor], - nil]; - for (NSColor* color in colors) { - FakeTheme theme(color); - [bar_ updateTheme:&theme]; - NSAttributedString* astr = [button attributedTitle]; - EXPECT_TRUE(astr); - EXPECT_NSEQ(@"small", [astr string]); - // Pick a char in the middle to test (index 3) - NSDictionary* attributes = [astr attributesAtIndex:3 effectiveRange:NULL]; - NSColor* newColor = - [attributes objectForKey:NSForegroundColorAttributeName]; - EXPECT_NSEQ(newColor, color); - } -} - -// Test that delegates and targets of buttons are cleared on dealloc. -TEST_F(BookmarkBarControllerTest, TestClearOnDealloc) { - // Make some bookmark buttons. - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - GURL gurls[] = { GURL("http://www.foo.com/"), - GURL("http://www.bar.com/"), - GURL("http://www.baz.com/") }; - string16 titles[] = { ASCIIToUTF16("a"), - ASCIIToUTF16("b"), - ASCIIToUTF16("c") }; - for (size_t i = 0; i < arraysize(titles); i++) - model->SetURLStarred(gurls[i], titles[i], true); - - // Get and retain the buttons so we can examine them after dealloc. - scoped_nsobject<NSArray> buttons([[bar_ buttons] retain]); - EXPECT_EQ([buttons count], arraysize(titles)); - - // Make sure that everything is set. - for (BookmarkButton* button in buttons.get()) { - ASSERT_TRUE([button isKindOfClass:[BookmarkButton class]]); - EXPECT_TRUE([button delegate]); - EXPECT_TRUE([button target]); - EXPECT_TRUE([button action]); - } - - // This will dealloc.... - bar_.reset(); - - // Make sure that everything is cleared. - for (BookmarkButton* button in buttons.get()) { - EXPECT_FALSE([button delegate]); - EXPECT_FALSE([button target]); - EXPECT_FALSE([button action]); - } -} - -TEST_F(BookmarkBarControllerTest, TestFolders) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - - // Create some folder buttons. - const BookmarkNode* parent = model->GetBookmarkBarNode(); - const BookmarkNode* folder = model->AddGroup(parent, - parent->GetChildCount(), - ASCIIToUTF16("group")); - model->AddURL(folder, folder->GetChildCount(), - ASCIIToUTF16("f1"), GURL("http://framma-lamma.com")); - folder = model->AddGroup(parent, parent->GetChildCount(), - ASCIIToUTF16("empty")); - - EXPECT_EQ([[bar_ buttons] count], 2U); - - // First confirm mouseEntered does nothing if "menus" aren't active. - NSEvent* event = test_event_utils::MakeMouseEvent(NSOtherMouseUp, 0); - [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event]; - EXPECT_FALSE([bar_ folderController]); - - // Make one active. Entering it is now a no-op. - [bar_ openBookmarkFolderFromButton:[[bar_ buttons] objectAtIndex:0]]; - BookmarkBarFolderController* bbfc = [bar_ folderController]; - EXPECT_TRUE(bbfc); - [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event]; - EXPECT_EQ(bbfc, [bar_ folderController]); - - // Enter a different one; a new folderController is active. - [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:1] event:event]; - EXPECT_NE(bbfc, [bar_ folderController]); - - // Confirm exited is a no-op. - [bar_ mouseExitedButton:[[bar_ buttons] objectAtIndex:1] event:event]; - EXPECT_NE(bbfc, [bar_ folderController]); - - // Clean up. - [bar_ closeBookmarkFolder:nil]; -} - -// Verify that the folder menu presentation properly tracks mouse movements -// over the bar. Until there is a click no folder menus should show. After a -// click on a folder folder menus should show until another click on a folder -// button, and a click outside the bar and its folder menus. -TEST_F(BookmarkBarControllerTest, TestFolderButtons) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b 4f:[ 4f1b 4f2b ] "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model and that we do not have a folder controller. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - EXPECT_FALSE([bar_ folderController]); - - // Add a real bookmark so we can click on it. - const BookmarkNode* folder = root->GetChild(3); - model.AddURL(folder, folder->GetChildCount(), ASCIIToUTF16("CLICK ME"), - GURL("http://www.google.com/")); - - // Click on a folder button. - BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"4f"]; - EXPECT_TRUE(button); - [bar_ openBookmarkFolderFromButton:button]; - BookmarkBarFolderController* bbfc = [bar_ folderController]; - EXPECT_TRUE(bbfc); - - // Make sure a 2nd click on the same button closes things. - [bar_ openBookmarkFolderFromButton:button]; - EXPECT_FALSE([bar_ folderController]); - - // Next open is a different button. - button = [bar_ buttonWithTitleEqualTo:@"2f"]; - EXPECT_TRUE(button); - [bar_ openBookmarkFolderFromButton:button]; - EXPECT_TRUE([bar_ folderController]); - - // Mouse over a non-folder button and confirm controller has gone away. - button = [bar_ buttonWithTitleEqualTo:@"1b"]; - EXPECT_TRUE(button); - NSEvent* event = test_event_utils::MouseEventAtPoint([button center], - NSMouseMoved, 0); - [bar_ mouseEnteredButton:button event:event]; - EXPECT_FALSE([bar_ folderController]); - - // Mouse over the original folder and confirm a new controller. - button = [bar_ buttonWithTitleEqualTo:@"2f"]; - EXPECT_TRUE(button); - [bar_ mouseEnteredButton:button event:event]; - BookmarkBarFolderController* oldBBFC = [bar_ folderController]; - EXPECT_TRUE(oldBBFC); - - // 'Jump' over to a different folder and confirm a new controller. - button = [bar_ buttonWithTitleEqualTo:@"4f"]; - EXPECT_TRUE(button); - [bar_ mouseEnteredButton:button event:event]; - BookmarkBarFolderController* newBBFC = [bar_ folderController]; - EXPECT_TRUE(newBBFC); - EXPECT_NE(oldBBFC, newBBFC); - - // A click on a real bookmark should close and stop tracking the folder menus. - BookmarkButton* bookmarkButton = [newBBFC buttonWithTitleEqualTo:@"CLICK ME"]; - EXPECT_TRUE(bookmarkButton); - [newBBFC openBookmark:bookmarkButton]; - EXPECT_FALSE([bar_ folderController]); - [bar_ mouseEnteredButton:button event:event]; - EXPECT_FALSE([bar_ folderController]); -} - -// Make sure the "off the side" folder looks like a bookmark folder -// but only contains "off the side" items. -TEST_F(BookmarkBarControllerTest, OffTheSideFolder) { - - // It starts hidden. - EXPECT_TRUE([bar_ offTheSideButtonIsHidden]); - - // Create some buttons. - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - for (int x = 0; x < 30; x++) { - model->AddURL(parent, parent->GetChildCount(), - ASCIIToUTF16("medium-size-title"), - GURL("http://framma-lamma.com")); - } - // Add a couple more so we can delete one and make sure its button goes away. - model->AddURL(parent, parent->GetChildCount(), - ASCIIToUTF16("DELETE_ME"), GURL("http://ashton-tate.com")); - model->AddURL(parent, parent->GetChildCount(), - ASCIIToUTF16("medium-size-title"), - GURL("http://framma-lamma.com")); - - // Should no longer be hidden. - EXPECT_FALSE([bar_ offTheSideButtonIsHidden]); - - // Open it; make sure we have a folder controller. - EXPECT_FALSE([bar_ folderController]); - [bar_ openOffTheSideFolderFromButton:[bar_ offTheSideButton]]; - BookmarkBarFolderController* bbfc = [bar_ folderController]; - EXPECT_TRUE(bbfc); - - // Confirm the contents are only buttons which fell off the side by - // making sure that none of the nodes in the off-the-side folder are - // found in bar buttons. Be careful since not all the bar buttons - // may be currently displayed. - NSArray* folderButtons = [bbfc buttons]; - NSArray* barButtons = [bar_ buttons]; - for (BookmarkButton* folderButton in folderButtons) { - for (BookmarkButton* barButton in barButtons) { - if ([barButton superview]) { - EXPECT_NE([folderButton bookmarkNode], [barButton bookmarkNode]); - } - } - } - - // Delete a bookmark in the off-the-side and verify it's gone. - BookmarkButton* button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"]; - EXPECT_TRUE(button); - model->Remove(parent, parent->GetChildCount() - 2); - button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"]; - EXPECT_FALSE(button); -} - -TEST_F(BookmarkBarControllerTest, EventToExitCheck) { - NSEvent* event = test_event_utils::MakeMouseEvent(NSMouseMoved, 0); - EXPECT_FALSE([bar_ isEventAnExitEvent:event]); - - BookmarkBarFolderWindow* folderWindow = [[[BookmarkBarFolderWindow alloc] - init] autorelease]; - [[[bar_ view] window] addChildWindow:folderWindow - ordered:NSWindowAbove]; - event = test_event_utils::LeftMouseDownAtPointInWindow(NSMakePoint(1,1), - folderWindow); - EXPECT_FALSE([bar_ isEventAnExitEvent:event]); - - event = test_event_utils::LeftMouseDownAtPointInWindow(NSMakePoint(100,100), - test_window()); - EXPECT_TRUE([bar_ isEventAnExitEvent:event]); - - // Many components are arbitrary (e.g. location, keycode). - event = [NSEvent keyEventWithType:NSKeyDown - location:NSMakePoint(1,1) - modifierFlags:0 - timestamp:0 - windowNumber:0 - context:nil - characters:@"x" - charactersIgnoringModifiers:@"x" - isARepeat:NO - keyCode:87]; - EXPECT_TRUE([bar_ isEventAnExitEvent:event]); - - [[[bar_ view] window] removeChildWindow:folderWindow]; -} - -TEST_F(BookmarkBarControllerTest, DropDestination) { - // Make some buttons. - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - model->AddGroup(parent, parent->GetChildCount(), ASCIIToUTF16("group 1")); - model->AddGroup(parent, parent->GetChildCount(), ASCIIToUTF16("group 2")); - EXPECT_EQ([[bar_ buttons] count], 2U); - - // Confirm "off to left" and "off to right" match nothing. - NSPoint p = NSMakePoint(-1, 2); - EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]); - EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]); - p = NSMakePoint(50000, 10); - EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]); - EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]); - - // Confirm "right in the center" (give or take a pixel) is a match, - // and confirm "just barely in the button" is not. Anything more - // specific seems likely to be tweaked. - CGFloat viewFrameXOffset = [[bar_ view] frame].origin.x; - for (BookmarkButton* button in [bar_ buttons]) { - CGFloat x = NSMidX([button frame]) + viewFrameXOffset; - // Somewhere near the center: a match - EXPECT_EQ(button, - [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x-1, 10)]); - EXPECT_EQ(button, - [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x+1, 10)]); - EXPECT_FALSE([bar_ shouldShowIndicatorShownForPoint:NSMakePoint(x, 10)]);; - - // On the very edges: NOT a match - x = NSMinX([button frame]) + viewFrameXOffset; - EXPECT_NE(button, - [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 9)]); - x = NSMaxX([button frame]) + viewFrameXOffset; - EXPECT_NE(button, - [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 11)]); - } -} - -TEST_F(BookmarkBarControllerTest, NodeDeletedWhileMenuIsOpen) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - [bar_ loaded:model]; - - const BookmarkNode* parent = model->GetBookmarkBarNode(); - const BookmarkNode* initialNode = model->AddURL( - parent, parent->GetChildCount(), - ASCIIToUTF16("initial"), - GURL("http://www.google.com")); - - NSMenuItem* item = ItemForBookmarkBarMenu(initialNode); - EXPECT_EQ(0U, noOpenBar()->urls_.size()); - - // Basic check of the menu item and an IBOutlet it can call. - EXPECT_EQ(initialNode, [bar_ nodeFromMenuItem:item]); - [bar_ openBookmarkInNewWindow:item]; - EXPECT_EQ(1U, noOpenBar()->urls_.size()); - [bar_ clear]; - - // Now delete the node and make sure things are happy (no crash, - // NULL node caught). - model->Remove(parent, parent->IndexOfChild(initialNode)); - EXPECT_EQ(nil, [bar_ nodeFromMenuItem:item]); - // Should not crash by referencing a deleted node. - [bar_ openBookmarkInNewWindow:item]; - // Confirm the above did nothing in case it somehow didn't crash. - EXPECT_EQ(0U, noOpenBar()->urls_.size()); - - // Confirm some more non-crashes. - [bar_ openBookmarkInNewForegroundTab:item]; - [bar_ openBookmarkInIncognitoWindow:item]; - [bar_ editBookmark:item]; - [bar_ copyBookmark:item]; - [bar_ deleteBookmark:item]; - [bar_ openAllBookmarks:item]; - [bar_ openAllBookmarksNewWindow:item]; - [bar_ openAllBookmarksIncognitoWindow:item]; -} - -TEST_F(BookmarkBarControllerTest, NodeDeletedWhileContextMenuIsOpen) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - [bar_ loaded:model]; - - const BookmarkNode* parent = model->GetBookmarkBarNode(); - const BookmarkNode* folder = model->AddGroup(parent, - parent->GetChildCount(), - ASCIIToUTF16("group")); - const BookmarkNode* framma = model->AddURL(folder, folder->GetChildCount(), - ASCIIToUTF16("f1"), - GURL("http://framma-lamma.com")); - - // Mock in a menu - id origMenu = [bar_ buttonContextMenu]; - id fakeMenu = [OCMockObject partialMockForObject:origMenu]; - [[fakeMenu expect] cancelTracking]; - [bar_ setButtonContextMenu:fakeMenu]; - - // Force a delete which should cancelTracking on the menu. - model->Remove(framma->GetParent(), framma->GetParent()->IndexOfChild(framma)); - - // Restore, then confirm cancelTracking was called. - [bar_ setButtonContextMenu:origMenu]; - [fakeMenu verify]; -} - -TEST_F(BookmarkBarControllerTest, CloseFolderOnAnimate) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - const BookmarkNode* folder = model->AddGroup(parent, - parent->GetChildCount(), - ASCIIToUTF16("group")); - model->AddGroup(parent, parent->GetChildCount(), - ASCIIToUTF16("sibbling group")); - model->AddURL(folder, folder->GetChildCount(), ASCIIToUTF16("title a"), - GURL("http://www.google.com/a")); - model->AddURL(folder, folder->GetChildCount(), - ASCIIToUTF16("title super duper long long whoa momma title you betcha"), - GURL("http://www.google.com/b")); - BookmarkButton* button = [[bar_ buttons] objectAtIndex:0]; - EXPECT_FALSE([bar_ folderController]); - [bar_ openBookmarkFolderFromButton:button]; - BookmarkBarFolderController* bbfc = [bar_ folderController]; - // The following tells us that the folder menu is showing. We want to make - // sure the folder menu goes away if the bookmark bar is hidden. - EXPECT_TRUE(bbfc); - EXPECT_TRUE([bar_ isVisible]); - - // Hide the bookmark bar. - [bar_ updateAndShowNormalBar:NO - showDetachedBar:YES - withAnimation:YES]; - EXPECT_TRUE([bar_ isAnimationRunning]); - - // Now that we've closed the bookmark bar (with animation) the folder menu - // should have been closed thus releasing the folderController. - EXPECT_FALSE([bar_ folderController]); -} - -TEST_F(BookmarkBarControllerTest, MoveRemoveAddButtons) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - - // Remember how many buttons are showing. - int oldDisplayedButtons = [bar_ displayedButtonCount]; - NSArray* buttons = [bar_ buttons]; - - // Move a button around a bit. - [bar_ moveButtonFromIndex:0 toIndex:2]; - EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:0] title]); - EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:1] title]); - EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:2] title]); - EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]); - [bar_ moveButtonFromIndex:2 toIndex:0]; - EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:0] title]); - EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]); - EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]); - EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]); - - // Add a couple of buttons. - const BookmarkNode* parent = root->GetChild(1); // Purloin an existing node. - const BookmarkNode* node = parent->GetChild(0); - [bar_ addButtonForNode:node atIndex:0]; - EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]); - EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]); - EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]); - EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]); - EXPECT_EQ(oldDisplayedButtons + 1, [bar_ displayedButtonCount]); - node = parent->GetChild(1); - [bar_ addButtonForNode:node atIndex:-1]; - EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]); - EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]); - EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]); - EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]); - EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:4] title]); - EXPECT_EQ(oldDisplayedButtons + 2, [bar_ displayedButtonCount]); - - // Remove a couple of buttons. - [bar_ removeButton:4 animate:NO]; - [bar_ removeButton:1 animate:NO]; - EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]); - EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]); - EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]); - EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]); -} - -TEST_F(BookmarkBarControllerTest, ShrinkOrHideView) { - NSRect viewFrame = NSMakeRect(0.0, 0.0, 500.0, 50.0); - NSView* view = [[[NSView alloc] initWithFrame:viewFrame] autorelease]; - EXPECT_FALSE([view isHidden]); - [bar_ shrinkOrHideView:view forMaxX:500.0]; - EXPECT_EQ(500.0, NSWidth([view frame])); - EXPECT_FALSE([view isHidden]); - [bar_ shrinkOrHideView:view forMaxX:450.0]; - EXPECT_EQ(450.0, NSWidth([view frame])); - EXPECT_FALSE([view isHidden]); - [bar_ shrinkOrHideView:view forMaxX:40.0]; - EXPECT_EQ(40.0, NSWidth([view frame])); - EXPECT_FALSE([view isHidden]); - [bar_ shrinkOrHideView:view forMaxX:31.0]; - EXPECT_EQ(31.0, NSWidth([view frame])); - EXPECT_FALSE([view isHidden]); - [bar_ shrinkOrHideView:view forMaxX:29.0]; - EXPECT_TRUE([view isHidden]); -} - -class BookmarkBarControllerOpenAllTest : public BookmarkBarControllerTest { -public: - BookmarkBarControllerOpenAllTest() { - resizeDelegate_.reset([[ViewResizerPong alloc] init]); - NSRect parent_frame = NSMakeRect(0, 0, 800, 50); - bar_.reset( - [[BookmarkBarControllerOpenAllPong alloc] - initWithBrowser:helper_.browser() - initialWidth:NSWidth(parent_frame) - delegate:nil - resizeDelegate:resizeDelegate_.get()]); - [bar_ view]; - // Awkwardness to look like we've been installed. - [parent_view_ addSubview:[bar_ view]]; - NSRect frame = [[[bar_ view] superview] frame]; - frame.origin.y = 100; - [[[bar_ view] superview] setFrame:frame]; - - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - parent_ = model->GetBookmarkBarNode(); - // { one, { two-one, two-two }, three } - model->AddURL(parent_, parent_->GetChildCount(), ASCIIToUTF16("title"), - GURL("http://one.com")); - folder_ = model->AddGroup(parent_, parent_->GetChildCount(), - ASCIIToUTF16("group")); - model->AddURL(folder_, folder_->GetChildCount(), - ASCIIToUTF16("title"), GURL("http://two-one.com")); - model->AddURL(folder_, folder_->GetChildCount(), - ASCIIToUTF16("title"), GURL("http://two-two.com")); - model->AddURL(parent_, parent_->GetChildCount(), - ASCIIToUTF16("title"), GURL("https://three.com")); - } - const BookmarkNode* parent_; // Weak - const BookmarkNode* folder_; // Weak -}; - -TEST_F(BookmarkBarControllerOpenAllTest, OpenAllBookmarks) { - // Our first OpenAll... is from the bar itself. - [bar_ openAllBookmarks:ItemForBookmarkBarMenu(parent_)]; - BookmarkBarControllerOpenAllPong* specialBar = - (BookmarkBarControllerOpenAllPong*)bar_.get(); - EXPECT_EQ([specialBar dispositionDetected], NEW_FOREGROUND_TAB); - - // Now try an OpenAll... from a folder node. - [specialBar setDispositionDetected:IGNORE_ACTION]; // Reset - [bar_ openAllBookmarks:ItemForBookmarkBarMenu(folder_)]; - EXPECT_EQ([specialBar dispositionDetected], NEW_FOREGROUND_TAB); -} - -TEST_F(BookmarkBarControllerOpenAllTest, OpenAllNewWindow) { - // Our first OpenAll... is from the bar itself. - [bar_ openAllBookmarksNewWindow:ItemForBookmarkBarMenu(parent_)]; - BookmarkBarControllerOpenAllPong* specialBar = - (BookmarkBarControllerOpenAllPong*)bar_.get(); - EXPECT_EQ([specialBar dispositionDetected], NEW_WINDOW); - - // Now try an OpenAll... from a folder node. - [specialBar setDispositionDetected:IGNORE_ACTION]; // Reset - [bar_ openAllBookmarksNewWindow:ItemForBookmarkBarMenu(folder_)]; - EXPECT_EQ([specialBar dispositionDetected], NEW_WINDOW); -} - -TEST_F(BookmarkBarControllerOpenAllTest, OpenAllIncognito) { - // Our first OpenAll... is from the bar itself. - [bar_ openAllBookmarksIncognitoWindow:ItemForBookmarkBarMenu(parent_)]; - BookmarkBarControllerOpenAllPong* specialBar = - (BookmarkBarControllerOpenAllPong*)bar_.get(); - EXPECT_EQ([specialBar dispositionDetected], OFF_THE_RECORD); - - // Now try an OpenAll... from a folder node. - [specialBar setDispositionDetected:IGNORE_ACTION]; // Reset - [bar_ openAllBookmarksIncognitoWindow:ItemForBookmarkBarMenu(folder_)]; - EXPECT_EQ([specialBar dispositionDetected], OFF_THE_RECORD); -} - -// Command-click on a folder should open all the bookmarks in it. -TEST_F(BookmarkBarControllerOpenAllTest, CommandClickOnFolder) { - NSButton* first = [[bar_ buttons] objectAtIndex:0]; - EXPECT_TRUE(first); - - // Create the right kind of event; mock NSApp so [NSApp - // currentEvent] finds it. - NSEvent* commandClick = test_event_utils::MouseEventAtPoint(NSZeroPoint, - NSLeftMouseDown, - NSCommandKeyMask); - id fakeApp = [OCMockObject partialMockForObject:NSApp]; - [[[fakeApp stub] andReturn:commandClick] currentEvent]; - id oldApp = NSApp; - NSApp = fakeApp; - size_t originalDispositionCount = noOpenBar()->dispositions_.size(); - - // Click! - [first performClick:first]; - - size_t dispositionCount = noOpenBar()->dispositions_.size(); - EXPECT_EQ(originalDispositionCount+1, dispositionCount); - EXPECT_EQ(noOpenBar()->dispositions_[dispositionCount-1], NEW_BACKGROUND_TAB); - - // Replace NSApp - NSApp = oldApp; -} - -class BookmarkBarControllerNotificationTest : public CocoaTest { - public: - BookmarkBarControllerNotificationTest() { - resizeDelegate_.reset([[ViewResizerPong alloc] init]); - NSRect parent_frame = NSMakeRect(0, 0, 800, 50); - parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]); - [parent_view_ setHidden:YES]; - bar_.reset( - [[BookmarkBarControllerNotificationPong alloc] - initWithBrowser:helper_.browser() - initialWidth:NSWidth(parent_frame) - delegate:nil - resizeDelegate:resizeDelegate_.get()]); - - // Force loading of the nib. - [bar_ view]; - // Awkwardness to look like we've been installed. - [parent_view_ addSubview:[bar_ view]]; - NSRect frame = [[[bar_ view] superview] frame]; - frame.origin.y = 100; - [[[bar_ view] superview] setFrame:frame]; - - // Do not add the bar to a window, yet. - } - - BrowserTestHelper helper_; - scoped_nsobject<NSView> parent_view_; - scoped_nsobject<ViewResizerPong> resizeDelegate_; - scoped_nsobject<BookmarkBarControllerNotificationPong> bar_; -}; - -TEST_F(BookmarkBarControllerNotificationTest, DeregistersForNotifications) { - NSWindow* window = [[CocoaTestHelperWindow alloc] init]; - [window setReleasedWhenClosed:YES]; - - // First add the bookmark bar to the temp window, then to another window. - [[window contentView] addSubview:parent_view_]; - [[test_window() contentView] addSubview:parent_view_]; - - // Post a fake windowDidResignKey notification for the temp window and make - // sure the bookmark bar controller wasn't listening. - [[NSNotificationCenter defaultCenter] - postNotificationName:NSWindowDidResignKeyNotification - object:window]; - EXPECT_FALSE([bar_ windowDidResignKeyReceived]); - - // Close the temp window and make sure no notification was received. - [window close]; - EXPECT_FALSE([bar_ windowWillCloseReceived]); -} - - -// TODO(jrg): draggingEntered: and draggingExited: trigger timers so -// they are hard to test. Factor out "fire timers" into routines -// which can be overridden to fire immediately to make behavior -// confirmable. - -// TODO(jrg): add unit test to make sure "Other Bookmarks" responds -// properly to a hover open. - -// TODO(viettrungluu): figure out how to test animations. - -class BookmarkBarControllerDragDropTest : public BookmarkBarControllerTestBase { - public: - scoped_nsobject<BookmarkBarControllerDragData> bar_; - - BookmarkBarControllerDragDropTest() { - bar_.reset( - [[BookmarkBarControllerDragData alloc] - initWithBrowser:helper_.browser() - initialWidth:NSWidth([parent_view_ frame]) - delegate:nil - resizeDelegate:resizeDelegate_.get()]); - InstallAndToggleBar(bar_.get()); - } -}; - -TEST_F(BookmarkBarControllerDragDropTest, DragMoveBarBookmarkToOffTheSide) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1bWithLongName 2fWithLongName:[ " - "2f1bWithLongName 2f2fWithLongName:[ 2f2f1bWithLongName " - "2f2f2bWithLongName 2f2f3bWithLongName 2f4b ] 2f3bWithLongName ] " - "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName " - "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName " - "11bWithLongName 12bWithLongName 13b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - - // Insure that the off-the-side is not showing. - ASSERT_FALSE([bar_ offTheSideButtonIsHidden]); - - // Remember how many buttons are showing and are available. - int oldDisplayedButtons = [bar_ displayedButtonCount]; - int oldChildCount = root->GetChildCount(); - - // Pop up the off-the-side menu. - BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton]; - ASSERT_TRUE(otsButton); - [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:) - withObject:otsButton]; - BookmarkBarFolderController* otsController = [bar_ folderController]; - EXPECT_TRUE(otsController); - NSWindow* toWindow = [otsController window]; - EXPECT_TRUE(toWindow); - BookmarkButton* draggedButton = - [bar_ buttonWithTitleEqualTo:@"3bWithLongName"]; - ASSERT_TRUE(draggedButton); - int oldOTSCount = (int)[[otsController buttons] count]; - EXPECT_EQ(oldOTSCount, oldChildCount - oldDisplayedButtons); - BookmarkButton* targetButton = [[otsController buttons] objectAtIndex:0]; - ASSERT_TRUE(targetButton); - [otsController dragButton:draggedButton - to:[targetButton center] - copy:YES]; - // There should still be the same number of buttons in the bar - // and off-the-side should have one more. - int newDisplayedButtons = [bar_ displayedButtonCount]; - int newChildCount = root->GetChildCount(); - int newOTSCount = (int)[[otsController buttons] count]; - EXPECT_EQ(oldDisplayedButtons, newDisplayedButtons); - EXPECT_EQ(oldChildCount + 1, newChildCount); - EXPECT_EQ(oldOTSCount + 1, newOTSCount); - EXPECT_EQ(newOTSCount, newChildCount - newDisplayedButtons); -} - -TEST_F(BookmarkBarControllerDragDropTest, DragOffTheSideToOther) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1bWithLongName 2bWithLongName " - "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName " - "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName " - "11bWithLongName 12bWithLongName 13bWithLongName 14bWithLongName " - "15bWithLongName 16bWithLongName 17bWithLongName 18bWithLongName " - "19bWithLongName 20bWithLongName "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - const BookmarkNode* other = model.other_node(); - const std::string other_string("1other 2other 3other "); - model_test_utils::AddNodesFromModelString(model, other, other_string); - - // Validate initial model. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - std::string actualOtherString = model_test_utils::ModelStringFromNode(other); - EXPECT_EQ(other_string, actualOtherString); - - // Insure that the off-the-side is showing. - ASSERT_FALSE([bar_ offTheSideButtonIsHidden]); - - // Remember how many buttons are showing and are available. - int oldDisplayedButtons = [bar_ displayedButtonCount]; - int oldRootCount = root->GetChildCount(); - int oldOtherCount = other->GetChildCount(); - - // Pop up the off-the-side menu. - BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton]; - ASSERT_TRUE(otsButton); - [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:) - withObject:otsButton]; - BookmarkBarFolderController* otsController = [bar_ folderController]; - EXPECT_TRUE(otsController); - int oldOTSCount = (int)[[otsController buttons] count]; - EXPECT_EQ(oldOTSCount, oldRootCount - oldDisplayedButtons); - - // Pick an off-the-side button and drag it to the other bookmarks. - BookmarkButton* draggedButton = - [otsController buttonWithTitleEqualTo:@"20bWithLongName"]; - ASSERT_TRUE(draggedButton); - BookmarkButton* targetButton = [bar_ otherBookmarksButton]; - ASSERT_TRUE(targetButton); - [bar_ dragButton:draggedButton to:[targetButton center] copy:NO]; - - // There should one less button in the bar, one less in off-the-side, - // and one more in other bookmarks. - int newRootCount = root->GetChildCount(); - int newOTSCount = (int)[[otsController buttons] count]; - int newOtherCount = other->GetChildCount(); - EXPECT_EQ(oldRootCount - 1, newRootCount); - EXPECT_EQ(oldOTSCount - 1, newOTSCount); - EXPECT_EQ(oldOtherCount + 1, newOtherCount); -} - -TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkData) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " - "2f3b ] 3b 4b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - const BookmarkNode* other = model.other_node(); - const std::string other_string("O1b O2b O3f:[ O3f1b O3f2f ] " - "O4f:[ O4f1b O4f2f ] 05b "); - model_test_utils::AddNodesFromModelString(model, other, other_string); - - // Validate initial model. - std::string actual = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actual); - actual = model_test_utils::ModelStringFromNode(other); - EXPECT_EQ(other_string, actual); - - // Remember the little ones. - int oldChildCount = root->GetChildCount(); - - BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"]; - ASSERT_TRUE(targetButton); - - // Gen up some dragging data. - const BookmarkNode* newNode = other->GetChild(2); - [bar_ setDragDataNode:newNode]; - scoped_nsobject<FakeDragInfo> dragInfo([[FakeDragInfo alloc] init]); - [dragInfo setDropLocation:[targetButton center]]; - [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()]; - - // There should one more button in the bar. - int newChildCount = root->GetChildCount(); - EXPECT_EQ(oldChildCount + 1, newChildCount); - // Verify the model. - const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " - "2f3b ] O3f:[ O3f1b O3f2f ] 3b 4b "); - actual = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(expected, actual); - oldChildCount = newChildCount; - - // Now do it over a folder button. - targetButton = [bar_ buttonWithTitleEqualTo:@"2f"]; - ASSERT_TRUE(targetButton); - NSPoint targetPoint = [targetButton center]; - newNode = other->GetChild(2); // Should be O4f. - EXPECT_EQ(newNode->GetTitle(), ASCIIToUTF16("O4f")); - [bar_ setDragDataNode:newNode]; - [dragInfo setDropLocation:targetPoint]; - [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()]; - - newChildCount = root->GetChildCount(); - EXPECT_EQ(oldChildCount, newChildCount); - // Verify the model. - const std::string expected1("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " - "2f3b O4f:[ O4f1b O4f2f ] ] O3f:[ O3f1b O3f2f ] " - "3b 4b "); - actual = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(expected1, actual); -} - -TEST_F(BookmarkBarControllerDragDropTest, AddURLs) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " - "2f3b ] 3b 4b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actual = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actual); - - // Remember the children. - int oldChildCount = root->GetChildCount(); - - BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"]; - ASSERT_TRUE(targetButton); - - NSArray* urls = [NSArray arrayWithObjects: @"http://www.a.com/", - @"http://www.b.com/", nil]; - NSArray* titles = [NSArray arrayWithObjects: @"SiteA", @"SiteB", nil]; - [bar_ addURLs:urls withTitles:titles at:[targetButton center]]; - - // There should two more nodes in the bar. - int newChildCount = root->GetChildCount(); - EXPECT_EQ(oldChildCount + 2, newChildCount); - // Verify the model. - const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " - "2f3b ] SiteA SiteB 3b 4b "); - actual = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(expected, actual); -} - -TEST_F(BookmarkBarControllerDragDropTest, ControllerForNode) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - - // Find the main bar controller. - const void* expectedController = bar_; - const void* actualController = [bar_ controllerForNode:root]; - EXPECT_EQ(expectedController, actualController); -} - -TEST_F(BookmarkBarControllerDragDropTest, DropPositionIndicator) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2b 2f3b ] 3b 4b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actualModel = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModel); - - // Test a series of points starting at the right edge of the bar. - BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"1b"]; - ASSERT_TRUE(targetButton); - NSPoint targetPoint = [targetButton left]; - const CGFloat xDelta = 0.5 * bookmarks::kBookmarkHorizontalPadding; - const CGFloat baseOffset = targetPoint.x; - CGFloat expected = xDelta; - CGFloat actual = [bar_ indicatorPosForDragToPoint:targetPoint]; - EXPECT_CGFLOAT_EQ(expected, actual); - targetButton = [bar_ buttonWithTitleEqualTo:@"2f"]; - actual = [bar_ indicatorPosForDragToPoint:[targetButton right]]; - targetButton = [bar_ buttonWithTitleEqualTo:@"3b"]; - expected = [targetButton left].x - baseOffset + xDelta; - EXPECT_CGFLOAT_EQ(expected, actual); - targetButton = [bar_ buttonWithTitleEqualTo:@"4b"]; - targetPoint = [targetButton right]; - targetPoint.x += 100; // Somewhere off to the right. - expected = NSMaxX([targetButton frame]) + xDelta; - actual = [bar_ indicatorPosForDragToPoint:targetPoint]; - EXPECT_CGFLOAT_EQ(expected, actual); -} - -TEST_F(BookmarkBarControllerDragDropTest, PulseButton) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* root = model->GetBookmarkBarNode(); - GURL gurl("http://www.google.com"); - const BookmarkNode* node = model->AddURL(root, root->GetChildCount(), - ASCIIToUTF16("title"), gurl); - - BookmarkButton* button = [[bar_ buttons] objectAtIndex:0]; - EXPECT_FALSE([button isContinuousPulsing]); - - NSValue *value = [NSValue valueWithPointer:node]; - NSDictionary *dict = [NSDictionary - dictionaryWithObjectsAndKeys:value, - bookmark_button::kBookmarkKey, - [NSNumber numberWithBool:YES], - bookmark_button::kBookmarkPulseFlagKey, - nil]; - [[NSNotificationCenter defaultCenter] - postNotificationName:bookmark_button::kPulseBookmarkButtonNotification - object:nil - userInfo:dict]; - EXPECT_TRUE([button isContinuousPulsing]); - - dict = [NSDictionary dictionaryWithObjectsAndKeys:value, - bookmark_button::kBookmarkKey, - [NSNumber numberWithBool:NO], - bookmark_button::kBookmarkPulseFlagKey, - nil]; - [[NSNotificationCenter defaultCenter] - postNotificationName:bookmark_button::kPulseBookmarkButtonNotification - object:nil - userInfo:dict]; - EXPECT_FALSE([button isContinuousPulsing]); -} - -TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkDataToTrash) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " - "2f3b ] 3b 4b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actual = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actual); - - int oldChildCount = root->GetChildCount(); - - // Drag a button to the trash. - BookmarkButton* buttonToDelete = [bar_ buttonWithTitleEqualTo:@"3b"]; - ASSERT_TRUE(buttonToDelete); - EXPECT_TRUE([bar_ canDragBookmarkButtonToTrash:buttonToDelete]); - [bar_ didDragBookmarkToTrash:buttonToDelete]; - - // There should be one less button in the bar. - int newChildCount = root->GetChildCount(); - EXPECT_EQ(oldChildCount - 1, newChildCount); - // Verify the model. - const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " - "2f3b ] 4b "); - actual = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(expected, actual); - - // Verify that the other bookmark folder can't be deleted. - BookmarkButton *otherButton = [bar_ otherBookmarksButton]; - EXPECT_FALSE([bar_ canDragBookmarkButtonToTrash:otherButton]); -} - -} // namespace diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_button_cell.h b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_button_cell.h deleted file mode 100644 index c8eaf71..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_button_cell.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_FOLDER_BUTTON_CELL_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_FOLDER_BUTTON_CELL_H_ -#pragma once - -#import "chrome/browser/cocoa/bookmarks/bookmark_button_cell.h" - -class BookmarkNode; - -// A button cell that handles drawing/highlighting of buttons in the -// bookmark bar. This cell forwards mouseEntered/mouseExited events -// to its control view so that pseudo-menu operations -// (e.g. hover-over to open) can be implemented. -@interface BookmarkBarFolderButtonCell : BookmarkButtonCell { - @private - scoped_nsobject<NSColor> frameColor_; -} - -// Create a button cell which draws without a theme and with a frame -// color provided by the BrowserThemeProvider defaults. -+ (id)buttonCellForNode:(const BookmarkNode*)node - contextMenu:(NSMenu*)contextMenu - cellText:(NSString*)cellText - cellImage:(NSImage*)cellImage; - -@end - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_FOLDER_BUTTON_CELL_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_button_cell.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_button_cell.mm deleted file mode 100644 index a3a73a3..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_button_cell.mm +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_button_cell.h" - -@implementation BookmarkBarFolderButtonCell - -+ (id)buttonCellForNode:(const BookmarkNode*)node - contextMenu:(NSMenu*)contextMenu - cellText:(NSString*)cellText - cellImage:(NSImage*)cellImage { - id buttonCell = - [[[BookmarkBarFolderButtonCell alloc] initForNode:node - contextMenu:contextMenu - cellText:cellText - cellImage:cellImage] - autorelease]; - return buttonCell; -} - -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_button_cell_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_button_cell_unittest.mm deleted file mode 100644 index 0fb8ae3..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_button_cell_unittest.mm +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/scoped_nsobject.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_button_cell.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" - -namespace { - -class BookmarkBarFolderButtonCellTest : public CocoaTest { -}; - -// Basic creation. -TEST_F(BookmarkBarFolderButtonCellTest, Create) { - scoped_nsobject<BookmarkBarFolderButtonCell> cell; - cell.reset([[BookmarkBarFolderButtonCell buttonCellForNode:nil - contextMenu:nil - cellText:nil - cellImage:nil] retain]); - EXPECT_TRUE(cell); -} - -} // namespace diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.h b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.h deleted file mode 100644 index 1511f11..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.h +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#include "base/scoped_nsobject.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button.h" - -@class BookmarkBarController; -@class BookmarkBarFolderView; -@class BookmarkFolderTarget; -@class BookmarkBarFolderHoverState; - -// A controller for the pop-up windows from bookmark folder buttons -// which look sort of like menus. -@interface BookmarkBarFolderController : - NSWindowController<BookmarkButtonDelegate, - BookmarkButtonControllerProtocol> { - @private - // The button whose click opened us. - scoped_nsobject<BookmarkButton> parentButton_; - - // Bookmark bar folder controller chains are torn down in two ways: - // 1. Clicking "outside" the folder (see use of - // CrApplicationEventHookProtocol in the bookmark bar controller). - // 2. Engaging a different folder (via hover over or explicit click). - // - // In either case, the BookmarkButtonControllerProtocol method - // closeAllBookmarkFolders gets called. For bookmark bar folder - // controllers, this is passed up the chain so we begin with a top - // level "close". - // When any bookmark folder window closes, it necessarily tells - // subcontroller windows to close (down the chain), and autoreleases - // the controller. (Must autorelease since the controller can still - // get delegate events such as windowDidClose). - // - // Bookmark bar folder controllers own their buttons. When doing - // drag and drop of a button from one sub-sub-folder to a different - // sub-sub-folder, we need to make sure the button's pointers stay - // valid until we've dropped (or cancelled). Note that such a drag - // causes the source sub-sub-folder (previous parent window) to go - // away (windows close, controllers autoreleased) since you're - // hovering over a different folder chain for dropping. To keep - // things valid (like the button's target, its delegate, the parent - // cotroller that we have a pointer to below [below], etc), we heep - // strong pointers to our owning controller, so the entire chain - // stays owned. - - // Our parent controller, if we are a nested folder, otherwise nil. - // Strong to insure the object lives as long as we need it. - scoped_nsobject<BookmarkBarFolderController> parentController_; - - // The main bar controller from whence we or a parent sprang. - BookmarkBarController* barController_; // WEAK: It owns us. - - // Our buttons. We do not have buttons for nested folders. - scoped_nsobject<NSMutableArray> buttons_; - - // The scroll view that contains our main button view (below). - IBOutlet NSScrollView* scrollView_; - - // Are we scrollable? If no, the full contents of the folder are - // always visible. - BOOL scrollable_; - - BOOL scrollUpArrowShown_; - BOOL scrollDownArrowShown_; - - // YES if subfolders should grow to the right (the default). - // Direction switches if we'd grow off the screen. - BOOL subFolderGrowthToRight_; - - // The main view of this window (where the buttons go). - IBOutlet BookmarkBarFolderView* mainView_; - - // Weak; we keep track to work around a - // setShowsBorderOnlyWhileMouseInside bug. - BookmarkButton* buttonThatMouseIsIn_; - - // The context menu for a bookmark button which represents an URL. - IBOutlet NSMenu* buttonMenu_; - - // The context menu for a bookmark button which represents a folder. - IBOutlet NSMenu* folderMenu_; - - // We model hover state as a state machine with specific allowable - // transitions. |hoverState_| is the state of this machine at any - // given time. - scoped_nsobject<BookmarkBarFolderHoverState> hoverState_; - - // Logic for dealing with a click on a bookmark folder button. - scoped_nsobject<BookmarkFolderTarget> folderTarget_; - - // A controller for a pop-up bookmark folder window (custom menu). - // We (self) are the parentController_ for our folderController_. - // This is not a scoped_nsobject because it owns itself (when its - // window closes the controller gets autoreleased). - BookmarkBarFolderController* folderController_; - - // Implement basic menu scrolling through this tracking area. - scoped_nsobject<NSTrackingArea> scrollTrackingArea_; - - // Timer to continue scrolling as needed. We own the timer but - // don't release it when done (we invalidate it). - NSTimer* scrollTimer_; - - // Amount to scroll by on each timer fire. Can be + or -. - CGFloat verticalScrollDelta_; - - // We need to know the size of the vertical scrolling arrows so we - // can obscure/unobscure them. - CGFloat verticalScrollArrowHeight_; - - // Set to YES to prevent any node animations. Useful for unit testing so that - // incomplete animations do not cause valgrind complaints. - BOOL ignoreAnimations_; -} - -// Designated initializer. -- (id)initWithParentButton:(BookmarkButton*)button - parentController:(BookmarkBarFolderController*)parentController - barController:(BookmarkBarController*)barController; - -// Return the parent button that owns the bookmark folder we represent. -- (BookmarkButton*)parentButton; - -// Offset our folder menu window. This is usually needed in response to a -// parent folder menu window or the bookmark bar changing position due to -// the dragging of a bookmark node from the parent into this folder menu. -- (void)offsetFolderMenuWindow:(NSSize)offset; - -// Re-layout the window menu in case some buttons were added or removed, -// specifically as a result of the bookmark bar changing configuration -// and altering the contents of the off-the-side folder. -- (void)reconfigureMenu; - -// Actions from a context menu over a button or folder. -- (IBAction)cutBookmark:(id)sender; -- (IBAction)copyBookmark:(id)sender; -- (IBAction)pasteBookmark:(id)sender; -- (IBAction)deleteBookmark:(id)sender; - -// Passed up by a child view to tell us of a desire to scroll. -- (void)scrollWheel:(NSEvent *)theEvent; - -// Forwarded to the associated BookmarkBarController. -- (IBAction)addFolder:(id)sender; -- (IBAction)addPage:(id)sender; -- (IBAction)editBookmark:(id)sender; -- (IBAction)openBookmark:(id)sender; -- (IBAction)openAllBookmarks:(id)sender; -- (IBAction)openAllBookmarksIncognitoWindow:(id)sender; -- (IBAction)openAllBookmarksNewWindow:(id)sender; -- (IBAction)openBookmarkInIncognitoWindow:(id)sender; -- (IBAction)openBookmarkInNewForegroundTab:(id)sender; -- (IBAction)openBookmarkInNewWindow:(id)sender; - -@property (assign, nonatomic) BOOL subFolderGrowthToRight; - -@end - -@interface BookmarkBarFolderController(TestingAPI) -- (NSView*)mainView; -- (NSPoint)windowTopLeftForWidth:(int)windowWidth; -- (NSArray*)buttons; -- (BookmarkBarFolderController*)folderController; -- (id)folderTarget; -- (void)configureWindowLevel; -- (void)performOneScroll:(CGFloat)delta; -- (BookmarkButton*)buttonThatMouseIsIn; -// Set to YES in order to prevent animations. -- (void)setIgnoreAnimations:(BOOL)ignore; - -// Return YES if we can scroll up or down. -- (BOOL)canScrollUp; -- (BOOL)canScrollDown; -// Return YES if the scrollable_ flag has been set. -- (BOOL)scrollable; - -- (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point; -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.mm deleted file mode 100644 index 55fa2d0..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.mm +++ /dev/null @@ -1,1459 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.h" - -#include "base/mac_util.h" -#include "base/nsimage_cache_mac.h" -#include "base/sys_string_conversions.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#include "chrome/browser/bookmarks/bookmark_utils.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_constants.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_button_cell.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_hover_state.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_view.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_folder_target.h" -#import "chrome/browser/cocoa/browser_window_controller.h" -#import "chrome/browser/cocoa/event_utils.h" -#import "chrome/browser/themes/browser_theme_provider.h" - -namespace { - -// Frequency of the scrolling timer in seconds. -const NSTimeInterval kBookmarkBarFolderScrollInterval = 0.1; - -// Amount to scroll by per timer fire. We scroll rather slowly; to -// accomodate we do several at a time. -const CGFloat kBookmarkBarFolderScrollAmount = - 3 * bookmarks::kBookmarkButtonVerticalSpan; - -// Amount to scroll for each scroll wheel delta. -const CGFloat kBookmarkBarFolderScrollWheelAmount = - 1 * bookmarks::kBookmarkButtonVerticalSpan; - -// When constraining a scrolling bookmark bar folder window to the -// screen, shrink the "constrain" by this much vertically. Currently -// this is 0.0 to avoid a problem with tracking areas leaving the -// window, but should probably be 8.0 or something. -// TODO(jrg): http://crbug.com/36225 -const CGFloat kScrollWindowVerticalMargin = 0.0; - -} // namespace - -@interface BookmarkBarFolderController(Private) -- (void)configureWindow; -- (void)addOrUpdateScrollTracking; -- (void)removeScrollTracking; -- (void)endScroll; -- (void)addScrollTimerWithDelta:(CGFloat)delta; - -// Determine the best button width (which will be the widest button or the -// maximum allowable button width, whichever is less) and resize all buttons. -// Return the new width (so that the window can be adjusted, if necessary). -- (CGFloat)adjustButtonWidths; - -// Returns the total menu height needed to display |buttonCount| buttons. -// Does not do any fancy tricks like trimming the height to fit on the screen. -- (int)windowHeightForButtonCount:(int)buttonCount; - -// Adjust the height and horizontal position of the window such that the -// scroll arrows are shown as needed and the window appears completely -// on screen. -- (void)adjustWindowForHeight:(int)windowHeight; - -// Show or hide the scroll arrows at the top/bottom of the window. -- (void)showOrHideScrollArrows; - -// |point| is in the base coordinate system of the destination window; -// it comes from an id<NSDraggingInfo>. |copy| is YES if a copy is to be -// made and inserted into the new location while leaving the bookmark in -// the old location, otherwise move the bookmark by removing from its old -// location and inserting into the new location. -- (BOOL)dragBookmark:(const BookmarkNode*)sourceNode - to:(NSPoint)point - copy:(BOOL)copy; - -@end - -@interface BookmarkButton (BookmarkBarFolderMenuHighlighting) - -// Make the button's border frame always appear when |forceOn| is YES, -// otherwise only border the button when the mouse is inside the button. -- (void)forceButtonBorderToStayOnAlways:(BOOL)forceOn; - -// On 10.6 event dispatch for an NSButtonCell's -// showsBorderOnlyWhileMouseInside seems broken if scrolling the -// view that contains the button. It appears that a mouseExited: -// gets lost, so the button stays highlit forever. We accomodate -// here. -- (void)toggleButtonBorderingWhileMouseInside; -@end - -@implementation BookmarkButton (BookmarkBarFolderMenuHighlighting) - -- (void)forceButtonBorderToStayOnAlways:(BOOL)forceOn { - [self setShowsBorderOnlyWhileMouseInside:!forceOn]; - [self setNeedsDisplay]; -} - -- (void)toggleButtonBorderingWhileMouseInside { - BOOL toggle = [self showsBorderOnlyWhileMouseInside]; - [self setShowsBorderOnlyWhileMouseInside:!toggle]; - [self setShowsBorderOnlyWhileMouseInside:toggle]; -} - -@end - -@implementation BookmarkBarFolderController - -@synthesize subFolderGrowthToRight = subFolderGrowthToRight_; - -- (id)initWithParentButton:(BookmarkButton*)button - parentController:(BookmarkBarFolderController*)parentController - barController:(BookmarkBarController*)barController { - NSString* nibPath = - [mac_util::MainAppBundle() pathForResource:@"BookmarkBarFolderWindow" - ofType:@"nib"]; - if ((self = [super initWithWindowNibPath:nibPath owner:self])) { - parentButton_.reset([button retain]); - - // We want the button to remain bordered as part of the menu path. - [button forceButtonBorderToStayOnAlways:YES]; - - parentController_.reset([parentController retain]); - if (!parentController_) - [self setSubFolderGrowthToRight:YES]; - else - [self setSubFolderGrowthToRight:[parentController - subFolderGrowthToRight]]; - barController_ = barController; // WEAK - buttons_.reset([[NSMutableArray alloc] init]); - folderTarget_.reset([[BookmarkFolderTarget alloc] initWithController:self]); - NSImage* image = nsimage_cache::ImageNamed(@"menu_overflow_up.pdf"); - DCHECK(image); - verticalScrollArrowHeight_ = [image size].height; - [self configureWindow]; - hoverState_.reset([[BookmarkBarFolderHoverState alloc] init]); - } - return self; -} - -- (void)dealloc { - // The button is no longer part of the menu path. - [parentButton_ forceButtonBorderToStayOnAlways:NO]; - [parentButton_ setNeedsDisplay]; - - [self removeScrollTracking]; - [self endScroll]; - [hoverState_ draggingExited]; - - // Delegate pattern does not retain; make sure pointers to us are removed. - for (BookmarkButton* button in buttons_.get()) { - [button setDelegate:nil]; - [button setTarget:nil]; - [button setAction:nil]; - } - - // Note: we don't need to - // [NSObject cancelPreviousPerformRequestsWithTarget:self]; - // Because all of our performSelector: calls use withDelay: which - // retains us. - [super dealloc]; -} - -// Overriden from NSWindowController to call childFolderWillShow: before showing -// the window. -- (void)showWindow:(id)sender { - [barController_ childFolderWillShow:self]; - [super showWindow:sender]; -} - -- (BookmarkButton*)parentButton { - return parentButton_.get(); -} - -- (void)offsetFolderMenuWindow:(NSSize)offset { - NSWindow* window = [self window]; - NSRect windowFrame = [window frame]; - windowFrame.origin.x -= offset.width; - windowFrame.origin.y += offset.height; // Yes, in the opposite direction! - [window setFrame:windowFrame display:YES]; - [folderController_ offsetFolderMenuWindow:offset]; -} - -- (void)reconfigureMenu { - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - for (BookmarkButton* button in buttons_.get()) { - [button setDelegate:nil]; - [button removeFromSuperview]; - } - [buttons_ removeAllObjects]; - [self configureWindow]; -} - -#pragma mark Private Methods - -- (BookmarkButtonCell*)cellForBookmarkNode:(const BookmarkNode*)child { - NSImage* image = child ? [barController_ favIconForNode:child] : nil; - NSMenu* menu = child ? child->is_folder() ? folderMenu_ : buttonMenu_ : nil; - BookmarkBarFolderButtonCell* cell = - [BookmarkBarFolderButtonCell buttonCellForNode:child - contextMenu:menu - cellText:nil - cellImage:image]; - [cell setTag:kStandardButtonTypeWithLimitedClickFeedback]; - return cell; -} - -// Redirect to our logic shared with BookmarkBarController. -- (IBAction)openBookmarkFolderFromButton:(id)sender { - [folderTarget_ openBookmarkFolderFromButton:sender]; -} - -// Create a bookmark button for the given node using frame. -// -// If |node| is NULL this is an "(empty)" button. -// Does NOT add this button to our button list. -// Returns an autoreleased button. -// Adjusts the input frame width as appropriate. -// -// TODO(jrg): combine with addNodesToButtonList: code from -// bookmark_bar_controller.mm, and generalize that to use both x and y -// offsets. -// http://crbug.com/35966 -- (BookmarkButton*)makeButtonForNode:(const BookmarkNode*)node - frame:(NSRect)frame { - BookmarkButtonCell* cell = [self cellForBookmarkNode:node]; - DCHECK(cell); - - // We must decide if we draw the folder arrow before we ask the cell - // how big it needs to be. - if (node && node->is_folder()) { - // Warning when combining code with bookmark_bar_controller.mm: - // this call should NOT be made for the bar buttons; only for the - // subfolder buttons. - [cell setDrawFolderArrow:YES]; - } - - // The "+2" is needed because, sometimes, Cocoa is off by a tad when - // returning the value it thinks it needs. - CGFloat desired = [cell cellSize].width + 2; - // The width is determined from the maximum of the proposed width - // (provided in |frame|) or the natural width of the title, then - // limited by the abolute minimum and maximum allowable widths. - frame.size.width = - std::min(std::max(bookmarks::kBookmarkMenuButtonMinimumWidth, - std::max(frame.size.width, desired)), - bookmarks::kBookmarkMenuButtonMaximumWidth); - - BookmarkButton* button = [[[BookmarkButton alloc] initWithFrame:frame] - autorelease]; - DCHECK(button); - - [button setCell:cell]; - [button setDelegate:self]; - if (node) { - 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::SysUTF16ToNSString(node->GetTitle()); - std::string urlString = node->GetURL().possibly_invalid_spec(); - NSString* tooltip = [NSString stringWithFormat:@"%@\n%s", title, - urlString.c_str()]; - [button setToolTip:tooltip]; - } - } else { - [button setEnabled:NO]; - [button setBordered:NO]; - } - return button; -} - -// Exposed for testing. -- (NSView*)mainView { - return mainView_; -} - -- (id)folderTarget { - return folderTarget_.get(); -} - - -// Our parent controller is another BookmarkBarFolderController, so -// our window is to the right or left of it. We use a little overlap -// since it looks much more menu-like than with none. If we would -// grow off the screen, switch growth to the other direction. Growth -// direction sticks for folder windows which are descendents of us. -// If we have tried both directions and neither fits, degrade to a -// default. -- (CGFloat)childFolderWindowLeftForWidth:(int)windowWidth { - // We may legitimately need to try two times (growth to right and - // left but not in that order). Limit us to three tries in case - // the folder window can't fit on either side of the screen; we - // don't want to loop forever. - CGFloat x; - int tries = 0; - while (tries < 2) { - // Try to grow right. - if ([self subFolderGrowthToRight]) { - tries++; - x = NSMaxX([[parentButton_ window] frame]) - - bookmarks::kBookmarkMenuOverlap; - // If off the screen, switch direction. - if ((x + windowWidth + - bookmarks::kBookmarkHorizontalScreenPadding) > - NSMaxX([[[self window] screen] frame])) { - [self setSubFolderGrowthToRight:NO]; - } else { - return x; - } - } - // Try to grow left. - if (![self subFolderGrowthToRight]) { - tries++; - x = NSMinX([[parentButton_ window] frame]) + - bookmarks::kBookmarkMenuOverlap - - windowWidth; - // If off the screen, switch direction. - if (x < NSMinX([[[self window] screen] frame])) { - [self setSubFolderGrowthToRight:YES]; - } else { - return x; - } - } - } - // Unhappy; do the best we can. - return NSMaxX([[[self window] screen] frame]) - windowWidth; -} - - -// Compute and return the top left point of our window (screen -// coordinates). The top left is positioned in a manner similar to -// cascading menus. Windows may grow to either the right or left of -// their parent (if a sub-folder) so we need to know |windowWidth|. -- (NSPoint)windowTopLeftForWidth:(int)windowWidth { - NSPoint newWindowTopLeft; - if (![parentController_ isKindOfClass:[self class]]) { - // If we're not popping up from one of ourselves, we must be - // popping up from the bookmark bar itself. In this case, start - // BELOW the parent button. Our left is the button left; our top - // is bottom of button's parent view. - NSPoint buttonBottomLeftInScreen = - [[parentButton_ window] - convertBaseToScreen:[parentButton_ - convertPoint:NSZeroPoint toView:nil]]; - NSPoint bookmarkBarBottomLeftInScreen = - [[parentButton_ window] - convertBaseToScreen:[[parentButton_ superview] - convertPoint:NSZeroPoint toView:nil]]; - newWindowTopLeft = NSMakePoint(buttonBottomLeftInScreen.x, - bookmarkBarBottomLeftInScreen.y); - // Make sure the window is on-screen; if not, push left. It is - // intentional that top level folders "push left" slightly - // different than subfolders. - NSRect screenFrame = [[[parentButton_ window] screen] frame]; - CGFloat spillOff = (newWindowTopLeft.x + windowWidth) - NSMaxX(screenFrame); - if (spillOff > 0.0) { - newWindowTopLeft.x = std::max(newWindowTopLeft.x - spillOff, - NSMinX(screenFrame)); - } - } else { - // Parent is a folder; grow right/left. - newWindowTopLeft.x = [self childFolderWindowLeftForWidth:windowWidth]; - NSPoint top = NSMakePoint(0, (NSMaxY([parentButton_ frame]) + - bookmarks::kBookmarkVerticalPadding)); - NSPoint topOfWindow = - [[parentButton_ window] - convertBaseToScreen:[[parentButton_ superview] - convertPoint:top toView:nil]]; - newWindowTopLeft.y = topOfWindow.y; - } - return newWindowTopLeft; -} - -// Set our window level to the right spot so we're above the menubar, dock, etc. -// Factored out so we can override/noop in a unit test. -- (void)configureWindowLevel { - [[self window] setLevel:NSPopUpMenuWindowLevel]; -} - -- (int)windowHeightForButtonCount:(int)buttonCount { - return (buttonCount * bookmarks::kBookmarkButtonVerticalSpan) + - bookmarks::kBookmarkVerticalPadding; -} - -- (void)adjustWindowForHeight:(int)windowHeight { - // Adjust all button widths to be consistent, determine the best size for - // the window, and set the window frame. - CGFloat windowWidth = - [self adjustButtonWidths] + - (2 * bookmarks::kBookmarkSubMenuHorizontalPadding); - NSPoint newWindowTopLeft = [self windowTopLeftForWidth:windowWidth]; - NSSize windowSize = NSMakeSize(windowWidth, windowHeight); - windowSize = [scrollView_ convertSize:windowSize toView:nil]; - NSWindow* window = [self window]; - // If the window is already visible then make sure its top remains stable. - BOOL windowAlreadyShowing = [window isVisible]; - CGFloat deltaY = windowHeight - NSHeight([mainView_ frame]); - if (windowAlreadyShowing) { - NSRect oldFrame = [window frame]; - newWindowTopLeft.y = oldFrame.origin.y + NSHeight(oldFrame); - } - NSRect windowFrame = NSMakeRect(newWindowTopLeft.x, - newWindowTopLeft.y - windowHeight, windowSize.width, windowHeight); - // Make the scrolled content be the right size (full size). - NSRect mainViewFrame = NSMakeRect(0, 0, NSWidth(windowFrame) - - bookmarks::kScrollViewContentWidthMargin, NSHeight(windowFrame)); - [mainView_ setFrame:mainViewFrame]; - // Make sure the window fits on the screen. If not, constrain. - // We'll scroll to allow the user to see all the content. - NSRect screenFrame = [[[self window] screen] frame]; - screenFrame = NSInsetRect(screenFrame, 0, kScrollWindowVerticalMargin); - BOOL wasScrollable = scrollable_; - if (!NSContainsRect(screenFrame, windowFrame)) { - scrollable_ = YES; - windowFrame = NSIntersectionRect(screenFrame, windowFrame); - } else { - scrollable_ = NO; - } - [window setFrame:windowFrame display:NO]; - if (wasScrollable != scrollable_) { - // If scrollability changed then rework visibility of the scroll arrows - // and the scroll offset of the menu view. - NSSize windowLocalSize = - [scrollView_ convertSize:windowFrame.size fromView:nil]; - CGFloat scrollPointY = NSHeight(mainViewFrame) - windowLocalSize.height + - bookmarks::kBookmarkVerticalPadding; - [mainView_ scrollPoint:NSMakePoint(0, scrollPointY)]; - [self showOrHideScrollArrows]; - [self addOrUpdateScrollTracking]; - } else if (scrollable_ && windowAlreadyShowing) { - // If the window was already showing and is still scrollable then make - // sure the main view moves upward, not downward so that the content - // at the bottom of the menu, not the top, appears to move. - // The edge case is when the menu is scrolled all the way to top (hence - // the test of scrollDownArrowShown_) - don't scroll then. - NSView* superView = [mainView_ superview]; - DCHECK([superView isKindOfClass:[NSClipView class]]); - NSClipView* clipView = static_cast<NSClipView*>(superView); - CGFloat scrollPointY = [clipView bounds].origin.y + - bookmarks::kBookmarkVerticalPadding; - if (scrollDownArrowShown_ || deltaY > 0.0) - scrollPointY += deltaY; - [mainView_ scrollPoint:NSMakePoint(0, scrollPointY)]; - } - [window display]; -} - -// Determine window size and position. -// Create buttons for all our nodes. -// TODO(jrg): break up into more and smaller routines for easier unit testing. -- (void)configureWindow { - const BookmarkNode* node = [parentButton_ bookmarkNode]; - DCHECK(node); - int startingIndex = [[parentButton_ cell] startingChildIndex]; - DCHECK_LE(startingIndex, node->GetChildCount()); - // Must have at least 1 button (for "empty") - int buttons = std::max(node->GetChildCount() - startingIndex, 1); - - // Prelim height of the window. We'll trim later as needed. - int height = [self windowHeightForButtonCount:buttons]; - // We'll need this soon... - [self window]; - - // TODO(jrg): combine with frame code in bookmark_bar_controller.mm - // http://crbug.com/35966 - NSRect buttonsOuterFrame = NSMakeRect( - bookmarks::kBookmarkSubMenuHorizontalPadding, - (height - bookmarks::kBookmarkButtonVerticalSpan), - bookmarks::kDefaultBookmarkWidth, - bookmarks::kBookmarkButtonHeight); - - // TODO(jrg): combine with addNodesToButtonList: code from - // bookmark_bar_controller.mm (but use y offset) - // http://crbug.com/35966 - if (!node->GetChildCount()) { - // If no children we are the empty button. - BookmarkButton* button = [self makeButtonForNode:nil - frame:buttonsOuterFrame]; - [buttons_ addObject:button]; - [mainView_ addSubview:button]; - } else { - for (int i = startingIndex; - i < node->GetChildCount(); - i++) { - const BookmarkNode* child = node->GetChild(i); - BookmarkButton* button = [self makeButtonForNode:child - frame:buttonsOuterFrame]; - [buttons_ addObject:button]; - [mainView_ addSubview:button]; - buttonsOuterFrame.origin.y -= bookmarks::kBookmarkButtonVerticalSpan; - } - } - - [self adjustWindowForHeight:height]; - // Finally pop me up. - [self configureWindowLevel]; -} - -// TODO(mrossetti): See if the following can be moved into view's viewWillDraw:. -- (CGFloat)adjustButtonWidths { - CGFloat width = bookmarks::kBookmarkMenuButtonMinimumWidth; - // Use the cell's size as the base for determining the desired width of the - // button rather than the button's current width. -[cell cellSize] always - // returns the 'optimum' size of the cell based on the cell's contents even - // if it's less than the current button size. Relying on the button size - // would result in buttons that could only get wider but we want to handle - // the case where the widest button gets removed from a folder menu. - for (BookmarkButton* button in buttons_.get()) - width = std::max(width, [[button cell] cellSize].width); - width = std::min(width, bookmarks::kBookmarkMenuButtonMaximumWidth); - // Things look and feel more menu-like if all the buttons are the - // full width of the window, especially if there are submenus. - for (BookmarkButton* button in buttons_.get()) { - NSRect buttonFrame = [button frame]; - buttonFrame.size.width = width; - [button setFrame:buttonFrame]; - } - return width; -} - -- (BOOL)canScrollUp { - // If removal of an arrow would make things "finished", state as - // such. - CGFloat scrollY = [scrollView_ documentVisibleRect].origin.y; - if (scrollUpArrowShown_) - scrollY -= verticalScrollArrowHeight_; - - if (scrollY <= 0) - return NO; - return YES; -} - -- (BOOL)canScrollDown { - CGFloat arrowAdjustment = 0.0; - - // We do NOT adjust based on the scrollDOWN arrow. This keeps - // things from "jumping"; if removal of the down arrow (at the top - // of the window) would cause a scroll to end, we'll end. - if (scrollUpArrowShown_) - arrowAdjustment += verticalScrollArrowHeight_; - - NSPoint scrollPosition = [scrollView_ documentVisibleRect].origin; - NSRect documentRect = [[scrollView_ documentView] frame]; - - // If we are exactly the right height, return no. We need this - // extra conditional in the case where we've just scrolled/grown - // into position. - if (NSHeight([[self window] frame]) == NSHeight(documentRect)) - return NO; - - if ((scrollPosition.y + NSHeight([[self window] frame])) >= - (NSHeight(documentRect) + arrowAdjustment)) { - return NO; - } - return YES; -} - -- (void)showOrHideScrollArrows { - NSRect frame = [scrollView_ frame]; - CGFloat scrollDelta = 0.0; - BOOL canScrollDown = [self canScrollDown]; - BOOL canScrollUp = [self canScrollUp]; - - if (canScrollUp != scrollUpArrowShown_) { - if (scrollUpArrowShown_) { - frame.origin.y -= verticalScrollArrowHeight_; - frame.size.height += verticalScrollArrowHeight_; - scrollDelta = verticalScrollArrowHeight_; - } else { - frame.origin.y += verticalScrollArrowHeight_; - frame.size.height -= verticalScrollArrowHeight_; - scrollDelta = -verticalScrollArrowHeight_; - } - } - if (canScrollDown != scrollDownArrowShown_) { - if (scrollDownArrowShown_) { - frame.size.height += verticalScrollArrowHeight_; - } else { - frame.size.height -= verticalScrollArrowHeight_; - } - } - scrollUpArrowShown_ = canScrollUp; - scrollDownArrowShown_ = canScrollDown; - [scrollView_ setFrame:frame]; - - // Adjust scroll based on new frame. For example, if we make room - // for an arrow at the bottom, adjust the scroll so the topmost item - // is still fully visible. - if (scrollDelta) { - NSPoint scrollPosition = [scrollView_ documentVisibleRect].origin; - scrollPosition.y -= scrollDelta; - [[scrollView_ documentView] scrollPoint:scrollPosition]; - } -} - -- (BOOL)scrollable { - return scrollable_; -} - -// Start a "scroll up" timer. -- (void)beginScrollWindowUp { - [self addScrollTimerWithDelta:kBookmarkBarFolderScrollAmount]; -} - -// Start a "scroll down" timer. -- (void)beginScrollWindowDown { - [self addScrollTimerWithDelta:-kBookmarkBarFolderScrollAmount]; -} - -// End a scrolling timer. Can be called excessively with no harm. -- (void)endScroll { - if (scrollTimer_) { - [scrollTimer_ invalidate]; - scrollTimer_ = nil; - verticalScrollDelta_ = 0; - } -} - -// Perform a single scroll of the specified amount. -// Scroll up: -// Scroll the documentView by the growth amount. -// If we cannot grow the window, simply scroll the documentView. -// If we can grow the window up without falling off the screen, do it. -// Scroll down: -// Never change the window size; only scroll the documentView. -- (void)performOneScroll:(CGFloat)delta { - NSRect windowFrame = [[self window] frame]; - NSRect screenFrame = [[[self window] screen] frame]; - - // First scroll the "document" area. - NSPoint scrollPosition = [scrollView_ documentVisibleRect].origin; - scrollPosition.y -= delta; - [[scrollView_ documentView] scrollPoint:scrollPosition]; - - if (buttonThatMouseIsIn_) - [buttonThatMouseIsIn_ toggleButtonBorderingWhileMouseInside]; - - // We update the window size after shifting the scroll to avoid a race. - CGFloat screenHeightMinusMargin = (NSHeight(screenFrame) - - (2 * kScrollWindowVerticalMargin)); - if (delta) { - // If we can, grow the window (up). - if (NSHeight(windowFrame) < screenHeightMinusMargin) { - CGFloat growAmount = delta; - // Don't scroll more than enough to "finish". - if (scrollPosition.y < 0) - growAmount += scrollPosition.y; - windowFrame.size.height += growAmount; - windowFrame.size.height = std::min(NSHeight(windowFrame), - screenHeightMinusMargin); - // Watch out for a finish that isn't the full height of the screen. - // We get here if using the scroll wheel to scroll by small amounts. - windowFrame.size.height = std::min(NSHeight(windowFrame), - NSHeight([mainView_ frame])); - // Don't allow scrolling to make the window smaller, ever. This - // conditional is important when processing scrollWheel events. - if (windowFrame.size.height > [[self window] frame].size.height) { - [[self window] setFrame:windowFrame display:YES]; - [self addOrUpdateScrollTracking]; - } - } - } - - // If we're at either end, happiness. - if ((scrollPosition.y <= 0) || - ((scrollPosition.y + NSHeight(windowFrame) >= - NSHeight([mainView_ frame])) && - (windowFrame.size.height == screenHeightMinusMargin))) { - [self endScroll]; - - // If we can't scroll either up or down we are completely done. - // For example, perhaps we've scrolled a little and grown the - // window on-screen until there is now room for everything. - if (![self canScrollUp] && ![self canScrollDown]) { - scrollable_ = NO; - [self removeScrollTracking]; - } - } - - [self showOrHideScrollArrows]; -} - -// Perform a scroll of the window on the screen. -// Called by a timer when scrolling. -- (void)performScroll:(NSTimer*)timer { - DCHECK(verticalScrollDelta_); - [self performOneScroll:verticalScrollDelta_]; -} - - -// Add a timer to fire at a regular interveral which scrolls the -// window vertically |delta|. -- (void)addScrollTimerWithDelta:(CGFloat)delta { - if (scrollTimer_ && verticalScrollDelta_ == delta) - return; - [self endScroll]; - verticalScrollDelta_ = delta; - scrollTimer_ = - [NSTimer scheduledTimerWithTimeInterval:kBookmarkBarFolderScrollInterval - target:self - selector:@selector(performScroll:) - userInfo:nil - repeats:YES]; -} - -// Called as a result of our tracking area. Warning: on the main -// screen (of a single-screened machine), the minimum mouse y value is -// 1, not 0. Also, we do not get events when the mouse is above the -// menubar (to be fixed by setting the proper window level; see -// initializer). -- (void)mouseMoved:(NSEvent*)theEvent { - DCHECK([theEvent window] == [self window]); - - NSPoint eventScreenLocation = - [[theEvent window] convertBaseToScreen:[theEvent locationInWindow]]; - - // We use frame (not visibleFrame) since our bookmark folder is on - // TOP of the menubar. - NSRect visibleRect = [[[self window] screen] frame]; - CGFloat closeToTopOfScreen = NSMaxY(visibleRect) - - verticalScrollArrowHeight_; - CGFloat closeToBottomOfScreen = NSMinY(visibleRect) + - verticalScrollArrowHeight_; - - if (eventScreenLocation.y <= closeToBottomOfScreen) { - [self beginScrollWindowUp]; - } else if (eventScreenLocation.y > closeToTopOfScreen) { - [self beginScrollWindowDown]; - } else { - [self endScroll]; - } -} - -- (void)mouseExited:(NSEvent*)theEvent { - [self endScroll]; -} - -// Add a tracking area so we know when the mouse is pinned to the top -// or bottom of the screen. If that happens, and if the mouse -// position overlaps the window, scroll it. -- (void)addOrUpdateScrollTracking { - [self removeScrollTracking]; - NSView* view = [[self window] contentView]; - scrollTrackingArea_.reset([[NSTrackingArea alloc] - initWithRect:[view bounds] - options:(NSTrackingMouseMoved | - NSTrackingMouseEnteredAndExited | - NSTrackingActiveAlways) - owner:self - userInfo:nil]); - [view addTrackingArea:scrollTrackingArea_]; -} - -// Remove the tracking area associated with scrolling. -- (void)removeScrollTracking { - if (scrollTrackingArea_.get()) { - [[[self window] contentView] removeTrackingArea:scrollTrackingArea_]; - } - scrollTrackingArea_.reset(); -} - -// Delegate callback. -- (void)windowWillClose:(NSNotification*)notification { - // If a "hover open" is pending when the bookmark bar folder is - // closed, be sure it gets cancelled. - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - - [barController_ childFolderWillClose:self]; - [self closeBookmarkFolder:self]; - [self autorelease]; -} - -// Close the old hover-open bookmark folder, and open a new one. We -// do both in one step to allow for a delay in closing the old one. -// See comments above kDragHoverCloseDelay (bookmark_bar_controller.h) -// for more details. -- (void)openBookmarkFolderFromButtonAndCloseOldOne:(id)sender { - // If an old submenu exists, close it immediately. - [self closeBookmarkFolder:sender]; - - // Open a new one if meaningful. - if ([sender isFolder]) - [folderTarget_ openBookmarkFolderFromButton:sender]; -} - -- (NSArray*)buttons { - return buttons_.get(); -} - -- (void)close { - [folderController_ close]; - [super close]; -} - -- (void)scrollWheel:(NSEvent *)theEvent { - if (scrollable_) { - // We go negative since an NSScrollView has a flipped coordinate frame. - CGFloat amt = kBookmarkBarFolderScrollWheelAmount * -[theEvent deltaY]; - [self performOneScroll:amt]; - } -} - -#pragma mark Actions Forwarded to Parent BookmarkBarController - -- (IBAction)openBookmark:(id)sender { - [barController_ openBookmark:sender]; -} - -- (IBAction)openBookmarkInNewForegroundTab:(id)sender { - [barController_ openBookmarkInNewForegroundTab:sender]; -} - -- (IBAction)openBookmarkInNewWindow:(id)sender { - [barController_ openBookmarkInNewWindow:sender]; -} - -- (IBAction)openBookmarkInIncognitoWindow:(id)sender { - [barController_ openBookmarkInIncognitoWindow:sender]; -} - -- (IBAction)editBookmark:(id)sender { - [barController_ editBookmark:sender]; -} - -- (IBAction)cutBookmark:(id)sender { - [self closeBookmarkFolder:self]; - [barController_ cutBookmark:sender]; -} - -- (IBAction)copyBookmark:(id)sender { - [barController_ copyBookmark:sender]; -} - -- (IBAction)pasteBookmark:(id)sender { - [barController_ pasteBookmark:sender]; -} - -- (IBAction)deleteBookmark:(id)sender { - [self closeBookmarkFolder:self]; - [barController_ deleteBookmark:sender]; -} - -- (IBAction)openAllBookmarks:(id)sender { - [barController_ openAllBookmarks:sender]; -} - -- (IBAction)openAllBookmarksNewWindow:(id)sender { - [barController_ openAllBookmarksNewWindow:sender]; -} - -- (IBAction)openAllBookmarksIncognitoWindow:(id)sender { - [barController_ openAllBookmarksIncognitoWindow:sender]; -} - -- (IBAction)addPage:(id)sender { - [barController_ addPage:sender]; -} - -- (IBAction)addFolder:(id)sender { - [barController_ addFolder:sender]; -} - -#pragma mark Drag & Drop - -// Find something like std::is_between<T>? I can't believe one doesn't exist. -// http://crbug.com/35966 -static BOOL ValueInRangeInclusive(CGFloat low, CGFloat value, CGFloat high) { - return ((value >= low) && (value <= high)); -} - -// Return the proposed drop target for a hover open button, or nil if none. -// -// TODO(jrg): this is just like the version in -// bookmark_bar_controller.mm, but vertical instead of horizontal. -// Generalize to be axis independent then share code. -// http://crbug.com/35966 -- (BookmarkButton*)buttonForDroppingOnAtPoint:(NSPoint)point { - for (BookmarkButton* button in buttons_.get()) { - // No early break -- makes no assumption about button ordering. - - // Intentionally NOT using NSPointInRect() so that scrolling into - // a submenu doesn't cause it to be closed. - if (ValueInRangeInclusive(NSMinY([button frame]), - point.y, - NSMaxY([button frame]))) { - - // Over a button but let's be a little more specific - // (e.g. over the middle half). - NSRect frame = [button frame]; - NSRect middleHalfOfButton = NSInsetRect(frame, 0, frame.size.height / 4); - if (ValueInRangeInclusive(NSMinY(middleHalfOfButton), - point.y, - NSMaxY(middleHalfOfButton))) { - // It makes no sense to drop on a non-folder; there is no hover. - if (![button isFolder]) - return nil; - // Got it! - return button; - } else { - // Over a button but not over the middle half. - return nil; - } - } - } - // Not hovering over a button. - return nil; -} - -// TODO(jrg): again we have code dup, sort of, with -// bookmark_bar_controller.mm, but the axis is changed. One minor -// difference is accomodation for the "empty" button (which may not -// exist in the future). -// http://crbug.com/35966 -- (int)indexForDragToPoint:(NSPoint)point { - // Identify which buttons we are between. For now, assume a button - // location is at the center point of its view, and that an exact - // match means "place before". - // TODO(jrg): revisit position info based on UI team feedback. - // dropLocation is in bar local coordinates. - // http://crbug.com/36276 - NSPoint dropLocation = - [mainView_ convertPoint:point - fromView:[[self window] contentView]]; - BookmarkButton* buttonToTheTopOfDraggedButton = nil; - // Buttons are laid out in this array from top to bottom (screen - // wise), which means "biggest y" --> "smallest y". - for (BookmarkButton* button in buttons_.get()) { - CGFloat midpoint = NSMidY([button frame]); - if (dropLocation.y > midpoint) { - break; - } - buttonToTheTopOfDraggedButton = button; - } - - // TODO(jrg): On Windows, dropping onto (empty) highlights the - // entire drop location and does not use an insertion point. - // http://crbug.com/35967 - if (!buttonToTheTopOfDraggedButton) { - // We are at the very top (we broke out of the loop on the first try). - return 0; - } - if ([buttonToTheTopOfDraggedButton isEmpty]) { - // There is a button but it's an empty placeholder. - // Default to inserting on top of it. - return 0; - } - const BookmarkNode* beforeNode = [buttonToTheTopOfDraggedButton - bookmarkNode]; - DCHECK(beforeNode); - // Be careful if the number of buttons != number of nodes. - return ((beforeNode->GetParent()->IndexOfChild(beforeNode) + 1) - - [[parentButton_ cell] startingChildIndex]); -} - -// TODO(jrg): Yet more code dup. -// http://crbug.com/35966 -- (BOOL)dragBookmark:(const BookmarkNode*)sourceNode - to:(NSPoint)point - copy:(BOOL)copy { - DCHECK(sourceNode); - - // Drop destination. - const BookmarkNode* destParent = NULL; - int destIndex = 0; - - // First check if we're dropping on a button. If we have one, and - // it's a folder, drop in it. - BookmarkButton* button = [self buttonForDroppingOnAtPoint:point]; - if ([button isFolder]) { - destParent = [button bookmarkNode]; - // Drop it at the end. - destIndex = [button bookmarkNode]->GetChildCount(); - } else { - // Else we're dropping somewhere in the folder, so find the right spot. - destParent = [parentButton_ bookmarkNode]; - destIndex = [self indexForDragToPoint:point]; - // Be careful if the number of buttons != number of nodes. - destIndex += [[parentButton_ cell] startingChildIndex]; - } - - // Prevent cycles. - BOOL wasCopiedOrMoved = NO; - if (!destParent->HasAncestor(sourceNode)) { - if (copy) - [self bookmarkModel]->Copy(sourceNode, destParent, destIndex); - else - [self bookmarkModel]->Move(sourceNode, destParent, destIndex); - wasCopiedOrMoved = YES; - // Movement of a node triggers observers (like us) to rebuild the - // bar so we don't have to do so explicitly. - } - - return wasCopiedOrMoved; -} - -#pragma mark BookmarkButtonDelegate Protocol - -- (void)fillPasteboard:(NSPasteboard*)pboard - forDragOfButton:(BookmarkButton*)button { - [[self folderTarget] fillPasteboard:pboard forDragOfButton:button]; - - // Close our folder menu and submenus since we know we're going to be dragged. - [self closeBookmarkFolder:self]; -} - -// Called from BookmarkButton. -// Unlike bookmark_bar_controller's version, we DO default to being enabled. -- (void)mouseEnteredButton:(id)sender event:(NSEvent*)event { - buttonThatMouseIsIn_ = sender; - - // Cancel a previous hover if needed. - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - - // If already opened, then we exited but re-entered the button - // (without entering another button open), do nothing. - if ([folderController_ parentButton] == sender) - return; - - [self performSelector:@selector(openBookmarkFolderFromButtonAndCloseOldOne:) - withObject:sender - afterDelay:bookmarks::kHoverOpenDelay]; -} - -// Called from the BookmarkButton -- (void)mouseExitedButton:(id)sender event:(NSEvent*)event { - if (buttonThatMouseIsIn_ == sender) - buttonThatMouseIsIn_ = nil; - - // Stop any timer about opening a new hover-open folder. - - // Since a performSelector:withDelay: on self retains self, it is - // possible that a cancelPreviousPerformRequestsWithTarget: reduces - // the refcount to 0, releasing us. That's a bad thing to do while - // this object (or others it may own) is in the event chain. Thus - // we have a retain/autorelease. - [self retain]; - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - [self autorelease]; -} - -- (NSWindow*)browserWindow { - return [parentController_ browserWindow]; -} - -- (BOOL)canDragBookmarkButtonToTrash:(BookmarkButton*)button { - return [barController_ canEditBookmark:[button bookmarkNode]]; -} - -- (void)didDragBookmarkToTrash:(BookmarkButton*)button { - // TODO(mrossetti): Refactor BookmarkBarFolder common code. - // http://crbug.com/35966 - const BookmarkNode* node = [button bookmarkNode]; - if (node) { - const BookmarkNode* parent = node->GetParent(); - [self bookmarkModel]->Remove(parent, - parent->IndexOfChild(node)); - } -} - -#pragma mark BookmarkButtonControllerProtocol - -// Recursively close all bookmark folders. -- (void)closeAllBookmarkFolders { - // Closing the top level implicitly closes all children. - [barController_ closeAllBookmarkFolders]; -} - -// Close our bookmark folder (a sub-controller) if we have one. -- (void)closeBookmarkFolder:(id)sender { - if (folderController_) { - [self setSubFolderGrowthToRight:YES]; - [[folderController_ window] close]; - folderController_ = nil; - } -} - -- (BookmarkModel*)bookmarkModel { - return [barController_ bookmarkModel]; -} - -// TODO(jrg): Refactor BookmarkBarFolder common code. http://crbug.com/35966 -// Most of the work (e.g. drop indicator) is taken care of in the -// folder_view. Here we handle hover open issues for subfolders. -// Caution: there are subtle differences between this one and -// bookmark_bar_controller.mm's version. -- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info { - NSPoint currentLocation = [info draggingLocation]; - BookmarkButton* button = [self buttonForDroppingOnAtPoint:currentLocation]; - - // Don't allow drops that would result in cycles. - if (button) { - NSData* data = [[info draggingPasteboard] - dataForType:kBookmarkButtonDragType]; - if (data && [info draggingSource]) { - BookmarkButton* sourceButton = nil; - [data getBytes:&sourceButton length:sizeof(sourceButton)]; - const BookmarkNode* sourceNode = [sourceButton bookmarkNode]; - const BookmarkNode* destNode = [button bookmarkNode]; - if (destNode->HasAncestor(sourceNode)) - button = nil; - } - } - // Delegate handling of dragging over a button to the |hoverState_| member. - return [hoverState_ draggingEnteredButton:button]; -} - -// Unlike bookmark_bar_controller, we need to keep track of dragging state. -// We also need to make sure we cancel the delayed hover close. -- (void)draggingExited:(id<NSDraggingInfo>)info { - // NOT the same as a cancel --> we may have moved the mouse into the submenu. - // Delegate handling of the hover button to the |hoverState_| member. - [hoverState_ draggingExited]; -} - -- (BOOL)dragShouldLockBarVisibility { - return [parentController_ dragShouldLockBarVisibility]; -} - -// TODO(jrg): ARGH more code dup. -// http://crbug.com/35966 -- (BOOL)dragButton:(BookmarkButton*)sourceButton - to:(NSPoint)point - copy:(BOOL)copy { - DCHECK([sourceButton isKindOfClass:[BookmarkButton class]]); - const BookmarkNode* sourceNode = [sourceButton bookmarkNode]; - return [self dragBookmark:sourceNode to:point copy:copy]; -} - -// TODO(mrossetti,jrg): Identical to the same function in BookmarkBarController. -// http://crbug.com/35966 -- (BOOL)dragBookmarkData:(id<NSDraggingInfo>)info { - BOOL dragged = NO; - std::vector<const BookmarkNode*> nodes([self retrieveBookmarkNodeData]); - if (nodes.size()) { - BOOL copy = !([info draggingSourceOperationMask] & NSDragOperationMove); - NSPoint dropPoint = [info draggingLocation]; - for (std::vector<const BookmarkNode*>::const_iterator it = nodes.begin(); - it != nodes.end(); ++it) { - const BookmarkNode* sourceNode = *it; - dragged = [self dragBookmark:sourceNode to:dropPoint copy:copy]; - } - } - return dragged; -} - -// TODO(mrossetti,jrg): Identical to the same function in BookmarkBarController. -// http://crbug.com/35966 -- (std::vector<const BookmarkNode*>)retrieveBookmarkNodeData { - std::vector<const BookmarkNode*> dragDataNodes; - BookmarkNodeData dragData; - if(dragData.ReadFromDragClipboard()) { - BookmarkModel* bookmarkModel = [self bookmarkModel]; - Profile* profile = bookmarkModel->profile(); - std::vector<const BookmarkNode*> nodes(dragData.GetNodes(profile)); - dragDataNodes.assign(nodes.begin(), nodes.end()); - } - return dragDataNodes; -} - -// Return YES if we should show the drop indicator, else NO. -// TODO(jrg): ARGH code dup! -// http://crbug.com/35966 -- (BOOL)shouldShowIndicatorShownForPoint:(NSPoint)point { - return ![self buttonForDroppingOnAtPoint:point]; -} - -// Return the y position for a drop indicator. -// -// TODO(jrg): again we have code dup, sort of, with -// bookmark_bar_controller.mm, but the axis is changed. -// http://crbug.com/35966 -- (CGFloat)indicatorPosForDragToPoint:(NSPoint)point { - CGFloat y = 0; - int destIndex = [self indexForDragToPoint:point]; - int numButtons = static_cast<int>([buttons_ count]); - - // If it's a drop strictly between existing buttons or at the very beginning - if (destIndex >= 0 && destIndex < numButtons) { - // ... put the indicator right between the buttons. - BookmarkButton* button = - [buttons_ objectAtIndex:static_cast<NSUInteger>(destIndex)]; - DCHECK(button); - NSRect buttonFrame = [button frame]; - y = NSMaxY(buttonFrame) + 0.5 * bookmarks::kBookmarkVerticalPadding; - - // If it's a drop at the end (past the last button, if there are any) ... - } else if (destIndex == numButtons) { - // and if it's past the last button ... - if (numButtons > 0) { - // ... find the last button, and put the indicator below it. - BookmarkButton* button = - [buttons_ objectAtIndex:static_cast<NSUInteger>(destIndex - 1)]; - DCHECK(button); - NSRect buttonFrame = [button frame]; - y = buttonFrame.origin.y - 0.5 * bookmarks::kBookmarkVerticalPadding; - - } - } else { - NOTREACHED(); - } - - return y; -} - -- (ThemeProvider*)themeProvider { - return [parentController_ themeProvider]; -} - -- (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child { - // Do nothing. -} - -- (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child { - // Do nothing. -} - -- (BookmarkBarFolderController*)folderController { - return folderController_; -} - -// Add a new folder controller as triggered by the given folder button. -- (void)addNewFolderControllerWithParentButton:(BookmarkButton*)parentButton { - if (folderController_) - [self closeBookmarkFolder:self]; - - // Folder controller, like many window controllers, owns itself. - folderController_ = - [[BookmarkBarFolderController alloc] initWithParentButton:parentButton - parentController:self - barController:barController_]; - [folderController_ showWindow:self]; -} - -- (void)openAll:(const BookmarkNode*)node - disposition:(WindowOpenDisposition)disposition { - [barController_ openAll:node disposition:disposition]; -} - -- (void)addButtonForNode:(const BookmarkNode*)node - atIndex:(NSInteger)buttonIndex { - // Propose the frame for the new button. By default, this will be set to the - // topmost button's frame (and there will always be one) offset upward in - // anticipation of insertion. - NSRect newButtonFrame = [[buttons_ objectAtIndex:0] frame]; - newButtonFrame.origin.y += bookmarks::kBookmarkButtonVerticalSpan; - // When adding a button to an empty folder we must remove the 'empty' - // placeholder button. This can be detected by checking for a parent - // child count of 1. - const BookmarkNode* parentNode = node->GetParent(); - if (parentNode->GetChildCount() == 1) { - BookmarkButton* emptyButton = [buttons_ lastObject]; - newButtonFrame = [emptyButton frame]; - [emptyButton setDelegate:nil]; - [emptyButton removeFromSuperview]; - [buttons_ removeLastObject]; - } - - if (buttonIndex == -1 || buttonIndex > (NSInteger)[buttons_ count]) - buttonIndex = [buttons_ count]; - - // Offset upward by one button height all buttons above insertion location. - BookmarkButton* button = nil; // Remember so it can be de-highlighted. - for (NSInteger i = 0; i < buttonIndex; ++i) { - button = [buttons_ objectAtIndex:i]; - // Remember this location in case it's the last button being moved - // which is where the new button will be located. - newButtonFrame = [button frame]; - NSRect buttonFrame = [button frame]; - buttonFrame.origin.y += bookmarks::kBookmarkButtonVerticalSpan; - [button setFrame:buttonFrame]; - } - [[button cell] mouseExited:nil]; // De-highlight. - BookmarkButton* newButton = [self makeButtonForNode:node - frame:newButtonFrame]; - [buttons_ insertObject:newButton atIndex:buttonIndex]; - [mainView_ addSubview:newButton]; - - // Close any child folder(s) which may still be open. - [self closeBookmarkFolder:self]; - - // Prelim height of the window. We'll trim later as needed. - int height = [self windowHeightForButtonCount:[buttons_ count]]; - [self adjustWindowForHeight:height]; -} - -// More code which essentially duplicates that of BookmarkBarController. -// TODO(mrossetti,jrg): http://crbug.com/35966 -- (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point { - DCHECK([urls count] == [titles count]); - BOOL nodesWereAdded = NO; - // Figure out where these new bookmarks nodes are to be added. - BookmarkButton* button = [self buttonForDroppingOnAtPoint:point]; - BookmarkModel* bookmarkModel = [self bookmarkModel]; - const BookmarkNode* destParent = NULL; - int destIndex = 0; - if ([button isFolder]) { - destParent = [button bookmarkNode]; - // Drop it at the end. - destIndex = [button bookmarkNode]->GetChildCount(); - } else { - // Else we're dropping somewhere in the folder, so find the right spot. - destParent = [parentButton_ bookmarkNode]; - destIndex = [self indexForDragToPoint:point]; - // Be careful if the number of buttons != number of nodes. - destIndex += [[parentButton_ cell] startingChildIndex]; - } - - // Create and add the new bookmark nodes. - size_t urlCount = [urls count]; - for (size_t i = 0; i < urlCount; ++i) { - GURL gurl; - const char* string = [[urls objectAtIndex:i] UTF8String]; - if (string) - gurl = GURL(string); - // We only expect to receive valid URLs. - DCHECK(gurl.is_valid()); - if (gurl.is_valid()) { - bookmarkModel->AddURL(destParent, - destIndex++, - base::SysNSStringToUTF16([titles objectAtIndex:i]), - gurl); - nodesWereAdded = YES; - } - } - return nodesWereAdded; -} - -- (void)moveButtonFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex { - if (fromIndex != toIndex) { - if (toIndex == -1) - toIndex = [buttons_ count]; - BookmarkButton* movedButton = [buttons_ objectAtIndex:fromIndex]; - [buttons_ removeObjectAtIndex:fromIndex]; - NSRect movedFrame = [movedButton frame]; - NSPoint toOrigin = movedFrame.origin; - [movedButton setHidden:YES]; - if (fromIndex < toIndex) { - BookmarkButton* targetButton = [buttons_ objectAtIndex:toIndex - 1]; - toOrigin = [targetButton frame].origin; - for (NSInteger i = fromIndex; i < toIndex; ++i) { - BookmarkButton* button = [buttons_ objectAtIndex:i]; - NSRect frame = [button frame]; - frame.origin.y += bookmarks::kBookmarkButtonVerticalSpan; - [button setFrameOrigin:frame.origin]; - } - } else { - BookmarkButton* targetButton = [buttons_ objectAtIndex:toIndex]; - toOrigin = [targetButton frame].origin; - for (NSInteger i = fromIndex - 1; i >= toIndex; --i) { - BookmarkButton* button = [buttons_ objectAtIndex:i]; - NSRect buttonFrame = [button frame]; - buttonFrame.origin.y -= bookmarks::kBookmarkButtonVerticalSpan; - [button setFrameOrigin:buttonFrame.origin]; - } - } - [buttons_ insertObject:movedButton atIndex:toIndex]; - [movedButton setFrameOrigin:toOrigin]; - [movedButton setHidden:NO]; - } -} - -// TODO(jrg): Refactor BookmarkBarFolder common code. http://crbug.com/35966 -- (void)removeButton:(NSInteger)buttonIndex animate:(BOOL)animate { - // TODO(mrossetti): Get disappearing animation to work. http://crbug.com/42360 - BookmarkButton* oldButton = [buttons_ objectAtIndex:buttonIndex]; - NSPoint poofPoint = [oldButton screenLocationForRemoveAnimation]; - - // If a hover-open is pending, cancel it. - if (oldButton == buttonThatMouseIsIn_) { - [NSObject cancelPreviousPerformRequestsWithTarget:self]; - buttonThatMouseIsIn_ = nil; - } - - // Deleting a button causes rearrangement that enables us to lose a - // mouse-exited event. This problem doesn't appear to exist with - // other keep-menu-open options (e.g. add folder). Since the - // showsBorderOnlyWhileMouseInside uses a tracking area, simple - // tricks (e.g. sending an extra mouseExited: to the button) don't - // fix the problem. - // http://crbug.com/54324 - for (NSButton* button in buttons_.get()) { - if ([button showsBorderOnlyWhileMouseInside]) { - [button setShowsBorderOnlyWhileMouseInside:NO]; - [button setShowsBorderOnlyWhileMouseInside:YES]; - } - } - - [oldButton setDelegate:nil]; - [oldButton removeFromSuperview]; - if (animate && !ignoreAnimations_) - NSShowAnimationEffect(NSAnimationEffectDisappearingItemDefault, poofPoint, - NSZeroSize, nil, nil, nil); - [buttons_ removeObjectAtIndex:buttonIndex]; - for (NSInteger i = 0; i < buttonIndex; ++i) { - BookmarkButton* button = [buttons_ objectAtIndex:i]; - NSRect buttonFrame = [button frame]; - buttonFrame.origin.y -= bookmarks::kBookmarkButtonVerticalSpan; - [button setFrame:buttonFrame]; - } - // Search for and adjust submenus, if necessary. - NSInteger buttonCount = [buttons_ count]; - if (buttonCount) { - BookmarkButton* subButton = [folderController_ parentButton]; - for (NSInteger i = buttonIndex; i < buttonCount; ++i) { - BookmarkButton* aButton = [buttons_ objectAtIndex:i]; - // If this button is showing its menu then we need to move the menu, too. - if (aButton == subButton) - [folderController_ offsetFolderMenuWindow:NSMakeSize(0.0, - bookmarks::kBookmarkBarHeight)]; - } - } else { - // If all nodes have been removed from this folder then add in the - // 'empty' placeholder button. - NSRect buttonFrame = - NSMakeRect(bookmarks::kBookmarkSubMenuHorizontalPadding, - bookmarks::kBookmarkButtonHeight - - (bookmarks::kBookmarkBarHeight - - bookmarks::kBookmarkVerticalPadding), - bookmarks::kDefaultBookmarkWidth, - (bookmarks::kBookmarkBarHeight - - 2 * bookmarks::kBookmarkVerticalPadding)); - BookmarkButton* button = [self makeButtonForNode:nil - frame:buttonFrame]; - [buttons_ addObject:button]; - [mainView_ addSubview:button]; - buttonCount = 1; - } - - // Propose a height for the window. We'll trim later as needed. - [self adjustWindowForHeight:[self windowHeightForButtonCount:buttonCount]]; -} - -- (id<BookmarkButtonControllerProtocol>)controllerForNode: - (const BookmarkNode*)node { - // See if we are holding this node, otherwise see if it is in our - // hierarchy of visible folder menus. - if ([parentButton_ bookmarkNode] == node) - return self; - return [folderController_ controllerForNode:node]; -} - -#pragma mark TestingAPI Only - -- (void)setIgnoreAnimations:(BOOL)ignore { - ignoreAnimations_ = ignore; -} - -- (BookmarkButton*)buttonThatMouseIsIn { - return buttonThatMouseIsIn_; -} - -@end // BookmarkBarFolderController diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller_unittest.mm deleted file mode 100644 index 890c0fc..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller_unittest.mm +++ /dev/null @@ -1,1552 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#include "base/basictypes.h" -#include "base/scoped_nsobject.h" -#include "base/utf_string_conversions.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_constants.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_button_cell.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_unittest_helper.h" -#include "chrome/browser/cocoa/browser_test_helper.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" -#import "chrome/browser/cocoa/view_resizer_pong.h" -#include "chrome/test/model_test_utils.h" -#include "testing/gtest/include/gtest/gtest.h" -#import "testing/gtest_mac.h" -#include "testing/platform_test.h" - -// Add a redirect to make testing easier. -@interface BookmarkBarFolderController(MakeTestingEasier) -- (IBAction)openBookmarkFolderFromButton:(id)sender; -- (void)validateMenuSpacing; -@end - -@implementation BookmarkBarFolderController(MakeTestingEasier) -- (IBAction)openBookmarkFolderFromButton:(id)sender { - [[self folderTarget] openBookmarkFolderFromButton:sender]; -} - -// Utility function to verify that the buttons in this folder are all -// evenly spaced in a progressive manner. -- (void)validateMenuSpacing { - BOOL firstButton = YES; - CGFloat lastVerticalOffset = 0.0; - for (BookmarkButton* button in [self buttons]) { - if (firstButton) { - firstButton = NO; - lastVerticalOffset = [button frame].origin.y; - } else { - CGFloat nextVerticalOffset = [button frame].origin.y; - EXPECT_CGFLOAT_EQ(lastVerticalOffset - - bookmarks::kBookmarkButtonVerticalSpan, - nextVerticalOffset); - lastVerticalOffset = nextVerticalOffset; - } - } -} -@end - -// Don't use a high window level when running unit tests -- it'll -// interfere with anything else you are working on. -// For testing. -@interface BookmarkBarFolderControllerNoLevel : BookmarkBarFolderController -@end - -@implementation BookmarkBarFolderControllerNoLevel -- (void)configureWindowLevel { - // Intentionally empty. -} -@end - -// No window level and the ability to fake the "top left" point of the window. -// For testing. -@interface BookmarkBarFolderControllerLow : BookmarkBarFolderControllerNoLevel { - BOOL realTopLeft_; // Use the real windowTopLeft call? -} -@property (nonatomic) BOOL realTopLeft; -@end - - -@implementation BookmarkBarFolderControllerLow - -@synthesize realTopLeft = realTopLeft_; - -- (NSPoint)windowTopLeftForWidth:(int)width { - return realTopLeft_ ? [super windowTopLeftForWidth:width] : - NSMakePoint(200,200); -} - -@end - - -@interface BookmarkBarFolderControllerPong : BookmarkBarFolderControllerLow { - BOOL childFolderWillShow_; - BOOL childFolderWillClose_; -} -@property (nonatomic, readonly) BOOL childFolderWillShow; -@property (nonatomic, readonly) BOOL childFolderWillClose; -@end - -@implementation BookmarkBarFolderControllerPong -@synthesize childFolderWillShow = childFolderWillShow_; -@synthesize childFolderWillClose = childFolderWillClose_; - -- (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child { - childFolderWillShow_ = YES; -} - -- (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child { - childFolderWillClose_ = YES; -} - -// We don't have a real BookmarkBarController as our parent root so -// we fake this one out. -- (void)closeAllBookmarkFolders { - [self closeBookmarkFolder:self]; -} - -@end - -namespace { -const int kLotsOfNodesCount = 150; -}; - - -// Redirect certain calls so they can be seen by tests. - -@interface BookmarkBarControllerChildFolderRedirect : BookmarkBarController { - BookmarkBarFolderController* childFolderDelegate_; -} -@property (nonatomic, assign) BookmarkBarFolderController* childFolderDelegate; -@end - -@implementation BookmarkBarControllerChildFolderRedirect - -@synthesize childFolderDelegate = childFolderDelegate_; - -- (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child { - [childFolderDelegate_ childFolderWillShow:child]; -} - -- (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child { - [childFolderDelegate_ childFolderWillClose:child]; -} - -@end - - -class BookmarkBarFolderControllerTest : public CocoaTest { - public: - BrowserTestHelper helper_; - scoped_nsobject<BookmarkBarControllerChildFolderRedirect> bar_; - const BookmarkNode* folderA_; // owned by model - const BookmarkNode* longTitleNode_; // owned by model - - BookmarkBarFolderControllerTest() { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - const BookmarkNode* folderA = model->AddGroup(parent, - parent->GetChildCount(), - ASCIIToUTF16("group")); - folderA_ = folderA; - model->AddGroup(parent, parent->GetChildCount(), - ASCIIToUTF16("sibbling group")); - const BookmarkNode* folderB = model->AddGroup(folderA, - folderA->GetChildCount(), - ASCIIToUTF16("subgroup 1")); - model->AddGroup(folderA, - folderA->GetChildCount(), - ASCIIToUTF16("subgroup 2")); - model->AddURL(folderA, folderA->GetChildCount(), ASCIIToUTF16("title a"), - GURL("http://www.google.com/a")); - longTitleNode_ = model->AddURL( - folderA, folderA->GetChildCount(), - ASCIIToUTF16("title super duper long long whoa momma title you betcha"), - GURL("http://www.google.com/b")); - model->AddURL(folderB, folderB->GetChildCount(), ASCIIToUTF16("t"), - GURL("http://www.google.com/c")); - - bar_.reset( - [[BookmarkBarControllerChildFolderRedirect alloc] - initWithBrowser:helper_.browser() - initialWidth:300 - delegate:nil - resizeDelegate:nil]); - [bar_ loaded:model]; - // Make parent frame for bookmark bar then open it. - NSRect frame = [[test_window() contentView] frame]; - frame = NSInsetRect(frame, 100, 200); - NSView* fakeToolbarView = [[[NSView alloc] initWithFrame:frame] - autorelease]; - [[test_window() contentView] addSubview:fakeToolbarView]; - [fakeToolbarView addSubview:[bar_ view]]; - [bar_ setBookmarkBarEnabled:YES]; - } - - // Remove the bookmark with the long title. - void RemoveLongTitleNode() { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - model->Remove(longTitleNode_->GetParent(), - longTitleNode_->GetParent()->IndexOfChild(longTitleNode_)); - } - - // Add LOTS of nodes to our model if needed (e.g. scrolling). - // Returns the number of nodes added. - int AddLotsOfNodes() { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - for (int i = 0; i < kLotsOfNodesCount; i++) { - model->AddURL(folderA_, folderA_->GetChildCount(), - ASCIIToUTF16("repeated title"), - GURL("http://www.google.com/repeated/url")); - } - return kLotsOfNodesCount; - } - - // Return a simple BookmarkBarFolderController. - BookmarkBarFolderControllerPong* SimpleBookmarkBarFolderController() { - BookmarkButton* parentButton = [[bar_ buttons] objectAtIndex:0]; - BookmarkBarFolderControllerPong* c = - [[BookmarkBarFolderControllerPong alloc] - initWithParentButton:parentButton - parentController:nil - barController:bar_]; - [c window]; // Force nib load. - return c; - } -}; - -TEST_F(BookmarkBarFolderControllerTest, InitCreateAndDelete) { - scoped_nsobject<BookmarkBarFolderController> bbfc; - bbfc.reset(SimpleBookmarkBarFolderController()); - - // Make sure none of the buttons overlap, that all are inside - // the content frame, and their cells are of the proper class. - NSArray* buttons = [bbfc buttons]; - EXPECT_TRUE([buttons count]); - for (unsigned int i = 0; i < ([buttons count]-1); i++) { - EXPECT_FALSE(NSContainsRect([[buttons objectAtIndex:i] frame], - [[buttons objectAtIndex:i+1] frame])); - } - Class cellClass = [BookmarkBarFolderButtonCell class]; - for (BookmarkButton* button in buttons) { - NSRect r = [[bbfc mainView] convertRect:[button frame] fromView:button]; - // TODO(jrg): remove this adjustment. - NSRect bigger = NSInsetRect([[bbfc mainView] frame], -2, 0); - EXPECT_TRUE(NSContainsRect(bigger, r)); - EXPECT_TRUE([[button cell] isKindOfClass:cellClass]); - } - - // Confirm folder buttons have no tooltip. The important thing - // really is that we insure folders and non-folders are treated - // differently; not sure of any other generic way to do this. - for (BookmarkButton* button in buttons) { - if ([button isFolder]) - EXPECT_FALSE([button toolTip]); - else - EXPECT_TRUE([button toolTip]); - } -} - -// Make sure closing of the window releases the controller. -// (e.g. valgrind shouldn't complain if we do this). -TEST_F(BookmarkBarFolderControllerTest, ReleaseOnClose) { - scoped_nsobject<BookmarkBarFolderController> bbfc; - bbfc.reset(SimpleBookmarkBarFolderController()); - EXPECT_TRUE(bbfc.get()); - - [bbfc retain]; // stop the scoped_nsobject from doing anything - [[bbfc window] close]; // trigger an autorelease of bbfc.get() -} - -TEST_F(BookmarkBarFolderControllerTest, BasicPosition) { - BookmarkButton* parentButton = [[bar_ buttons] objectAtIndex:0]; - EXPECT_TRUE(parentButton); - - // If parent is a BookmarkBarController, grow down. - scoped_nsobject<BookmarkBarFolderControllerLow> bbfc; - bbfc.reset([[BookmarkBarFolderControllerLow alloc] - initWithParentButton:parentButton - parentController:nil - barController:bar_]); - [bbfc window]; - [bbfc setRealTopLeft:YES]; - NSPoint pt = [bbfc windowTopLeftForWidth:0]; // screen coords - NSPoint buttonOriginInScreen = - [[parentButton window] - convertBaseToScreen:[parentButton - convertRectToBase:[parentButton frame]].origin]; - // Within margin - EXPECT_LE(abs(pt.x - buttonOriginInScreen.x), - bookmarks::kBookmarkMenuOverlap+1); - EXPECT_LE(abs(pt.y - buttonOriginInScreen.y), - bookmarks::kBookmarkMenuOverlap+1); - - // Make sure we see the window shift left if it spills off the screen - pt = [bbfc windowTopLeftForWidth:0]; - NSPoint shifted = [bbfc windowTopLeftForWidth:9999999]; - EXPECT_LT(shifted.x, pt.x); - - // If parent is a BookmarkBarFolderController, grow right. - scoped_nsobject<BookmarkBarFolderControllerLow> bbfc2; - bbfc2.reset([[BookmarkBarFolderControllerLow alloc] - initWithParentButton:[[bbfc buttons] objectAtIndex:0] - parentController:bbfc.get() - barController:bar_]); - [bbfc2 window]; - [bbfc2 setRealTopLeft:YES]; - pt = [bbfc2 windowTopLeftForWidth:0]; - // We're now overlapping the window a bit. - EXPECT_EQ(pt.x, NSMaxX([[bbfc.get() window] frame]) - - bookmarks::kBookmarkMenuOverlap); -} - -// Confirm we grow right until end of screen, then start growing left -// until end of screen again, then right. -TEST_F(BookmarkBarFolderControllerTest, PositionRightLeftRight) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - const BookmarkNode* folder = parent; - - const int count = 100; - int i; - // Make some super duper deeply nested folders. - for (i=0; i<count; i++) { - folder = model->AddGroup(folder, 0, ASCIIToUTF16("nested folder")); - } - - // Setup initial state for opening all folders. - folder = parent; - BookmarkButton* parentButton = [[bar_ buttons] objectAtIndex:0]; - BookmarkBarFolderController* parentController = nil; - EXPECT_TRUE(parentButton); - - // Open them all. - scoped_nsobject<NSMutableArray> folder_controller_array; - folder_controller_array.reset([[NSMutableArray array] retain]); - for (i=0; i<count; i++) { - BookmarkBarFolderControllerNoLevel* bbfcl = - [[BookmarkBarFolderControllerNoLevel alloc] - initWithParentButton:parentButton - parentController:parentController - barController:bar_]; - [folder_controller_array addObject:bbfcl]; - [bbfcl autorelease]; - [bbfcl window]; - parentController = bbfcl; - parentButton = [[bbfcl buttons] objectAtIndex:0]; - } - - // Make vector of all x positions. - std::vector<CGFloat> leftPositions; - for (i=0; i<count; i++) { - CGFloat x = [[[folder_controller_array objectAtIndex:i] window] - frame].origin.x; - leftPositions.push_back(x); - } - - // Make sure the first few grow right. - for (i=0; i<3; i++) - EXPECT_TRUE(leftPositions[i+1] > leftPositions[i]); - - // Look for the first "grow left". - while (leftPositions[i] > leftPositions[i-1]) - i++; - // Confirm the next few also grow left. - int j; - for (j=i; j<i+3; j++) - EXPECT_TRUE(leftPositions[j+1] < leftPositions[j]); - i = j; - - // Finally, confirm we see a "grow right" once more. - while (leftPositions[i] < leftPositions[i-1]) - i++; - // (No need to EXPECT a final "grow right"; if we didn't find one - // we'd get a C++ array bounds exception). -} - -TEST_F(BookmarkBarFolderControllerTest, DropDestination) { - scoped_nsobject<BookmarkBarFolderController> bbfc; - bbfc.reset(SimpleBookmarkBarFolderController()); - EXPECT_TRUE(bbfc.get()); - - // Confirm "off the top" and "off the bottom" match no buttons. - NSPoint p = NSMakePoint(NSMidX([[bbfc mainView] frame]), 10000); - EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:p]); - EXPECT_TRUE([bbfc shouldShowIndicatorShownForPoint:p]); - p = NSMakePoint(NSMidX([[bbfc mainView] frame]), -1); - EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:p]); - EXPECT_TRUE([bbfc shouldShowIndicatorShownForPoint:p]); - - // Confirm "right in the center" (give or take a pixel) is a match, - // and confirm "just barely in the button" is not. Anything more - // specific seems likely to be tweaked. We don't loop over all - // buttons because the scroll view makes them not visible. - for (BookmarkButton* button in [bbfc buttons]) { - CGFloat x = NSMidX([button frame]); - CGFloat y = NSMidY([button frame]); - // Somewhere near the center: a match (but only if a folder!) - if ([button isFolder]) { - EXPECT_EQ(button, - [bbfc buttonForDroppingOnAtPoint:NSMakePoint(x-1, y+1)]); - EXPECT_EQ(button, - [bbfc buttonForDroppingOnAtPoint:NSMakePoint(x+1, y-1)]); - EXPECT_FALSE([bbfc shouldShowIndicatorShownForPoint:NSMakePoint(x, y)]);; - } else { - // If not a folder we don't drop into it. - EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:NSMakePoint(x-1, y+1)]); - EXPECT_FALSE([bbfc buttonForDroppingOnAtPoint:NSMakePoint(x+1, y-1)]); - EXPECT_TRUE([bbfc shouldShowIndicatorShownForPoint:NSMakePoint(x, y)]);; - } - } -} - -TEST_F(BookmarkBarFolderControllerTest, OpenFolder) { - scoped_nsobject<BookmarkBarFolderController> bbfc; - bbfc.reset(SimpleBookmarkBarFolderController()); - EXPECT_TRUE(bbfc.get()); - - EXPECT_FALSE([bbfc folderController]); - BookmarkButton* button = [[bbfc buttons] objectAtIndex:0]; - [bbfc openBookmarkFolderFromButton:button]; - id controller = [bbfc folderController]; - EXPECT_TRUE(controller); - EXPECT_EQ([controller parentButton], button); - - // Click the same one --> it gets closed. - [bbfc openBookmarkFolderFromButton:[[bbfc buttons] objectAtIndex:0]]; - EXPECT_FALSE([bbfc folderController]); - - // Open a new one --> change. - [bbfc openBookmarkFolderFromButton:[[bbfc buttons] objectAtIndex:1]]; - EXPECT_NE(controller, [bbfc folderController]); - EXPECT_NE([[bbfc folderController] parentButton], button); - - // Close it --> all gone! - [bbfc closeBookmarkFolder:nil]; - EXPECT_FALSE([bbfc folderController]); -} - -TEST_F(BookmarkBarFolderControllerTest, ChildFolderCallbacks) { - scoped_nsobject<BookmarkBarFolderControllerPong> bbfc; - bbfc.reset(SimpleBookmarkBarFolderController()); - EXPECT_TRUE(bbfc.get()); - [bar_ setChildFolderDelegate:bbfc.get()]; - - EXPECT_FALSE([bbfc childFolderWillShow]); - [bbfc openBookmarkFolderFromButton:[[bbfc buttons] objectAtIndex:0]]; - EXPECT_TRUE([bbfc childFolderWillShow]); - - EXPECT_FALSE([bbfc childFolderWillClose]); - [bbfc closeBookmarkFolder:nil]; - EXPECT_TRUE([bbfc childFolderWillClose]); - - [bar_ setChildFolderDelegate:nil]; -} - -// Make sure bookmark folders have variable widths. -TEST_F(BookmarkBarFolderControllerTest, ChildFolderWidth) { - scoped_nsobject<BookmarkBarFolderController> bbfc; - - bbfc.reset(SimpleBookmarkBarFolderController()); - EXPECT_TRUE(bbfc.get()); - [bbfc showWindow:bbfc.get()]; - CGFloat wideWidth = NSWidth([[bbfc window] frame]); - - RemoveLongTitleNode(); - bbfc.reset(SimpleBookmarkBarFolderController()); - EXPECT_TRUE(bbfc.get()); - CGFloat thinWidth = NSWidth([[bbfc window] frame]); - - // Make sure window size changed as expected. - EXPECT_GT(wideWidth, thinWidth); -} - -// Simple scrolling tests. -TEST_F(BookmarkBarFolderControllerTest, SimpleScroll) { - scoped_nsobject<BookmarkBarFolderController> bbfc; - - int nodecount = AddLotsOfNodes(); - bbfc.reset(SimpleBookmarkBarFolderController()); - EXPECT_TRUE(bbfc.get()); - [bbfc showWindow:bbfc.get()]; - - // Make sure the window fits on the screen. - EXPECT_LT(NSHeight([[bbfc window] frame]), - NSHeight([[NSScreen mainScreen] frame])); - - // Verify the logic used by the scroll arrow code. - EXPECT_TRUE([bbfc canScrollUp]); - EXPECT_FALSE([bbfc canScrollDown]); - - // Scroll it up. Make sure the window has gotten bigger each time. - // Also, for each scroll, make sure our hit test finds a new button - // (to confirm the content area changed). - NSView* savedHit = nil; - for (int i=0; i<3; i++) { - CGFloat height = NSHeight([[bbfc window] frame]); - [bbfc performOneScroll:60]; - EXPECT_GT(NSHeight([[bbfc window] frame]), height); - NSView* hit = [[[bbfc window] contentView] hitTest:NSMakePoint(22, 22)]; - EXPECT_NE(hit, savedHit); - savedHit = hit; - } - - // Keep scrolling up; make sure we never get bigger than the screen. - // Also confirm we never scroll the window off the screen. - bool bothAtOnce = false; - NSRect screenFrame = [[NSScreen mainScreen] frame]; - for (int i = 0; i < nodecount; i++) { - [bbfc performOneScroll:60]; - EXPECT_TRUE(NSContainsRect(screenFrame, - [[bbfc window] frame])); - // Make sure, sometime during our scroll, we have the ability to - // scroll in either direction. - if ([bbfc canScrollUp] && - [bbfc canScrollDown]) - bothAtOnce = true; - } - EXPECT_TRUE(bothAtOnce); - - // Once we've scrolled to the end, our only option should be to scroll back. - EXPECT_FALSE([bbfc canScrollUp]); - EXPECT_TRUE([bbfc canScrollDown]); - - // Now scroll down and make sure the window size does not change. - // Also confirm we never scroll the window off the screen the other - // way. - for (int i=0; i<nodecount+50; i++) { - CGFloat height = NSHeight([[bbfc window] frame]); - [bbfc performOneScroll:-60]; - EXPECT_EQ(height, NSHeight([[bbfc window] frame])); - EXPECT_TRUE(NSContainsRect(screenFrame, - [[bbfc window] frame])); - } -} - -// Folder menu sizing and placementwhile deleting bookmarks and scrolling tests. -TEST_F(BookmarkBarFolderControllerTest, MenuPlacementWhileScrollingDeleting) { - scoped_nsobject<BookmarkBarFolderController> bbfc; - AddLotsOfNodes(); - bbfc.reset(SimpleBookmarkBarFolderController()); - [bbfc showWindow:bbfc.get()]; - NSWindow* menuWindow = [bbfc window]; - BookmarkBarFolderController* folder = [bar_ folderController]; - NSArray* buttons = [folder buttons]; - - // Before scrolling any, delete a bookmark and make sure the window top has - // not moved. Pick a button which is near the top and visible. - CGFloat oldTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]); - BookmarkButton* button = [buttons objectAtIndex:3]; - [folder deleteBookmark:button]; - CGFloat newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]); - EXPECT_CGFLOAT_EQ(oldTop, newTop); - - // Scroll so that both the top and bottom scroll arrows show, make sure - // the top of the window has moved up, then delete a visible button and - // make sure the top has not moved. - oldTop = newTop; - const CGFloat scrollOneBookmark = bookmarks::kBookmarkButtonHeight + - bookmarks::kBookmarkVerticalPadding; - NSUInteger buttonCounter = 0; - NSUInteger extraButtonLimit = 3; - while (![bbfc canScrollDown] || extraButtonLimit > 0) { - [bbfc performOneScroll:scrollOneBookmark]; - ++buttonCounter; - if ([bbfc canScrollDown]) - --extraButtonLimit; - } - newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]); - EXPECT_NE(oldTop, newTop); - oldTop = newTop; - button = [buttons objectAtIndex:buttonCounter + 3]; - [folder deleteBookmark:button]; - newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]); - EXPECT_CGFLOAT_EQ(oldTop, newTop); - - // Scroll so that the top scroll arrow is no longer showing, make sure - // the top of the window has not moved, then delete a visible button and - // make sure the top has not moved. - while ([bbfc canScrollDown]) { - [bbfc performOneScroll:-scrollOneBookmark]; - --buttonCounter; - } - button = [buttons objectAtIndex:buttonCounter + 3]; - [folder deleteBookmark:button]; - newTop = [menuWindow frame].origin.y + NSHeight([menuWindow frame]); - EXPECT_CGFLOAT_EQ(oldTop, newTop); -} - -@interface FakedDragInfo : NSObject { -@public - NSPoint dropLocation_; - NSDragOperation sourceMask_; -} -@property (nonatomic, assign) NSPoint dropLocation; -- (void)setDraggingSourceOperationMask:(NSDragOperation)mask; -@end - -@implementation FakedDragInfo - -@synthesize dropLocation = dropLocation_; - -- (id)init { - if ((self = [super init])) { - dropLocation_ = NSZeroPoint; - sourceMask_ = NSDragOperationMove; - } - return self; -} - -// NSDraggingInfo protocol functions. - -- (id)draggingPasteboard { - return self; -} - -- (id)draggingSource { - return self; -} - -- (NSDragOperation)draggingSourceOperationMask { - return sourceMask_; -} - -- (NSPoint)draggingLocation { - return dropLocation_; -} - -// Other functions. - -- (void)setDraggingSourceOperationMask:(NSDragOperation)mask { - sourceMask_ = mask; -} - -@end - - -class BookmarkBarFolderControllerMenuTest : public CocoaTest { - public: - BrowserTestHelper helper_; - scoped_nsobject<NSView> parent_view_; - scoped_nsobject<ViewResizerPong> resizeDelegate_; - scoped_nsobject<BookmarkBarController> bar_; - - BookmarkBarFolderControllerMenuTest() { - resizeDelegate_.reset([[ViewResizerPong alloc] init]); - NSRect parent_frame = NSMakeRect(0, 0, 800, 50); - parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]); - [parent_view_ setHidden:YES]; - bar_.reset([[BookmarkBarController alloc] - initWithBrowser:helper_.browser() - initialWidth:NSWidth(parent_frame) - delegate:nil - resizeDelegate:resizeDelegate_.get()]); - InstallAndToggleBar(bar_.get()); - } - - void InstallAndToggleBar(BookmarkBarController* bar) { - // Force loading of the nib. - [bar view]; - // Awkwardness to look like we've been installed. - [parent_view_ addSubview:[bar view]]; - NSRect frame = [[[bar view] superview] frame]; - frame.origin.y = 100; - [[[bar view] superview] setFrame:frame]; - - // Make sure it's on in a window so viewDidMoveToWindow is called - [[test_window() contentView] addSubview:parent_view_]; - - // Make sure it's open so certain things aren't no-ops. - [bar updateAndShowNormalBar:YES - showDetachedBar:NO - withAnimation:NO]; - } -}; - -TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveBarBookmarkToFolder) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b " - "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b " - "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - - // Pop up a folder menu and drag in a button from the bar. - BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"2f"]; - NSRect oldToFolderFrame = [toFolder frame]; - [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:toFolder]; - BookmarkBarFolderController* folderController = [bar_ folderController]; - EXPECT_TRUE(folderController); - NSWindow* toWindow = [folderController window]; - EXPECT_TRUE(toWindow); - NSRect oldToWindowFrame = [toWindow frame]; - // Drag a bar button onto a bookmark (i.e. not a folder) in a folder - // so it should end up below the target bookmark. - BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"1b"]; - ASSERT_TRUE(draggedButton); - CGFloat horizontalShift = - NSWidth([draggedButton frame]) + bookmarks::kBookmarkHorizontalPadding; - BookmarkButton* targetButton = - [folderController buttonWithTitleEqualTo:@"2f1b"]; - ASSERT_TRUE(targetButton); - [folderController dragButton:draggedButton - to:[targetButton center] - copy:NO]; - // The button should have landed just after "2f1b". - const std::string expected_string("2f:[ 2f1b 1b 2f2f:[ 2f2f1b " - "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ " - "4f2f1b 4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b "); - EXPECT_EQ(expected_string, model_test_utils::ModelStringFromNode(root)); - - // Verify the window still appears by looking for its controller. - EXPECT_TRUE([bar_ folderController]); - - // Gather the new frames. - NSRect newToFolderFrame = [toFolder frame]; - NSRect newToWindowFrame = [toWindow frame]; - // The toFolder should have shifted left horizontally but not vertically. - NSRect expectedToFolderFrame = - NSOffsetRect(oldToFolderFrame, -horizontalShift, 0); - EXPECT_NSRECT_EQ(expectedToFolderFrame, newToFolderFrame); - // The toWindow should have shifted left horizontally, down vertically, - // and grown vertically. - NSRect expectedToWindowFrame = oldToWindowFrame; - expectedToWindowFrame.origin.x -= horizontalShift; - CGFloat diff = (bookmarks::kBookmarkBarHeight + - 2*bookmarks::kBookmarkVerticalPadding); - expectedToWindowFrame.origin.y -= diff; - expectedToWindowFrame.size.height += diff; - EXPECT_NSRECT_EQ(expectedToWindowFrame, newToWindowFrame); - - // Check button spacing. - [folderController validateMenuSpacing]; - - // Move the button back to the bar at the beginning. - draggedButton = [folderController buttonWithTitleEqualTo:@"1b"]; - ASSERT_TRUE(draggedButton); - targetButton = [bar_ buttonWithTitleEqualTo:@"2f"]; - ASSERT_TRUE(targetButton); - [bar_ dragButton:draggedButton - to:[targetButton left] - copy:NO]; - EXPECT_EQ(model_string, model_test_utils::ModelStringFromNode(root)); - // Don't check the folder window since it's not supposed to be showing. -} - -TEST_F(BookmarkBarFolderControllerMenuTest, DragCopyBarBookmarkToFolder) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b " - "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b " - "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - - // Pop up a folder menu and copy in a button from the bar. - BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"2f"]; - ASSERT_TRUE(toFolder); - NSRect oldToFolderFrame = [toFolder frame]; - [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:toFolder]; - BookmarkBarFolderController* folderController = [bar_ folderController]; - EXPECT_TRUE(folderController); - NSWindow* toWindow = [folderController window]; - EXPECT_TRUE(toWindow); - NSRect oldToWindowFrame = [toWindow frame]; - // Drag a bar button onto a bookmark (i.e. not a folder) in a folder - // so it should end up below the target bookmark. - BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"1b"]; - ASSERT_TRUE(draggedButton); - BookmarkButton* targetButton = - [folderController buttonWithTitleEqualTo:@"2f1b"]; - ASSERT_TRUE(targetButton); - [folderController dragButton:draggedButton - to:[targetButton center] - copy:YES]; - // The button should have landed just after "2f1b". - const std::string expected_1("1b 2f:[ 2f1b 1b 2f2f:[ 2f2f1b " - "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ " - "4f2f1b 4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b "); - EXPECT_EQ(expected_1, model_test_utils::ModelStringFromNode(root)); - - // Gather the new frames. - NSRect newToFolderFrame = [toFolder frame]; - NSRect newToWindowFrame = [toWindow frame]; - // The toFolder should have shifted. - EXPECT_NSRECT_EQ(oldToFolderFrame, newToFolderFrame); - // The toWindow should have shifted down vertically and grown vertically. - NSRect expectedToWindowFrame = oldToWindowFrame; - CGFloat diff = (bookmarks::kBookmarkBarHeight + - 2*bookmarks::kBookmarkVerticalPadding); - expectedToWindowFrame.origin.y -= diff; - expectedToWindowFrame.size.height += diff; - EXPECT_NSRECT_EQ(expectedToWindowFrame, newToWindowFrame); - - // Copy the button back to the bar after "3b". - draggedButton = [folderController buttonWithTitleEqualTo:@"1b"]; - ASSERT_TRUE(draggedButton); - targetButton = [bar_ buttonWithTitleEqualTo:@"4f"]; - ASSERT_TRUE(targetButton); - [bar_ dragButton:draggedButton - to:[targetButton left] - copy:YES]; - const std::string expected_2("1b 2f:[ 2f1b 1b 2f2f:[ 2f2f1b " - "2f2f2b 2f2f3b ] 2f3b ] 3b 1b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ " - "4f2f1b 4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b "); - EXPECT_EQ(expected_2, model_test_utils::ModelStringFromNode(root)); -} - -TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveBarBookmarkToSubfolder) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b " - "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b " - "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - - // Pop up a folder menu and a subfolder menu. - BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"]; - ASSERT_TRUE(toFolder); - [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:toFolder]; - BookmarkBarFolderController* folderController = [bar_ folderController]; - EXPECT_TRUE(folderController); - NSWindow* toWindow = [folderController window]; - EXPECT_TRUE(toWindow); - NSRect oldToWindowFrame = [toWindow frame]; - BookmarkButton* toSubfolder = - [folderController buttonWithTitleEqualTo:@"4f2f"]; - ASSERT_TRUE(toSubfolder); - [[toSubfolder target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:toSubfolder]; - BookmarkBarFolderController* subfolderController = - [folderController folderController]; - EXPECT_TRUE(subfolderController); - NSWindow* toSubwindow = [subfolderController window]; - EXPECT_TRUE(toSubwindow); - NSRect oldToSubwindowFrame = [toSubwindow frame]; - // Drag a bar button onto a bookmark (i.e. not a folder) in a folder - // so it should end up below the target bookmark. - BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"5b"]; - ASSERT_TRUE(draggedButton); - BookmarkButton* targetButton = - [subfolderController buttonWithTitleEqualTo:@"4f2f3b"]; - ASSERT_TRUE(targetButton); - [subfolderController dragButton:draggedButton - to:[targetButton center] - copy:NO]; - // The button should have landed just after "2f". - const std::string expected_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b " - "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ " - "4f2f1b 4f2f2b 4f2f3b 5b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] "); - EXPECT_EQ(expected_string, model_test_utils::ModelStringFromNode(root)); - - // Check button spacing. - [folderController validateMenuSpacing]; - [subfolderController validateMenuSpacing]; - - // Check the window layouts. The folder window should not have changed, - // but the subfolder window should have shifted vertically and grown. - NSRect newToWindowFrame = [toWindow frame]; - EXPECT_NSRECT_EQ(oldToWindowFrame, newToWindowFrame); - NSRect newToSubwindowFrame = [toSubwindow frame]; - NSRect expectedToSubwindowFrame = oldToSubwindowFrame; - expectedToSubwindowFrame.origin.y -= - bookmarks::kBookmarkBarHeight + bookmarks::kVisualHeightOffset; - expectedToSubwindowFrame.size.height += - bookmarks::kBookmarkBarHeight + bookmarks::kVisualHeightOffset; - EXPECT_NSRECT_EQ(expectedToSubwindowFrame, newToSubwindowFrame); -} - -TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveWithinFolder) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b " - "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b " - "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - - // Pop up a folder menu. - BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"]; - ASSERT_TRUE(toFolder); - [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:toFolder]; - BookmarkBarFolderController* folderController = [bar_ folderController]; - EXPECT_TRUE(folderController); - NSWindow* toWindow = [folderController window]; - EXPECT_TRUE(toWindow); - NSRect oldToWindowFrame = [toWindow frame]; - // Drag a folder button to the top within the same parent. - BookmarkButton* draggedButton = - [folderController buttonWithTitleEqualTo:@"4f2f"]; - ASSERT_TRUE(draggedButton); - BookmarkButton* targetButton = - [folderController buttonWithTitleEqualTo:@"4f1f"]; - ASSERT_TRUE(targetButton); - [folderController dragButton:draggedButton - to:[targetButton top] - copy:NO]; - // The button should have landed above "4f1f". - const std::string expected_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b " - "2f2f2b 2f2f3b ] 2f3b ] 3b 4f:[ 4f2f:[ 4f2f1b 4f2f2b 4f2f3b ] " - "4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b "); - EXPECT_EQ(expected_string, model_test_utils::ModelStringFromNode(root)); - - // The window should not have gone away. - EXPECT_TRUE([bar_ folderController]); - - // The folder window should not have changed. - NSRect newToWindowFrame = [toWindow frame]; - EXPECT_NSRECT_EQ(oldToWindowFrame, newToWindowFrame); - - // Check button spacing. - [folderController validateMenuSpacing]; -} - -TEST_F(BookmarkBarFolderControllerMenuTest, DragParentOntoChild) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b " - "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b " - "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - - // Pop up a folder menu. - BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"]; - ASSERT_TRUE(toFolder); - [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:toFolder]; - BookmarkBarFolderController* folderController = [bar_ folderController]; - EXPECT_TRUE(folderController); - NSWindow* toWindow = [folderController window]; - EXPECT_TRUE(toWindow); - // Drag a folder button to one of its children. - BookmarkButton* draggedButton = [bar_ buttonWithTitleEqualTo:@"4f"]; - ASSERT_TRUE(draggedButton); - BookmarkButton* targetButton = - [folderController buttonWithTitleEqualTo:@"4f3f"]; - ASSERT_TRUE(targetButton); - [folderController dragButton:draggedButton - to:[targetButton top] - copy:NO]; - // The model should not have changed. - EXPECT_EQ(model_string, model_test_utils::ModelStringFromNode(root)); - - // Check button spacing. - [folderController validateMenuSpacing]; -} - -TEST_F(BookmarkBarFolderControllerMenuTest, DragMoveChildToParent) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b " - "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f:[ 4f2f1b " - "4f2f2b 4f2f3b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - - // Pop up a folder menu and a subfolder menu. - BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"4f"]; - ASSERT_TRUE(toFolder); - [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:toFolder]; - BookmarkBarFolderController* folderController = [bar_ folderController]; - EXPECT_TRUE(folderController); - BookmarkButton* toSubfolder = - [folderController buttonWithTitleEqualTo:@"4f2f"]; - ASSERT_TRUE(toSubfolder); - [[toSubfolder target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:toSubfolder]; - BookmarkBarFolderController* subfolderController = - [folderController folderController]; - EXPECT_TRUE(subfolderController); - - // Drag a subfolder bookmark to the parent folder. - BookmarkButton* draggedButton = - [subfolderController buttonWithTitleEqualTo:@"4f2f3b"]; - ASSERT_TRUE(draggedButton); - BookmarkButton* targetButton = - [folderController buttonWithTitleEqualTo:@"4f2f"]; - ASSERT_TRUE(targetButton); - [folderController dragButton:draggedButton - to:[targetButton top] - copy:NO]; - // The button should have landed above "4f2f". - const std::string expected_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b " - "2f2f3b ] 2f3b ] 3b 4f:[ 4f1f:[ 4f1f1b 4f1f2b 4f1f3b ] 4f2f3b 4f2f:[ " - "4f2f1b 4f2f2b ] 4f3f:[ 4f3f1b 4f3f2b 4f3f3b ] ] 5b "); - EXPECT_EQ(expected_string, model_test_utils::ModelStringFromNode(root)); - - // Check button spacing. - [folderController validateMenuSpacing]; - // The window should not have gone away. - EXPECT_TRUE([bar_ folderController]); - // The subfolder should have gone away. - EXPECT_FALSE([folderController folderController]); -} - -TEST_F(BookmarkBarFolderControllerMenuTest, DragWindowResizing) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string - model_string("a b:[ b1 b2 b3 ] reallyReallyLongBookmarkName c "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - - // Pop up a folder menu. - BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"b"]; - ASSERT_TRUE(toFolder); - [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:toFolder]; - BookmarkBarFolderController* folderController = [bar_ folderController]; - EXPECT_TRUE(folderController); - NSWindow* toWindow = [folderController window]; - EXPECT_TRUE(toWindow); - CGFloat oldWidth = NSWidth([toWindow frame]); - // Drag the bookmark with a long name to the folder. - BookmarkButton* draggedButton = - [bar_ buttonWithTitleEqualTo:@"reallyReallyLongBookmarkName"]; - ASSERT_TRUE(draggedButton); - BookmarkButton* targetButton = - [folderController buttonWithTitleEqualTo:@"b1"]; - ASSERT_TRUE(targetButton); - [folderController dragButton:draggedButton - to:[targetButton center] - copy:NO]; - // Verify the model change. - const std::string - expected_string("a b:[ b1 reallyReallyLongBookmarkName b2 b3 ] c "); - EXPECT_EQ(expected_string, model_test_utils::ModelStringFromNode(root)); - // Verify the window grew. Just test a reasonable width gain. - CGFloat newWidth = NSWidth([toWindow frame]); - EXPECT_LT(oldWidth + 30.0, newWidth); -} - -TEST_F(BookmarkBarFolderControllerMenuTest, MoveRemoveAddButtons) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2b 2f3b ] 3b 4b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - - // Pop up a folder menu. - BookmarkButton* toFolder = [bar_ buttonWithTitleEqualTo:@"2f"]; - ASSERT_TRUE(toFolder); - [[toFolder target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:toFolder]; - BookmarkBarFolderController* folder = [bar_ folderController]; - EXPECT_TRUE(folder); - - // Remember how many buttons are showing. - NSArray* buttons = [folder buttons]; - NSUInteger oldDisplayedButtons = [buttons count]; - - // Move a button around a bit. - [folder moveButtonFromIndex:0 toIndex:2]; - EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:0] title]); - EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:1] title]); - EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:2] title]); - EXPECT_EQ(oldDisplayedButtons, [buttons count]); - [folder moveButtonFromIndex:2 toIndex:0]; - EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]); - EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:1] title]); - EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:2] title]); - EXPECT_EQ(oldDisplayedButtons, [buttons count]); - - // Add a couple of buttons. - const BookmarkNode* node = root->GetChild(2); // Purloin an existing node. - [folder addButtonForNode:node atIndex:0]; - EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:0] title]); - EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:1] title]); - EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:2] title]); - EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:3] title]); - EXPECT_EQ(oldDisplayedButtons + 1, [buttons count]); - node = root->GetChild(3); - [folder addButtonForNode:node atIndex:-1]; - EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:0] title]); - EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:1] title]); - EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:2] title]); - EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:3] title]); - EXPECT_NSEQ(@"4b", [[buttons objectAtIndex:4] title]); - EXPECT_EQ(oldDisplayedButtons + 2, [buttons count]); - - // Remove a couple of buttons. - [folder removeButton:4 animate:NO]; - [folder removeButton:1 animate:NO]; - EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:0] title]); - EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:1] title]); - EXPECT_NSEQ(@"2f3b", [[buttons objectAtIndex:2] title]); - EXPECT_EQ(oldDisplayedButtons, [buttons count]); - - // Check button spacing. - [folder validateMenuSpacing]; -} - -TEST_F(BookmarkBarFolderControllerMenuTest, ControllerForNode) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - - // Find the main bar controller. - const void* expectedController = bar_; - const void* actualController = [bar_ controllerForNode:root]; - EXPECT_EQ(expectedController, actualController); - - // Pop up the folder menu. - BookmarkButton* targetFolder = [bar_ buttonWithTitleEqualTo:@"2f"]; - ASSERT_TRUE(targetFolder); - [[targetFolder target] - performSelector:@selector(openBookmarkFolderFromButton:) - withObject:targetFolder]; - BookmarkBarFolderController* folder = [bar_ folderController]; - EXPECT_TRUE(folder); - - // Find the folder controller using the folder controller. - const BookmarkNode* targetNode = root->GetChild(1); - expectedController = folder; - actualController = [bar_ controllerForNode:targetNode]; - EXPECT_EQ(expectedController, actualController); - - // Find the folder controller from the bar. - actualController = [folder controllerForNode:targetNode]; - EXPECT_EQ(expectedController, actualController); -} - -TEST_F(BookmarkBarFolderControllerMenuTest, MenuSizingAndScrollArrows) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2b 3b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - - const BookmarkNode* parent = model.GetBookmarkBarNode(); - const BookmarkNode* folder = model.AddGroup(parent, - parent->GetChildCount(), - ASCIIToUTF16("BIG")); - - // Pop open the new folder window and verify it has one (empty) item. - BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"BIG"]; - [[button target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:button]; - BookmarkBarFolderController* folderController = [bar_ folderController]; - EXPECT_TRUE(folderController); - NSWindow* folderMenu = [folderController window]; - EXPECT_TRUE(folderMenu); - CGFloat expectedHeight = (CGFloat)bookmarks::kBookmarkButtonHeight + - (2*bookmarks::kBookmarkVerticalPadding); - NSRect menuFrame = [folderMenu frame]; - CGFloat menuHeight = NSHeight(menuFrame); - EXPECT_CGFLOAT_EQ(expectedHeight, menuHeight); - EXPECT_FALSE([folderController scrollable]); - - // Now add a real bookmark and reopen. - model.AddURL(folder, folder->GetChildCount(), ASCIIToUTF16("a"), - GURL("http://a.com/")); - folderController = [bar_ folderController]; - EXPECT_TRUE(folderController); - folderMenu = [folderController window]; - EXPECT_TRUE(folderMenu); - menuFrame = [folderMenu frame]; - menuHeight = NSHeight(menuFrame); - EXPECT_CGFLOAT_EQ(expectedHeight, menuHeight); - CGFloat menuWidth = NSWidth(menuFrame); - button = [folderController buttonWithTitleEqualTo:@"a"]; - CGFloat buttonWidth = NSWidth([button frame]); - CGFloat expectedWidth = - buttonWidth + (2 * bookmarks::kBookmarkSubMenuHorizontalPadding); - EXPECT_CGFLOAT_EQ(expectedWidth, menuWidth); - - // Add a wider bookmark and make sure the button widths match. - model.AddURL(folder, folder->GetChildCount(), - ASCIIToUTF16("A really, really long name"), - GURL("http://www.google.com/a")); - EXPECT_LT(menuWidth, NSWidth([folderMenu frame])); - EXPECT_LT(buttonWidth, NSWidth([button frame])); - buttonWidth = NSWidth([button frame]); - BookmarkButton* buttonB = - [folderController buttonWithTitleEqualTo:@"A really, really long name"]; - EXPECT_TRUE(buttonB); - CGFloat buttonWidthB = NSWidth([buttonB frame]); - EXPECT_CGFLOAT_EQ(buttonWidth, buttonWidthB); - // Add a bunch of bookmarks until the window grows no more, then check for - // a scroll down arrow. - CGFloat oldMenuHeight = 0.0; // It just has to be different for first run. - menuHeight = NSHeight([folderMenu frame]); - NSUInteger tripWire = 0; // Prevent a runaway. - while (![folderController scrollable] && ++tripWire < 100) { - model.AddURL(folder, folder->GetChildCount(), ASCIIToUTF16("B"), - GURL("http://b.com/")); - oldMenuHeight = menuHeight; - menuHeight = NSHeight([folderMenu frame]); - } - EXPECT_TRUE([folderController scrollable]); - EXPECT_TRUE([folderController canScrollUp]); - - // Remove one bookmark and make sure the scroll down arrow has been removed. - // We'll remove the really long node so we can see if the buttons get resized. - menuWidth = NSWidth([folderMenu frame]); - buttonWidth = NSWidth([button frame]); - model.Remove(folder, 1); - EXPECT_FALSE([folderController scrollable]); - EXPECT_FALSE([folderController canScrollUp]); - EXPECT_FALSE([folderController canScrollDown]); - - // Check the size. It should have reduced. - EXPECT_GT(menuWidth, NSWidth([folderMenu frame])); - EXPECT_GT(buttonWidth, NSWidth([button frame])); - - // Check button spacing. - [folderController validateMenuSpacing]; -} - -// See http://crbug.com/46101 -TEST_F(BookmarkBarFolderControllerMenuTest, HoverThenDeleteBookmark) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const BookmarkNode* folder = model.AddGroup(root, - root->GetChildCount(), - ASCIIToUTF16("BIG")); - for (int i = 0; i < kLotsOfNodesCount; i++) - model.AddURL(folder, folder->GetChildCount(), ASCIIToUTF16("kid"), - GURL("http://kid.com/smile")); - - // Pop open the new folder window and hover one of its kids. - BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"BIG"]; - [[button target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:button]; - BookmarkBarFolderController* bbfc = [bar_ folderController]; - NSArray* buttons = [bbfc buttons]; - - // Hover over a button and verify that it is now known. - button = [buttons objectAtIndex:3]; - BookmarkButton* buttonThatMouseIsIn = [bbfc buttonThatMouseIsIn]; - EXPECT_FALSE(buttonThatMouseIsIn); - [bbfc mouseEnteredButton:button event:nil]; - buttonThatMouseIsIn = [bbfc buttonThatMouseIsIn]; - EXPECT_EQ(button, buttonThatMouseIsIn); - - // Delete the bookmark and verify that it is now not known. - model.Remove(folder, 3); - buttonThatMouseIsIn = [bbfc buttonThatMouseIsIn]; - EXPECT_FALSE(buttonThatMouseIsIn); -} - -// Just like a BookmarkBarFolderController but intercedes when providing -// pasteboard drag data. -@interface BookmarkBarFolderControllerDragData : BookmarkBarFolderController { - const BookmarkNode* dragDataNode_; // Weak -} -- (void)setDragDataNode:(const BookmarkNode*)node; -@end - -@implementation BookmarkBarFolderControllerDragData - -- (id)initWithParentButton:(BookmarkButton*)button - parentController:(BookmarkBarFolderController*)parentController - barController:(BookmarkBarController*)barController { - if ((self = [super initWithParentButton:button - parentController:parentController - barController:barController])) { - dragDataNode_ = NULL; - } - return self; -} - -- (void)setDragDataNode:(const BookmarkNode*)node { - dragDataNode_ = node; -} - -- (std::vector<const BookmarkNode*>)retrieveBookmarkNodeData { - std::vector<const BookmarkNode*> dragDataNodes; - if(dragDataNode_) { - dragDataNodes.push_back(dragDataNode_); - } - return dragDataNodes; -} - -@end - -TEST_F(BookmarkBarFolderControllerMenuTest, DragBookmarkData) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " - "2f3b ] 3b 4b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - const BookmarkNode* other = model.other_node(); - const std::string other_string("O1b O2b O3f:[ O3f1b O3f2f ] " - "O4f:[ O4f1b O4f2f ] 05b "); - model_test_utils::AddNodesFromModelString(model, other, other_string); - - // Validate initial model. - std::string actual = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actual); - actual = model_test_utils::ModelStringFromNode(other); - EXPECT_EQ(other_string, actual); - - // Pop open a folder. - BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"]; - scoped_nsobject<BookmarkBarFolderControllerDragData> folderController; - folderController.reset([[BookmarkBarFolderControllerDragData alloc] - initWithParentButton:button - parentController:nil - barController:bar_]); - BookmarkButton* targetButton = - [folderController buttonWithTitleEqualTo:@"2f1b"]; - ASSERT_TRUE(targetButton); - - // Gen up some dragging data. - const BookmarkNode* newNode = other->GetChild(2); - [folderController setDragDataNode:newNode]; - scoped_nsobject<FakedDragInfo> dragInfo([[FakedDragInfo alloc] init]); - [dragInfo setDropLocation:[targetButton top]]; - [folderController dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()]; - - // Verify the model. - const std::string expected("1b 2f:[ O3f:[ O3f1b O3f2f ] 2f1b 2f2f:[ 2f2f1b " - "2f2f2b 2f2f3b ] 2f3b ] 3b 4b "); - actual = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(expected, actual); - - // Now drag over a folder button. - targetButton = [folderController buttonWithTitleEqualTo:@"2f2f"]; - ASSERT_TRUE(targetButton); - newNode = other->GetChild(2); // Should be O4f. - EXPECT_EQ(newNode->GetTitle(), ASCIIToUTF16("O4f")); - [folderController setDragDataNode:newNode]; - [dragInfo setDropLocation:[targetButton center]]; - [folderController dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()]; - - // Verify the model. - const std::string expectedA("1b 2f:[ O3f:[ O3f1b O3f2f ] 2f1b 2f2f:[ " - "2f2f1b 2f2f2b 2f2f3b O4f:[ O4f1b O4f2f ] ] " - "2f3b ] 3b 4b "); - actual = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(expectedA, actual); - - // Check button spacing. - [folderController validateMenuSpacing]; -} - -TEST_F(BookmarkBarFolderControllerMenuTest, DragBookmarkDataToTrash) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " - "2f3b ] 3b 4b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actual = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actual); - - const BookmarkNode* folderNode = root->GetChild(1); - int oldFolderChildCount = folderNode->GetChildCount(); - - // Pop open a folder. - BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"]; - scoped_nsobject<BookmarkBarFolderControllerDragData> folderController; - folderController.reset([[BookmarkBarFolderControllerDragData alloc] - initWithParentButton:button - parentController:nil - barController:bar_]); - - // Drag a button to the trash. - BookmarkButton* buttonToDelete = - [folderController buttonWithTitleEqualTo:@"2f1b"]; - ASSERT_TRUE(buttonToDelete); - EXPECT_TRUE([folderController canDragBookmarkButtonToTrash:buttonToDelete]); - [folderController didDragBookmarkToTrash:buttonToDelete]; - - // There should be one less button in the folder. - int newFolderChildCount = folderNode->GetChildCount(); - EXPECT_EQ(oldFolderChildCount - 1, newFolderChildCount); - // Verify the model. - const std::string expected("1b 2f:[ 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " - "2f3b ] 3b 4b "); - actual = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(expected, actual); - - // Check button spacing. - [folderController validateMenuSpacing]; -} - -TEST_F(BookmarkBarFolderControllerMenuTest, AddURLs) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " - "2f3b ] 3b 4b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actual = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actual); - - // Pop open a folder. - BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"]; - [[button target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:button]; - BookmarkBarFolderController* folderController = [bar_ folderController]; - EXPECT_TRUE(folderController); - NSArray* buttons = [folderController buttons]; - EXPECT_TRUE(buttons); - - // Remember how many buttons are showing. - int oldDisplayedButtons = [buttons count]; - - BookmarkButton* targetButton = - [folderController buttonWithTitleEqualTo:@"2f1b"]; - ASSERT_TRUE(targetButton); - - NSArray* urls = [NSArray arrayWithObjects: @"http://www.a.com/", - @"http://www.b.com/", nil]; - NSArray* titles = [NSArray arrayWithObjects: @"SiteA", @"SiteB", nil]; - [folderController addURLs:urls withTitles:titles at:[targetButton top]]; - - // There should two more buttons in the folder. - int newDisplayedButtons = [buttons count]; - EXPECT_EQ(oldDisplayedButtons + 2, newDisplayedButtons); - // Verify the model. - const std::string expected("1b 2f:[ SiteA SiteB 2f1b 2f2f:[ 2f2f1b 2f2f2b " - "2f2f3b ] 2f3b ] 3b 4b "); - actual = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(expected, actual); - - // Check button spacing. - [folderController validateMenuSpacing]; -} - -TEST_F(BookmarkBarFolderControllerMenuTest, DropPositionIndicator) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " - "2f3b ] 3b 4b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actual = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actual); - - // Pop open the folder. - BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"2f"]; - [[button target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:button]; - BookmarkBarFolderController* folder = [bar_ folderController]; - EXPECT_TRUE(folder); - - // Test a series of points starting at the top of the folder. - const CGFloat yOffset = 0.5 * bookmarks::kBookmarkVerticalPadding; - BookmarkButton* targetButton = [folder buttonWithTitleEqualTo:@"2f1b"]; - ASSERT_TRUE(targetButton); - NSPoint targetPoint = [targetButton top]; - CGFloat pos = [folder indicatorPosForDragToPoint:targetPoint]; - EXPECT_CGFLOAT_EQ(targetPoint.y + yOffset, pos); - pos = [folder indicatorPosForDragToPoint:[targetButton bottom]]; - targetButton = [folder buttonWithTitleEqualTo:@"2f2f"]; - EXPECT_CGFLOAT_EQ([targetButton top].y + yOffset, pos); - pos = [folder indicatorPosForDragToPoint:NSMakePoint(10,0)]; - targetButton = [folder buttonWithTitleEqualTo:@"2f3b"]; - EXPECT_CGFLOAT_EQ([targetButton bottom].y - yOffset, pos); -} - -@interface BookmarkBarControllerNoDelete : BookmarkBarController -- (IBAction)deleteBookmark:(id)sender; -@end - -@implementation BookmarkBarControllerNoDelete -- (IBAction)deleteBookmark:(id)sender { - // NOP -} -@end - -class BookmarkBarFolderControllerClosingTest : public - BookmarkBarFolderControllerMenuTest { - public: - BookmarkBarFolderControllerClosingTest() { - bar_.reset([[BookmarkBarControllerNoDelete alloc] - initWithBrowser:helper_.browser() - initialWidth:NSWidth([parent_view_ frame]) - delegate:nil - resizeDelegate:resizeDelegate_.get()]); - InstallAndToggleBar(bar_.get()); - } -}; - -TEST_F(BookmarkBarFolderControllerClosingTest, DeleteClosesFolder) { - BookmarkModel& model(*helper_.profile()->GetBookmarkModel()); - const BookmarkNode* root = model.GetBookmarkBarNode(); - const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b ] " - "2f3b ] 3b "); - model_test_utils::AddNodesFromModelString(model, root, model_string); - - // Validate initial model. - std::string actualModelString = model_test_utils::ModelStringFromNode(root); - EXPECT_EQ(model_string, actualModelString); - - // Open the folder menu and submenu. - BookmarkButton* target = [bar_ buttonWithTitleEqualTo:@"2f"]; - ASSERT_TRUE(target); - [[target target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:target]; - BookmarkBarFolderController* folder = [bar_ folderController]; - EXPECT_TRUE(folder); - BookmarkButton* subTarget = [folder buttonWithTitleEqualTo:@"2f2f"]; - ASSERT_TRUE(subTarget); - [[subTarget target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:subTarget]; - BookmarkBarFolderController* subFolder = [folder folderController]; - EXPECT_TRUE(subFolder); - - // Delete the folder node and verify the window closed down by looking - // for its controller again. - [folder deleteBookmark:folder]; - EXPECT_FALSE([folder folderController]); -} - -// TODO(jrg): draggingEntered: and draggingExited: trigger timers so -// they are hard to test. Factor out "fire timers" into routines -// which can be overridden to fire immediately to make behavior -// confirmable. -// There is a similar problem with mouseEnteredButton: and -// mouseExitedButton:. diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_hover_state.h b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_hover_state.h deleted file mode 100644 index 21d0658..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_hover_state.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#include "base/scoped_nsobject.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button.h" - -// Hover state machine. Encapsulates the hover state for -// BookmarkBarFolderController. -// A strict call order is implied with these calls. It is ONLY valid to make -// the following state transitions: -// From: To: Via: -// closed opening scheduleOpen...: -// opening closed cancelPendingOpen...: or -// open scheduleOpen...: completes. -// open closing scheduleClose...: -// closing open cancelPendingClose...: or -// closed scheduleClose...: completes. -// -@interface BookmarkBarFolderHoverState : NSObject { - @private - // Enumeration of the valid states that the |hoverButton_| member can be in. - // Because the opening and closing of hover views can be done asyncronously - // there are periods where the hover state is in transtion between open and - // closed. During those times of transition the opening or closing operation - // can be cancelled. We serialize the opening and closing of the - // |hoverButton_| using this state information. This serialization is to - // avoid race conditions where one hover button is being opened while another - // is closing. - enum HoverState { - kHoverStateClosed = 0, - kHoverStateOpening = 1, - kHoverStateOpen = 2, - kHoverStateClosing = 3 - }; - - // Like normal menus, hovering over a folder button causes it to - // open. This variable is set when a hover is initiated (but has - // not necessarily fired yet). - scoped_nsobject<BookmarkButton> hoverButton_; - - // We model hover state as a state machine with specific allowable - // transitions. |hoverState_| is the state of this machine at any - // given time. - HoverState hoverState_; -} - -// Designated initializer. -- (id)init; - -// The BookmarkBarFolderHoverState decides when it is appropriate to hide -// and show the button that the BookmarkBarFolderController drags over. -- (NSDragOperation)draggingEnteredButton:(BookmarkButton*)button; - -// The BookmarkBarFolderHoverState decides the fate of the hover button -// when the BookmarkBarFolderController's view is exited. -- (void)draggingExited; - -@end - -// Exposing these for unit testing purposes. They are used privately in the -// implementation as well. -@interface BookmarkBarFolderHoverState(PrivateAPI) -// State change APIs. -- (void)scheduleCloseBookmarkFolderOnHoverButton; -- (void)cancelPendingCloseBookmarkFolderOnHoverButton; -- (void)scheduleOpenBookmarkFolderOnHoverButton:(BookmarkButton*)hoverButton; -- (void)cancelPendingOpenBookmarkFolderOnHoverButton; -@end - -// Exposing these for unit testing purposes. They are used only in tests. -@interface BookmarkBarFolderHoverState(TestingAPI) -// Accessors and setters for button and hover state. -- (BookmarkButton*)hoverButton; -- (HoverState)hoverState; -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_hover_state.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_hover_state.mm deleted file mode 100644 index 2c6feee..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_hover_state.mm +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_hover_state.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" - -@interface BookmarkBarFolderHoverState(Private) -- (void)setHoverState:(HoverState)state; -- (void)closeBookmarkFolderOnHoverButton:(BookmarkButton*)button; -- (void)openBookmarkFolderOnHoverButton:(BookmarkButton*)button; -@end - -@implementation BookmarkBarFolderHoverState - -- (id)init { - if ((self = [super init])) { - hoverState_ = kHoverStateClosed; - } - return self; -} - -- (NSDragOperation)draggingEnteredButton:(BookmarkButton*)button { - if ([button isFolder]) { - if (hoverButton_ == button) { - // CASE A: hoverButton_ == button implies we've dragged over - // the same folder so no need to open or close anything new. - } else if (hoverButton_ && - hoverButton_ != button) { - // CASE B: we have a hoverButton_ but it is different from the new button. - // This implies we've dragged over a new folder, so we'll close the old - // and open the new. - // Note that we only schedule the open or close if we have no other tasks - // currently pending. - - if (hoverState_ == kHoverStateOpen) { - // Close the old. - [self scheduleCloseBookmarkFolderOnHoverButton]; - } else if (hoverState_ == kHoverStateClosed) { - // Open the new. - [self scheduleOpenBookmarkFolderOnHoverButton:button]; - } - } else if (!hoverButton_) { - // CASE C: we don't have a current hoverButton_ but we have dragged onto - // a new folder so we open the new one. - [self scheduleOpenBookmarkFolderOnHoverButton:button]; - } - } else if (!button) { - if (hoverButton_) { - // CASE D: We have a hoverButton_ but we've moved onto an area that - // requires no hover. We close the hoverButton_ in this case. This - // means cancelling if the open is pending (i.e. |kHoverStateOpening|) - // or closing if we don't alrealy have once in progress. - - // Intiate close only if we have not already done so. - if (hoverState_ == kHoverStateOpening) { - // Cancel the pending open. - [self cancelPendingOpenBookmarkFolderOnHoverButton]; - } else if (hoverState_ != kHoverStateClosing) { - // Schedule the close. - [self scheduleCloseBookmarkFolderOnHoverButton]; - } - } else { - // CASE E: We have neither a hoverButton_ nor a new button that requires - // a hover. In this case we do nothing. - } - } - - return NSDragOperationMove; -} - -- (void)draggingExited { - if (hoverButton_) { - if (hoverState_ == kHoverStateOpening) { - [self cancelPendingOpenBookmarkFolderOnHoverButton]; - } else if (hoverState_ == kHoverStateClosing) { - [self cancelPendingCloseBookmarkFolderOnHoverButton]; - } - } -} - -// Schedule close of hover button. Transition to kHoverStateClosing state. -- (void)scheduleCloseBookmarkFolderOnHoverButton { - DCHECK(hoverButton_); - [self setHoverState:kHoverStateClosing]; - [self performSelector:@selector(closeBookmarkFolderOnHoverButton:) - withObject:hoverButton_ - afterDelay:bookmarks::kDragHoverCloseDelay]; -} - -// Cancel pending hover close. Transition to kHoverStateOpen state. -- (void)cancelPendingCloseBookmarkFolderOnHoverButton { - [self setHoverState:kHoverStateOpen]; - [NSObject - cancelPreviousPerformRequestsWithTarget:self - selector:@selector(closeBookmarkFolderOnHoverButton:) - object:hoverButton_]; -} - -// Schedule open of hover button. Transition to kHoverStateOpening state. -- (void)scheduleOpenBookmarkFolderOnHoverButton:(BookmarkButton*)button { - DCHECK(button); - hoverButton_.reset([button retain]); - [self setHoverState:kHoverStateOpening]; - [self performSelector:@selector(openBookmarkFolderOnHoverButton:) - withObject:hoverButton_ - afterDelay:bookmarks::kDragHoverOpenDelay]; -} - -// Cancel pending hover open. Transition to kHoverStateClosed state. -- (void)cancelPendingOpenBookmarkFolderOnHoverButton { - [self setHoverState:kHoverStateClosed]; - [NSObject - cancelPreviousPerformRequestsWithTarget:self - selector:@selector(openBookmarkFolderOnHoverButton:) - object:hoverButton_]; - hoverButton_.reset(); -} - -// Hover button accessor. For testing only. -- (BookmarkButton*)hoverButton { - return hoverButton_; -} - -// Hover state accessor. For testing only. -- (HoverState)hoverState { - return hoverState_; -} - -// This method encodes the rules of our |hoverButton_| state machine. Only -// specific state transitions are allowable (encoded in the DCHECK). -// Note that there is no state for simultaneously opening and closing. A -// pending open must complete before scheduling a close, and vice versa. And -// it is not possible to make a transition directly from open to closed, and -// vice versa. -- (void)setHoverState:(HoverState)state { - DCHECK( - (hoverState_ == kHoverStateClosed && state == kHoverStateOpening) || - (hoverState_ == kHoverStateOpening && state == kHoverStateClosed) || - (hoverState_ == kHoverStateOpening && state == kHoverStateOpen) || - (hoverState_ == kHoverStateOpen && state == kHoverStateClosing) || - (hoverState_ == kHoverStateClosing && state == kHoverStateOpen) || - (hoverState_ == kHoverStateClosing && state == kHoverStateClosed) - ) << "bad transition: old = " << hoverState_ << " new = " << state; - - hoverState_ = state; -} - -// Called after a delay to close a previously hover-opened folder. -// Note: this method is not meant to be invoked directly, only through -// a delayed call to |scheduleCloseBookmarkFolderOnHoverButton:|. -- (void)closeBookmarkFolderOnHoverButton:(BookmarkButton*)button { - [NSObject - cancelPreviousPerformRequestsWithTarget:self - selector:@selector(closeBookmarkFolderOnHoverButton:) - object:hoverButton_]; - [self setHoverState:kHoverStateClosed]; - [[button target] closeBookmarkFolder:button]; - hoverButton_.reset(); -} - -// Called after a delay to open a new hover folder. -// Note: this method is not meant to be invoked directly, only through -// a delayed call to |scheduleOpenBookmarkFolderOnHoverButton:|. -- (void)openBookmarkFolderOnHoverButton:(BookmarkButton*)button { - [self setHoverState:kHoverStateOpen]; - [[button target] performSelector:@selector(openBookmarkFolderFromButton:) - withObject:button]; -} - -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_hover_state_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_hover_state_unittest.mm deleted file mode 100644 index bcd1293..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_hover_state_unittest.mm +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#include "base/message_loop.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_hover_state.h" -#import "chrome/browser/cocoa/browser_test_helper.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" - -namespace { - -typedef CocoaTest BookmarkBarFolderHoverStateTest; - -// Hover state machine interface. -// A strict call order is implied with these calls. It is ONLY valid to make -// these specific state transitions. -TEST(BookmarkBarFolderHoverStateTest, HoverState) { - BrowserTestHelper helper; - scoped_nsobject<BookmarkBarFolderHoverState> bbfhs; - bbfhs.reset([[BookmarkBarFolderHoverState alloc] init]); - - // Initial state. - EXPECT_FALSE([bbfhs hoverButton]); - ASSERT_EQ(kHoverStateClosed, [bbfhs hoverState]); - - scoped_nsobject<BookmarkButton> button; - button.reset([[BookmarkButton alloc] initWithFrame:NSMakeRect(0, 0, 20, 20)]); - - // Test transition from closed to opening. - ASSERT_EQ(kHoverStateClosed, [bbfhs hoverState]); - [bbfhs scheduleOpenBookmarkFolderOnHoverButton:button]; - ASSERT_EQ(kHoverStateOpening, [bbfhs hoverState]); - - // Test transition from opening to closed (aka cancel open). - [bbfhs cancelPendingOpenBookmarkFolderOnHoverButton]; - ASSERT_EQ(kHoverStateClosed, [bbfhs hoverState]); - ASSERT_EQ(nil, [bbfhs hoverButton]); - - // Test transition from closed to opening. - ASSERT_EQ(kHoverStateClosed, [bbfhs hoverState]); - [bbfhs scheduleOpenBookmarkFolderOnHoverButton:button]; - ASSERT_EQ(kHoverStateOpening, [bbfhs hoverState]); - - // Test transition from opening to opened. - MessageLoop::current()->PostDelayedTask( - FROM_HERE, - new MessageLoop::QuitTask, - bookmarks::kDragHoverOpenDelay * 1000.0 * 1.5); - MessageLoop::current()->Run(); - ASSERT_EQ(kHoverStateOpen, [bbfhs hoverState]); - ASSERT_EQ(button, [bbfhs hoverButton]); - - // Test transition from opening to opened. - [bbfhs scheduleCloseBookmarkFolderOnHoverButton]; - ASSERT_EQ(kHoverStateClosing, [bbfhs hoverState]); - - // Test transition from closing to open (aka cancel close). - [bbfhs cancelPendingCloseBookmarkFolderOnHoverButton]; - ASSERT_EQ(kHoverStateOpen, [bbfhs hoverState]); - ASSERT_EQ(button, [bbfhs hoverButton]); - - // Test transition from closing to closed. - [bbfhs scheduleCloseBookmarkFolderOnHoverButton]; - ASSERT_EQ(kHoverStateClosing, [bbfhs hoverState]); - MessageLoop::current()->PostDelayedTask( - FROM_HERE, - new MessageLoop::QuitTask, - bookmarks::kDragHoverCloseDelay * 1000.0 * 1.5); - MessageLoop::current()->Run(); - ASSERT_EQ(kHoverStateClosed, [bbfhs hoverState]); - ASSERT_EQ(nil, [bbfhs hoverButton]); -} - -} // namespace diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_view.h b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_view.h deleted file mode 100644 index 8f60b8e..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_view.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -@protocol BookmarkButtonControllerProtocol; -@class BookmarkBarFolderController; - -// Main content view for a bookmark bar folder "menu" window. This is -// logically similar to a BookmarkBarView but is oriented vertically. -@interface BookmarkBarFolderView : NSView { - @private - BOOL inDrag_; // Are we in the middle of a drag? - BOOL dropIndicatorShown_; - CGFloat dropIndicatorPosition_; // y position - // The following |controller_| is weak; used for testing only. See the imple- - // mentation comment for - (id<BookmarkButtonControllerProtocol>)controller. - BookmarkBarFolderController* controller_; -} -// Return the controller that owns this view. -- (id<BookmarkButtonControllerProtocol>)controller; -@end - -@interface BookmarkBarFolderView() // TestingOrInternalAPI -@property (assign) BOOL dropIndicatorShown; -@property (readonly) CGFloat dropIndicatorPosition; -- (void)setController:(id)controller; -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_view.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_view.mm deleted file mode 100644 index 7cf18af..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_view.mm +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_view.h" - -#include "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_folder_target.h" -#include "chrome/browser/metrics/user_metrics.h" -#import "third_party/mozilla/NSPasteboard+Utils.h" - -@implementation BookmarkBarFolderView - -@synthesize dropIndicatorShown = dropIndicatorShown_; -@synthesize dropIndicatorPosition = dropIndicatorPosition_; - -- (void)awakeFromNib { - NSArray* types = [NSArray arrayWithObjects: - NSStringPboardType, - NSHTMLPboardType, - NSURLPboardType, - kBookmarkButtonDragType, - kBookmarkDictionaryListPboardType, - nil]; - [self registerForDraggedTypes:types]; -} - -- (void)dealloc { - [self unregisterDraggedTypes]; - [super dealloc]; -} - -- (id<BookmarkButtonControllerProtocol>)controller { - // When needed for testing, set the local data member |controller_| to - // the test controller. - return controller_ ? controller_ : [[self window] windowController]; -} - -- (void)setController:(id)controller { - controller_ = controller; -} - -- (void)drawRect:(NSRect)rect { - // TODO(jrg): copied from bookmark_bar_view but orientation changed. - // Code dup sucks but I'm not sure I can take 16 lines and make it - // generic for horiz vs vertical while keeping things simple. - // TODO(jrg): when throwing it all away and using animations, try - // hard to make a common routine for both. - // http://crbug.com/35966, http://crbug.com/35968 - - // Draw the bookmark-button-dragging drop indicator if necessary. - if (dropIndicatorShown_) { - const CGFloat kBarHeight = 1; - const CGFloat kBarHorizPad = 4; - const CGFloat kBarOpacity = 0.85; - - NSRect uglyBlackBar = - NSMakeRect(kBarHorizPad, dropIndicatorPosition_, - NSWidth([self bounds]) - 2*kBarHorizPad, - kBarHeight); - NSColor* uglyBlackBarColor = [NSColor blackColor]; - [[uglyBlackBarColor colorWithAlphaComponent:kBarOpacity] setFill]; - [[NSBezierPath bezierPathWithRect:uglyBlackBar] fill]; - } -} - -// TODO(mrossetti,jrg): Identical to -[BookmarkBarView -// dragClipboardContainsBookmarks]. http://crbug.com/35966 -// Shim function to assist in unit testing. -- (BOOL)dragClipboardContainsBookmarks { - return bookmark_pasteboard_helper_mac::DragClipboardContainsBookmarks(); -} - -// Virtually identical to [BookmarkBarView draggingEntered:]. -// TODO(jrg): find a way to share code. Lack of multiple inheritance -// makes things more of a pain but there should be no excuse for laziness. -// http://crbug.com/35966 -- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info { - inDrag_ = YES; - if ([[info draggingPasteboard] dataForType:kBookmarkButtonDragType] || - [self dragClipboardContainsBookmarks] || - [[info draggingPasteboard] containsURLData]) { - // Find the position of the drop indicator. - BOOL showIt = [[self controller] - shouldShowIndicatorShownForPoint:[info draggingLocation]]; - if (!showIt) { - if (dropIndicatorShown_) { - dropIndicatorShown_ = NO; - [self setNeedsDisplay:YES]; - } - } else { - CGFloat y = - [[self controller] - indicatorPosForDragToPoint:[info draggingLocation]]; - - // Need an update if the indicator wasn't previously shown or if it has - // moved. - if (!dropIndicatorShown_ || dropIndicatorPosition_ != y) { - dropIndicatorShown_ = YES; - dropIndicatorPosition_ = y; - [self setNeedsDisplay:YES]; - } - } - - [[self controller] draggingEntered:info]; // allow hover-open to work - return [info draggingSource] ? NSDragOperationMove : NSDragOperationCopy; - } - return NSDragOperationNone; -} - -- (void)draggingExited:(id<NSDraggingInfo>)info { - [[self controller] draggingExited:info]; - - // Regardless of the type of dragging which ended, we need to get rid of the - // drop indicator if one was shown. - if (dropIndicatorShown_) { - dropIndicatorShown_ = NO; - [self setNeedsDisplay:YES]; - } -} - -- (void)draggingEnded:(id<NSDraggingInfo>)info { - // Awkwardness since views open and close out from under us. - if (inDrag_) { - inDrag_ = NO; - } - - [self draggingExited:info]; -} - -- (BOOL)wantsPeriodicDraggingUpdates { - // TODO(jrg): This should probably return |YES| and the controller should - // slide the existing bookmark buttons interactively to the side to make - // room for the about-to-be-dropped bookmark. - // http://crbug.com/35968 - return NO; -} - -- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info { - // For now it's the same as draggingEntered:. - // TODO(jrg): once we return YES for wantsPeriodicDraggingUpdates, - // this should ping the [self controller] to perform animations. - // http://crbug.com/35968 - return [self draggingEntered:info]; -} - -- (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info { - return YES; -} - -// This code is practically identical to the same function in BookmarkBarView -// with the only difference being how the controller is retrieved. -// TODO(mrossetti,jrg): http://crbug.com/35966 -// Implement NSDraggingDestination protocol method -// performDragOperation: for URLs. -- (BOOL)performDragOperationForURL:(id<NSDraggingInfo>)info { - NSPasteboard* pboard = [info draggingPasteboard]; - DCHECK([pboard containsURLData]); - - NSArray* urls = nil; - NSArray* titles = nil; - [pboard getURLs:&urls andTitles:&titles convertingFilenames:YES]; - - return [[self controller] addURLs:urls - withTitles:titles - at:[info draggingLocation]]; -} - -// This code is practically identical to the same function in BookmarkBarView -// with the only difference being how the controller is retrieved. -// http://crbug.com/35966 -// Implement NSDraggingDestination protocol method -// performDragOperation: for bookmark buttons. -- (BOOL)performDragOperationForBookmarkButton:(id<NSDraggingInfo>)info { - BOOL doDrag = NO; - NSData* data = [[info draggingPasteboard] - dataForType:kBookmarkButtonDragType]; - // [info draggingSource] is nil if not the same application. - if (data && [info draggingSource]) { - BookmarkButton* button = nil; - [data getBytes:&button length:sizeof(button)]; - BOOL copy = !([info draggingSourceOperationMask] & NSDragOperationMove); - doDrag = [[self controller] dragButton:button - to:[info draggingLocation] - copy:copy]; - UserMetrics::RecordAction(UserMetricsAction("BookmarkBarFolder_DragEnd")); - } - return doDrag; -} - -- (BOOL)performDragOperation:(id<NSDraggingInfo>)info { - if ([[self controller] dragBookmarkData:info]) - return YES; - NSPasteboard* pboard = [info draggingPasteboard]; - if ([pboard dataForType:kBookmarkButtonDragType] && - [self performDragOperationForBookmarkButton:info]) - return YES; - if ([pboard containsURLData] && [self performDragOperationForURL:info]) - return YES; - return NO; -} - -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_view_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_view_unittest.mm deleted file mode 100644 index 9abe55a..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_view_unittest.mm +++ /dev/null @@ -1,211 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/scoped_nsobject.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_view.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_folder_target.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" -#import "chrome/browser/cocoa/url_drop_target.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" -#import "third_party/mozilla/NSPasteboard+Utils.h" - -namespace { - const CGFloat kFakeIndicatorPos = 7.0; -}; - -// Fake DraggingInfo, fake BookmarkBarController, fake NSPasteboard... -@interface FakeDraggingInfo : NSObject { - @public - BOOL dragButtonToPong_; - BOOL dragURLsPong_; - BOOL dragBookmarkDataPong_; - BOOL dropIndicatorShown_; - BOOL draggingEnteredCalled_; - // Only mock one type of drag data at a time. - NSString* dragDataType_; -} -@property (readwrite) BOOL dropIndicatorShown; -@property (readwrite) BOOL draggingEnteredCalled; -@property (copy) NSString* dragDataType; -@end - -@implementation FakeDraggingInfo - -@synthesize dropIndicatorShown = dropIndicatorShown_; -@synthesize draggingEnteredCalled = draggingEnteredCalled_; -@synthesize dragDataType = dragDataType_; - -- (id)init { - if ((self = [super init])) { - dropIndicatorShown_ = YES; - } - return self; -} - -- (void)dealloc { - [dragDataType_ release]; - [super dealloc]; -} - -- (void)reset { - [dragDataType_ release]; - dragDataType_ = nil; - dragButtonToPong_ = NO; - dragURLsPong_ = NO; - dragBookmarkDataPong_ = NO; - dropIndicatorShown_ = YES; - draggingEnteredCalled_ = NO; -} - -// NSDragInfo mocking functions. - -- (id)draggingPasteboard { - return self; -} - -// So we can look local. -- (id)draggingSource { - return self; -} - -- (NSDragOperation)draggingSourceOperationMask { - return NSDragOperationCopy | NSDragOperationMove; -} - -- (NSPoint)draggingLocation { - return NSMakePoint(10, 10); -} - -// NSPasteboard mocking functions. - -- (BOOL)containsURLData { - NSArray* urlTypes = [URLDropTargetHandler handledDragTypes]; - if (dragDataType_) - return [urlTypes containsObject:dragDataType_]; - return NO; -} - -- (NSData*)dataForType:(NSString*)type { - if (dragDataType_ && [dragDataType_ isEqualToString:type]) - return [NSData data]; // Return something, anything. - return nil; -} - -// Fake a controller for callback ponging - -- (BOOL)dragButton:(BookmarkButton*)button to:(NSPoint)point copy:(BOOL)copy { - dragButtonToPong_ = YES; - return YES; -} - -- (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point { - dragURLsPong_ = YES; - return YES; -} - -- (void)getURLs:(NSArray**)outUrls - andTitles:(NSArray**)outTitles - convertingFilenames:(BOOL)convertFilenames { -} - -- (BOOL)dragBookmarkData:(id<NSDraggingInfo>)info { - dragBookmarkDataPong_ = YES; - return NO; -} - -// Confirm the pongs. - -- (BOOL)dragButtonToPong { - return dragButtonToPong_; -} - -- (BOOL)dragURLsPong { - return dragURLsPong_; -} - -- (BOOL)dragBookmarkDataPong { - return dragBookmarkDataPong_; -} - -- (CGFloat)indicatorPosForDragToPoint:(NSPoint)point { - return kFakeIndicatorPos; -} - -- (BOOL)shouldShowIndicatorShownForPoint:(NSPoint)point { - return dropIndicatorShown_; -} - -- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info { - draggingEnteredCalled_ = YES; - return NSDragOperationNone; -} - -@end - -namespace { - -class BookmarkBarFolderViewTest : public CocoaTest { - public: - virtual void SetUp() { - CocoaTest::SetUp(); - view_.reset([[BookmarkBarFolderView alloc] init]); - } - - scoped_nsobject<BookmarkBarFolderView> view_; -}; - -TEST_F(BookmarkBarFolderViewTest, BookmarkButtonDragAndDrop) { - [view_ awakeFromNib]; - scoped_nsobject<FakeDraggingInfo> info([[FakeDraggingInfo alloc] init]); - [view_ setController:info.get()]; - [info reset]; - - [info setDragDataType:kBookmarkButtonDragType]; - EXPECT_EQ([view_ draggingEntered:(id)info.get()], NSDragOperationMove); - EXPECT_TRUE([view_ performDragOperation:(id)info.get()]); - EXPECT_TRUE([info dragButtonToPong]); - EXPECT_FALSE([info dragURLsPong]); - EXPECT_TRUE([info dragBookmarkDataPong]); -} - -TEST_F(BookmarkBarFolderViewTest, URLDragAndDrop) { - [view_ awakeFromNib]; - scoped_nsobject<FakeDraggingInfo> info([[FakeDraggingInfo alloc] init]); - [view_ setController:info.get()]; - [info reset]; - - NSArray* dragTypes = [URLDropTargetHandler handledDragTypes]; - for (NSString* type in dragTypes) { - [info setDragDataType:type]; - EXPECT_EQ([view_ draggingEntered:(id)info.get()], NSDragOperationMove); - EXPECT_TRUE([view_ performDragOperation:(id)info.get()]); - EXPECT_FALSE([info dragButtonToPong]); - EXPECT_TRUE([info dragURLsPong]); - EXPECT_TRUE([info dragBookmarkDataPong]); - [info reset]; - } -} - -TEST_F(BookmarkBarFolderViewTest, BookmarkButtonDropIndicator) { - [view_ awakeFromNib]; - scoped_nsobject<FakeDraggingInfo> info([[FakeDraggingInfo alloc] init]); - [view_ setController:info.get()]; - [info reset]; - - [info setDragDataType:kBookmarkButtonDragType]; - EXPECT_FALSE([info draggingEnteredCalled]); - EXPECT_EQ([view_ draggingEntered:(id)info.get()], NSDragOperationMove); - EXPECT_TRUE([info draggingEnteredCalled]); // Ensure controller pinged. - EXPECT_TRUE([view_ dropIndicatorShown]); - EXPECT_EQ([view_ dropIndicatorPosition], kFakeIndicatorPos); - - [info setDropIndicatorShown:NO]; - EXPECT_EQ([view_ draggingEntered:(id)info.get()], NSDragOperationMove); - EXPECT_FALSE([view_ dropIndicatorShown]); -} - -} // namespace diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_window.h b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_window.h deleted file mode 100644 index 4b85277..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_window.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_FOLDER_WINDOW_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_FOLDER_WINDOW_H_ -#pragma once - -#import <Cocoa/Cocoa.h> -#import "base/cocoa_protocols_mac.h" -#include "base/scoped_nsobject.h" - - -// Window for a bookmark folder "menu". This menu pops up when you -// click on a bookmark button that represents a folder of bookmarks. -// This window is borderless. -@interface BookmarkBarFolderWindow : NSWindow -@end - -// Content view for the above window. "Stock" other than the drawing -// of rounded corners. Only used in the nib. -@interface BookmarkBarFolderWindowContentView : NSView { - // Arrows to show ability to scroll up and down as needed. - scoped_nsobject<NSImage> arrowUpImage_; - scoped_nsobject<NSImage> arrowDownImage_; -} -@end - -// Scroll view that contains the main view (where the buttons go). -@interface BookmarkBarFolderWindowScrollView : NSScrollView -@end - - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_FOLDER_WINDOW_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_window.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_window.mm deleted file mode 100644 index 5e51a81..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_window.mm +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_window.h" - -#import "base/logging.h" -#include "base/nsimage_cache_mac.h" -#import "base/scoped_nsobject.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.h" -#import "chrome/browser/cocoa/image_utils.h" -#import "third_party/GTM/AppKit/GTMNSColor+Luminance.h" -#import "third_party/GTM/AppKit/GTMNSBezierPath+RoundRect.h" - - -@implementation BookmarkBarFolderWindow - -- (id)initWithContentRect:(NSRect)contentRect - styleMask:(NSUInteger)windowStyle - backing:(NSBackingStoreType)bufferingType - defer:(BOOL)deferCreation { - if ((self = [super initWithContentRect:contentRect - styleMask:NSBorderlessWindowMask // override - backing:bufferingType - defer:deferCreation])) { - [self setBackgroundColor:[NSColor clearColor]]; - [self setOpaque:NO]; - } - return self; -} - -@end - - -namespace { -// Corner radius for our bookmark bar folder window. -// Copied from bubble_view.mm. -const CGFloat kViewCornerRadius = 4.0; -} - -@implementation BookmarkBarFolderWindowContentView - -- (void)awakeFromNib { - arrowUpImage_.reset([nsimage_cache::ImageNamed(@"menu_overflow_up.pdf") - retain]); - arrowDownImage_.reset([nsimage_cache::ImageNamed(@"menu_overflow_down.pdf") - retain]); -} - -// Draw the arrows at the top and bottom of the folder window as a -// visual indication that scrolling is possible. We always draw the -// scrolling arrows; when not relevant (e.g. when not scrollable), the -// scroll view overlaps the window and the arrows aren't visible. -- (void)drawScrollArrows:(NSRect)rect { - NSRect visibleRect = [self bounds]; - - // On top - NSRect imageRect = NSZeroRect; - imageRect.size = [arrowUpImage_ size]; - NSRect drawRect = NSOffsetRect( - imageRect, - (NSWidth(visibleRect) - NSWidth(imageRect)) / 2, - NSHeight(visibleRect) - NSHeight(imageRect)); - [arrowUpImage_ drawInRect:drawRect - fromRect:imageRect - operation:NSCompositeSourceOver - fraction:1.0 - neverFlipped:YES]; - - // On bottom - imageRect = NSZeroRect; - imageRect.size = [arrowDownImage_ size]; - drawRect = NSOffsetRect(imageRect, - (NSWidth(visibleRect) - NSWidth(imageRect)) / 2, - 0); - [arrowDownImage_ drawInRect:drawRect - fromRect:imageRect - operation:NSCompositeSourceOver - fraction:1.0 - neverFlipped:YES]; -} - -- (void)drawRect:(NSRect)rect { - NSRect bounds = [self bounds]; - // Like NSMenus, only the bottom corners are rounded. - NSBezierPath* bezier = - [NSBezierPath gtm_bezierPathWithRoundRect:bounds - topLeftCornerRadius:0 - topRightCornerRadius:0 - bottomLeftCornerRadius:kViewCornerRadius - bottomRightCornerRadius:kViewCornerRadius]; - [bezier closePath]; - - // TODO(jrg): share code with info_bubble_view.mm? Or bubble_view.mm? - NSColor* base_color = [NSColor colorWithCalibratedWhite:0.5 alpha:1.0]; - NSColor* startColor = - [base_color gtm_colorAdjustedFor:GTMColorationLightHighlight - faded:YES]; - NSColor* midColor = - [base_color gtm_colorAdjustedFor:GTMColorationLightMidtone - faded:YES]; - NSColor* endColor = - [base_color gtm_colorAdjustedFor:GTMColorationLightShadow - faded:YES]; - NSColor* glowColor = - [base_color gtm_colorAdjustedFor:GTMColorationLightPenumbra - faded:YES]; - - scoped_nsobject<NSGradient> gradient( - [[NSGradient alloc] initWithColorsAndLocations:startColor, 0.0, - midColor, 0.25, - endColor, 0.5, - glowColor, 0.75, - nil]); - [gradient drawInBezierPath:bezier angle:0.0]; - - [self drawScrollArrows:rect]; -} - -@end - - -@implementation BookmarkBarFolderWindowScrollView - -// We want "draw background" of the NSScrollView in the xib to be NOT -// checked. That allows us to round the bottom corners of the folder -// window. However that also allows some scrollWheel: events to leak -// into the NSWindow behind it (even in a different application). -// Better to plug the scroll leak than to round corners for M5. -- (void)scrollWheel:(NSEvent *)theEvent { - DCHECK([[[self window] windowController] - respondsToSelector:@selector(scrollWheel:)]); - [[[self window] windowController] scrollWheel:theEvent]; -} - -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_window_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_window_unittest.mm deleted file mode 100644 index d291390..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_folder_window_unittest.mm +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/scoped_ptr.h" -#include "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_window.h" -#include "chrome/browser/cocoa/cocoa_test_helper.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" - -class BookmarkBarFolderWindowTest : public CocoaTest { -}; - -TEST_F(BookmarkBarFolderWindowTest, Borderless) { - scoped_nsobject<BookmarkBarFolderWindow> window_; - window_.reset([[BookmarkBarFolderWindow alloc] - initWithContentRect:NSMakeRect(0,0,20,20) - styleMask:0 - backing:NSBackingStoreBuffered - defer:NO]); - EXPECT_EQ(NSBorderlessWindowMask, [window_ styleMask]); -} - - -class BookmarkBarFolderWindowContentViewTest : public CocoaTest { - public: - BookmarkBarFolderWindowContentViewTest() { - view_.reset([[BookmarkBarFolderWindowContentView alloc] - initWithFrame:NSMakeRect(0, 0, 100, 100)]); - [[test_window() contentView] addSubview:view_.get()]; - } - scoped_nsobject<BookmarkBarFolderWindowContentView> view_; - scoped_nsobject<BookmarkBarFolderWindowScrollView> scroll_view_; -}; - -TEST_VIEW(BookmarkBarFolderWindowContentViewTest, view_); - - -class BookmarkBarFolderWindowScrollViewTest : public CocoaTest { - public: - BookmarkBarFolderWindowScrollViewTest() { - scroll_view_.reset([[BookmarkBarFolderWindowScrollView alloc] - initWithFrame:NSMakeRect(0, 0, 100, 100)]); - [[test_window() contentView] addSubview:scroll_view_.get()]; - } - scoped_nsobject<BookmarkBarFolderWindowScrollView> scroll_view_; -}; - -TEST_VIEW(BookmarkBarFolderWindowScrollViewTest, scroll_view_); diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_state.h b/chrome/browser/cocoa/bookmarks/bookmark_bar_state.h deleted file mode 100644 index 4f98b5c..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_state.h +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_STATE_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_STATE_H_ -#pragma once - -#import <Cocoa/Cocoa.h> - -namespace bookmarks { - -// States for the bookmark bar. -enum VisualState { - kInvalidState = 0, - kHiddenState = 1, - kShowingState = 2, - kDetachedState = 3, -}; - -} // namespace bookmarks - -// The interface for controllers (etc.) which can give information about the -// bookmark bar's state. -@protocol BookmarkBarState - -// Returns YES if the bookmark bar is currently visible (as a normal toolbar or -// as a detached bar on the NTP), NO otherwise. -- (BOOL)isVisible; - -// Returns YES if an animation is currently running, NO otherwise. -- (BOOL)isAnimationRunning; - -// Returns YES if the bookmark bar is in the given state and not in an -// animation, NO otherwise. -- (BOOL)isInState:(bookmarks::VisualState)state; - -// Returns YES if the bookmark bar is animating from the given state (to any -// other state), NO otherwise. -- (BOOL)isAnimatingToState:(bookmarks::VisualState)state; - -// Returns YES if the bookmark bar is animating to the given state (from any -// other state), NO otherwise. -- (BOOL)isAnimatingFromState:(bookmarks::VisualState)state; - -// Returns YES if the bookmark bar is animating from the first given state to -// the second given state, NO otherwise. -- (BOOL)isAnimatingFromState:(bookmarks::VisualState)fromState - toState:(bookmarks::VisualState)toState; - -// Returns YES if the bookmark bar is animating between the two given states (in -// either direction), NO otherwise. -- (BOOL)isAnimatingBetweenState:(bookmarks::VisualState)fromState - andState:(bookmarks::VisualState)toState; - -// Returns how morphed into the detached bubble the bookmark bar should be (1 = -// completely detached, 0 = normal). -- (CGFloat)detachedMorphProgress; - -@end - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_STATE_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_toolbar_view.h b/chrome/browser/cocoa/bookmarks/bookmark_bar_toolbar_view.h deleted file mode 100644 index cdc6e9c..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_toolbar_view.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// The BookmarkBarToolbarView is responsible for drawing the background of the -// BookmarkBar's toolbar in either of its two display modes - permanently -// attached (slimline with a stroke at the bottom edge) or New Tab Page style -// (padded with a round rect border and the New Tab Page theme behind). - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_TOOLBAR_VIEW_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_TOOLBAR_VIEW_H_ -#pragma once - -#import <Cocoa/Cocoa.h> - -#import "chrome/browser/cocoa/animatable_view.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_state.h" - -@class BookmarkBarView; -class TabContents; -class ThemeProvider; - -// An interface to allow mocking of a BookmarkBarController by the -// BookmarkBarToolbarView. -@protocol BookmarkBarToolbarViewController <BookmarkBarState> -// Displaying the bookmark toolbar background in bubble (floating) mode requires -// the size of the currently selected tab to properly calculate where the -// background image is joined. -- (int)currentTabContentsHeight; - -// Current theme provider, passed to the cross platform NtpBackgroundUtil class. -- (ThemeProvider*)themeProvider; - -@end - -@interface BookmarkBarToolbarView : AnimatableView { - @private - // The controller which tells us how we should be drawing (as normal or as a - // floating bar). - IBOutlet id<BookmarkBarToolbarViewController> controller_; -} -@end - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_TOOLBAR_VIEW_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_toolbar_view.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_toolbar_view.mm deleted file mode 100644 index 6abf516..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_toolbar_view.mm +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_toolbar_view.h" - -#include "app/theme_provider.h" -#include "gfx/rect.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_constants.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" -#import "chrome/browser/cocoa/browser_window_controller.h" -#import "chrome/browser/cocoa/themed_window.h" -#include "chrome/browser/ntp_background_util.h" -#include "chrome/browser/themes/browser_theme_provider.h" -#include "gfx/canvas_skia_paint.h" - -const CGFloat kBorderRadius = 3.0; - -@interface BookmarkBarToolbarView (Private) -- (void)drawRectAsBubble:(NSRect)rect; -@end - -@implementation BookmarkBarToolbarView - -- (BOOL)isOpaque { - return [controller_ isInState:bookmarks::kDetachedState]; -} - -- (void)drawRect:(NSRect)rect { - if ([controller_ isInState:bookmarks::kDetachedState] || - [controller_ isAnimatingToState:bookmarks::kDetachedState] || - [controller_ isAnimatingFromState:bookmarks::kDetachedState]) { - [self drawRectAsBubble:rect]; - } else { - NSPoint phase = [[self window] themePatternPhase]; - [[NSGraphicsContext currentContext] setPatternPhase:phase]; - [self drawBackground]; - } -} - -- (void)drawRectAsBubble:(NSRect)rect { - // The state of our morph; 1 is total bubble, 0 is the regular bar. We use it - // to morph the bubble to a regular bar (shape and colour). - CGFloat morph = [controller_ detachedMorphProgress]; - - NSRect bounds = [self bounds]; - - ThemeProvider* themeProvider = [controller_ themeProvider]; - if (!themeProvider) - return; - - NSGraphicsContext* context = [NSGraphicsContext currentContext]; - [context saveGraphicsState]; - - // Draw the background. - { - // CanvasSkiaPaint draws to the NSGraphicsContext during its destructor, so - // explicitly scope this. - // - // Paint the entire bookmark bar, even if the damage rect is much smaller - // because PaintBackgroundDetachedMode() assumes that area's origin is - // (0, 0) and that its size is the size of the bookmark bar. - // - // In practice, this sounds worse than it is because redraw time is still - // minimal compared to the pause between frames of animations. We were - // already repainting the rest of the bookmark bar below without setting a - // clip area, anyway. Also, the only time we weren't asked to redraw the - // whole bookmark bar is when the find bar is drawn over it. - gfx::CanvasSkiaPaint canvas(bounds, true); - gfx::Rect area(0, 0, NSWidth(bounds), NSHeight(bounds)); - - NtpBackgroundUtil::PaintBackgroundDetachedMode(themeProvider, &canvas, - area, [controller_ currentTabContentsHeight]); - } - - // Draw our bookmark bar border on top of the background. - NSRect frameRect = - NSMakeRect( - morph * bookmarks::kNTPBookmarkBarPadding, - morph * bookmarks::kNTPBookmarkBarPadding, - NSWidth(bounds) - 2 * morph * bookmarks::kNTPBookmarkBarPadding, - NSHeight(bounds) - 2 * morph * bookmarks::kNTPBookmarkBarPadding); - // Now draw a bezier path with rounded rectangles around the area. - frameRect = NSInsetRect(frameRect, morph * 0.5, morph * 0.5); - NSBezierPath* border = - [NSBezierPath bezierPathWithRoundedRect:frameRect - xRadius:(morph * kBorderRadius) - yRadius:(morph * kBorderRadius)]; - - // Draw the rounded rectangle. - NSColor* toolbarColor = - themeProvider->GetNSColor(BrowserThemeProvider::COLOR_TOOLBAR, true); - CGFloat alpha = morph * [toolbarColor alphaComponent]; - [[toolbarColor colorWithAlphaComponent:alpha] set]; // Set with opacity. - [border fill]; - - // Fade in/out the background. - [context saveGraphicsState]; - [border setClip]; - CGContextRef cgContext = (CGContextRef)[context graphicsPort]; - CGContextBeginTransparencyLayer(cgContext, NULL); - CGContextSetAlpha(cgContext, 1 - morph); - [context setPatternPhase:[[self window] themePatternPhase]]; - [self drawBackground]; - CGContextEndTransparencyLayer(cgContext); - [context restoreGraphicsState]; - - // Draw the border of the rounded rectangle. - NSColor* borderColor = themeProvider->GetNSColor( - BrowserThemeProvider::COLOR_TOOLBAR_BUTTON_STROKE, true); - alpha = morph * [borderColor alphaComponent]; - [[borderColor colorWithAlphaComponent:alpha] set]; // Set with opacity. - [border stroke]; - - // Fade in/out the divider. - // TODO(viettrungluu): It's not obvious that this divider lines up exactly - // with |BackgroundGradientView|'s (in fact, it probably doesn't). - NSColor* strokeColor = [self strokeColor]; - alpha = (1 - morph) * [strokeColor alphaComponent]; - [[strokeColor colorWithAlphaComponent:alpha] set]; - NSBezierPath* divider = [NSBezierPath bezierPath]; - NSPoint dividerStart = - NSMakePoint(morph * bookmarks::kNTPBookmarkBarPadding + morph * 0.5, - morph * bookmarks::kNTPBookmarkBarPadding + morph * 0.5); - CGFloat dividerWidth = - NSWidth(bounds) - 2 * morph * bookmarks::kNTPBookmarkBarPadding - 2 * 0.5; - [divider moveToPoint:dividerStart]; - [divider relativeLineToPoint:NSMakePoint(dividerWidth, 0)]; - [divider stroke]; - - // Restore the graphics context. - [context restoreGraphicsState]; -} - -@end // @implementation BookmarkBarToolbarView diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_toolbar_view_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_toolbar_view_unittest.mm deleted file mode 100644 index aecdf84..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_toolbar_view_unittest.mm +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#include "app/theme_provider.h" -#include "base/scoped_nsobject.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_toolbar_view.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" -#include "chrome/browser/themes/browser_theme_provider.h" -#include "grit/theme_resources.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkColor.h" - -using ::testing::_; -using ::testing::DoAll; -using ::testing::Return; -using ::testing::SetArgumentPointee; - -// When testing the floating drawing, we need to have a source of theme data. -class MockThemeProvider : public ThemeProvider { - public: - // Cross platform methods - MOCK_METHOD1(Init, void(Profile*)); - MOCK_CONST_METHOD1(GetBitmapNamed, SkBitmap*(int)); - MOCK_CONST_METHOD1(GetColor, SkColor(int)); - MOCK_CONST_METHOD2(GetDisplayProperty, bool(int, int*)); - MOCK_CONST_METHOD0(ShouldUseNativeFrame, bool()); - MOCK_CONST_METHOD1(HasCustomImage, bool(int)); - MOCK_CONST_METHOD1(GetRawData, RefCountedMemory*(int)); - - // OSX stuff - MOCK_CONST_METHOD2(GetNSImageNamed, NSImage*(int, bool)); - MOCK_CONST_METHOD2(GetNSImageColorNamed, NSColor*(int, bool)); - MOCK_CONST_METHOD2(GetNSColor, NSColor*(int, bool)); - MOCK_CONST_METHOD2(GetNSColorTint, NSColor*(int, bool)); - MOCK_CONST_METHOD1(GetNSGradient, NSGradient*(int)); -}; - -// Allows us to inject our fake controller below. -@interface BookmarkBarToolbarView (TestingAPI) --(void)setController:(id<BookmarkBarToolbarViewController>)controller; -@end - -@implementation BookmarkBarToolbarView (TestingAPI) --(void)setController:(id<BookmarkBarToolbarViewController>)controller { - controller_ = controller; -} -@end - -// Allows us to control which way the view is rendered. -@interface DrawDetachedBarFakeController : - NSObject<BookmarkBarState, BookmarkBarToolbarViewController> { - @private - int currentTabContentsHeight_; - ThemeProvider* themeProvider_; - bookmarks::VisualState visualState_; -} -@property (nonatomic, assign) int currentTabContentsHeight; -@property (nonatomic, assign) ThemeProvider* themeProvider; -@property (nonatomic, assign) bookmarks::VisualState visualState; - -// |BookmarkBarState| protocol: -- (BOOL)isVisible; -- (BOOL)isAnimationRunning; -- (BOOL)isInState:(bookmarks::VisualState)state; -- (BOOL)isAnimatingToState:(bookmarks::VisualState)state; -- (BOOL)isAnimatingFromState:(bookmarks::VisualState)state; -- (BOOL)isAnimatingFromState:(bookmarks::VisualState)fromState - toState:(bookmarks::VisualState)toState; -- (BOOL)isAnimatingBetweenState:(bookmarks::VisualState)fromState - andState:(bookmarks::VisualState)toState; -- (CGFloat)detachedMorphProgress; -@end - -@implementation DrawDetachedBarFakeController -@synthesize currentTabContentsHeight = currentTabContentsHeight_; -@synthesize themeProvider = themeProvider_; -@synthesize visualState = visualState_; - -- (id)init { - if ((self = [super init])) { - [self setVisualState:bookmarks::kHiddenState]; - } - return self; -} - -- (BOOL)isVisible { return YES; } -- (BOOL)isAnimationRunning { return NO; } -- (BOOL)isInState:(bookmarks::VisualState)state - { return ([self visualState] == state) ? YES : NO; } -- (BOOL)isAnimatingToState:(bookmarks::VisualState)state { return NO; } -- (BOOL)isAnimatingFromState:(bookmarks::VisualState)state { return NO; } -- (BOOL)isAnimatingFromState:(bookmarks::VisualState)fromState - toState:(bookmarks::VisualState)toState { return NO; } -- (BOOL)isAnimatingBetweenState:(bookmarks::VisualState)fromState - andState:(bookmarks::VisualState)toState { return NO; } -- (CGFloat)detachedMorphProgress { return 1; } -@end - -class BookmarkBarToolbarViewTest : public CocoaTest { - public: - BookmarkBarToolbarViewTest() { - controller_.reset([[DrawDetachedBarFakeController alloc] init]); - NSRect frame = NSMakeRect(0, 0, 400, 40); - scoped_nsobject<BookmarkBarToolbarView> view( - [[BookmarkBarToolbarView alloc] initWithFrame:frame]); - view_ = view.get(); - [[test_window() contentView] addSubview:view_]; - [view_ setController:controller_.get()]; - } - - scoped_nsobject<DrawDetachedBarFakeController> controller_; - BookmarkBarToolbarView* view_; -}; - -TEST_VIEW(BookmarkBarToolbarViewTest, view_) - -// Test drawing (part 1), mostly to ensure nothing leaks or crashes. -TEST_F(BookmarkBarToolbarViewTest, DisplayAsNormalBar) { - [controller_.get() setVisualState:bookmarks::kShowingState]; - [view_ display]; -} - -// Test drawing (part 2), mostly to ensure nothing leaks or crashes. -TEST_F(BookmarkBarToolbarViewTest, DisplayAsDetachedBarWithNoImage) { - [controller_.get() setVisualState:bookmarks::kDetachedState]; - - // Tests where we don't have a background image, only a color. - MockThemeProvider provider; - EXPECT_CALL(provider, GetColor(BrowserThemeProvider::COLOR_NTP_BACKGROUND)) - .WillRepeatedly(Return(SK_ColorWHITE)); - EXPECT_CALL(provider, HasCustomImage(IDR_THEME_NTP_BACKGROUND)) - .WillRepeatedly(Return(false)); - [controller_.get() setThemeProvider:&provider]; - - [view_ display]; -} - -// Actions used in DisplayAsDetachedBarWithBgImage. -ACTION(SetBackgroundTiling) { - *arg1 = BrowserThemeProvider::NO_REPEAT; - return true; -} - -ACTION(SetAlignLeft) { - *arg1 = BrowserThemeProvider::ALIGN_LEFT; - return true; -} - -// Test drawing (part 3), mostly to ensure nothing leaks or crashes. -TEST_F(BookmarkBarToolbarViewTest, DisplayAsDetachedBarWithBgImage) { - [controller_.get() setVisualState:bookmarks::kDetachedState]; - - // Tests where we have a background image, with positioning information. - MockThemeProvider provider; - - // Advertise having an image. - EXPECT_CALL(provider, GetColor(BrowserThemeProvider::COLOR_NTP_BACKGROUND)) - .WillRepeatedly(Return(SK_ColorRED)); - EXPECT_CALL(provider, HasCustomImage(IDR_THEME_NTP_BACKGROUND)) - .WillRepeatedly(Return(true)); - - // Return the correct tiling/alignment information. - EXPECT_CALL(provider, - GetDisplayProperty(BrowserThemeProvider::NTP_BACKGROUND_TILING, _)) - .WillRepeatedly(SetBackgroundTiling()); - EXPECT_CALL(provider, - GetDisplayProperty(BrowserThemeProvider::NTP_BACKGROUND_ALIGNMENT, _)) - .WillRepeatedly(SetAlignLeft()); - - // Create a dummy bitmap full of not-red to blit with. - SkBitmap fake_bg; - fake_bg.setConfig(SkBitmap::kARGB_8888_Config, 800, 800); - fake_bg.allocPixels(); - fake_bg.eraseColor(SK_ColorGREEN); - EXPECT_CALL(provider, GetBitmapNamed(IDR_THEME_NTP_BACKGROUND)) - .WillRepeatedly(Return(&fake_bg)); - - [controller_.get() setThemeProvider:&provider]; - [controller_.get() setCurrentTabContentsHeight:200]; - - [view_ display]; -} - -// TODO(viettrungluu): write more unit tests, especially after my refactoring. diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_unittest_helper.h b/chrome/browser/cocoa/bookmarks/bookmark_bar_unittest_helper.h deleted file mode 100644 index edd60d2..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_unittest_helper.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_UNITTEST_HELPER_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_UNITTEST_HELPER_H_ -#pragma once - -#import <Foundation/Foundation.h> - -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button.h" - -@interface BookmarkBarController (BookmarkBarUnitTestHelper) - -// Return the bookmark button from this bar controller with the given -// |title|, otherwise nil. This does not recurse into folders. -- (BookmarkButton*)buttonWithTitleEqualTo:(NSString*)title; - -@end - - -@interface BookmarkBarFolderController (BookmarkBarUnitTestHelper) - -// Return the bookmark button from this folder controller with the given -// |title|, otherwise nil. This does not recurse into subfolders. -- (BookmarkButton*)buttonWithTitleEqualTo:(NSString*)title; - -@end - - -@interface BookmarkButton (BookmarkBarUnitTestHelper) - -// Return the center of the button in the base coordinate system of the -// containing window. Useful for simulating mouse clicks or drags. -- (NSPoint)center; - -// Return the top of the button in the base coordinate system of the -// containing window. -- (NSPoint)top; - -// Return the bottom of the button in the base coordinate system of the -// containing window. -- (NSPoint)bottom; - -// Return the center-left point of the button in the base coordinate system -// of the containing window. -- (NSPoint)left; - -// Return the center-right point of the button in the base coordinate system -// of the containing window. -- (NSPoint)right; - -@end - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_UNITTEST_HELPER_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_unittest_helper.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_unittest_helper.mm deleted file mode 100644 index c8eefdb..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_unittest_helper.mm +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_unittest_helper.h" - -@interface NSArray (BookmarkBarUnitTestHelper) - -// A helper function for scanning an array of buttons looking for the -// button with the given |title|. -- (BookmarkButton*)buttonWithTitleEqualTo:(NSString*)title; - -@end - - -@implementation NSArray (BookmarkBarUnitTestHelper) - -- (BookmarkButton*)buttonWithTitleEqualTo:(NSString*)title { - for (BookmarkButton* button in self) { - if ([[button title] isEqualToString:title]) - return button; - } - return nil; -} - -@end - -@implementation BookmarkBarController (BookmarkBarUnitTestHelper) - -- (BookmarkButton*)buttonWithTitleEqualTo:(NSString*)title { - return [[self buttons] buttonWithTitleEqualTo:title]; -} - -@end - -@implementation BookmarkBarFolderController(BookmarkBarUnitTestHelper) - -- (BookmarkButton*)buttonWithTitleEqualTo:(NSString*)title { - return [[self buttons] buttonWithTitleEqualTo:title]; -} - -@end - -@implementation BookmarkButton(BookmarkBarUnitTestHelper) - -- (NSPoint)center { - NSRect frame = [self frame]; - NSPoint center = NSMakePoint(NSMidX(frame), NSMidY(frame)); - center = [[self superview] convertPoint:center toView:nil]; - return center; -} - -- (NSPoint)top { - NSRect frame = [self frame]; - NSPoint top = NSMakePoint(NSMidX(frame), NSMaxY(frame)); - top = [[self superview] convertPoint:top toView:nil]; - return top; -} - -- (NSPoint)bottom { - NSRect frame = [self frame]; - NSPoint bottom = NSMakePoint(NSMidX(frame), NSMinY(frame)); - bottom = [[self superview] convertPoint:bottom toView:nil]; - return bottom; -} - -- (NSPoint)left { - NSRect frame = [self frame]; - NSPoint left = NSMakePoint(NSMinX(frame), NSMidY(frame)); - left = [[self superview] convertPoint:left toView:nil]; - return left; -} - -- (NSPoint)right { - NSRect frame = [self frame]; - NSPoint right = NSMakePoint(NSMaxX(frame), NSMidY(frame)); - right = [[self superview] convertPoint:right toView:nil]; - return right; -} - -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_view.h b/chrome/browser/cocoa/bookmarks/bookmark_bar_view.h deleted file mode 100644 index a409171..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_view.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// A simple custom NSView for the bookmark bar used to prevent clicking and -// dragging from moving the browser window. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_VIEW_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_VIEW_H_ -#pragma once - -#import <Cocoa/Cocoa.h> - -#import "chrome/browser/cocoa/background_gradient_view.h" - -@class BookmarkBarController; - -@interface BookmarkBarView : BackgroundGradientView { - @private - BOOL dropIndicatorShown_; - CGFloat dropIndicatorPosition_; // x position - - IBOutlet BookmarkBarController* controller_; - IBOutlet NSTextField* noItemTextfield_; - IBOutlet NSButton* importBookmarksButton_; - NSView* noItemContainer_; -} -- (NSTextField*)noItemTextfield; -- (NSButton*)importBookmarksButton; -- (BookmarkBarController*)controller; - -@property (nonatomic, assign) IBOutlet NSView* noItemContainer; -@end - -@interface BookmarkBarView() // TestingOrInternalAPI -@property (nonatomic, readonly) BOOL dropIndicatorShown; -@property (nonatomic, readonly) CGFloat dropIndicatorPosition; -- (void)setController:(id)controller; -@end - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BAR_VIEW_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_view.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_view.mm deleted file mode 100644 index 667cff1..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_view.mm +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_view.h" - -#include "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_folder_target.h" -#import "chrome/browser/cocoa/themed_window.h" -#import "chrome/browser/cocoa/view_id_util.h" -#include "chrome/browser/metrics/user_metrics.h" -#import "chrome/browser/themes/browser_theme_provider.h" -#import "third_party/mozilla/NSPasteboard+Utils.h" - -@interface BookmarkBarView (Private) -- (void)themeDidChangeNotification:(NSNotification*)aNotification; -- (void)updateTheme:(ThemeProvider*)themeProvider; -@end - -@implementation BookmarkBarView - -@synthesize dropIndicatorShown = dropIndicatorShown_; -@synthesize dropIndicatorPosition = dropIndicatorPosition_; -@synthesize noItemContainer = noItemContainer_; - -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - // This probably isn't strictly necessary, but can't hurt. - [self unregisterDraggedTypes]; - [super dealloc]; - - // To be clear, our controller_ is an IBOutlet and owns us, so we - // don't deallocate it explicitly. It is owned by the browser - // window controller, so gets deleted with a browser window is - // closed. -} - -- (void)awakeFromNib { - NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter]; - [defaultCenter addObserver:self - selector:@selector(themeDidChangeNotification:) - name:kBrowserThemeDidChangeNotification - object:nil]; - - DCHECK(controller_) << "Expected this to be hooked up via Interface Builder"; - NSArray* types = [NSArray arrayWithObjects: - NSStringPboardType, - NSHTMLPboardType, - NSURLPboardType, - kBookmarkButtonDragType, - kBookmarkDictionaryListPboardType, - nil]; - [self registerForDraggedTypes:types]; -} - -// We need the theme to color the bookmark buttons properly. But our -// controller desn't have access to it until it's placed in the view -// hierarchy. This is the spot where we close the loop. -- (void)viewWillMoveToWindow:(NSWindow*)window { - ThemeProvider* themeProvider = [window themeProvider]; - [self updateTheme:themeProvider]; - [controller_ updateTheme:themeProvider]; -} - -- (void)viewDidMoveToWindow { - [controller_ viewDidMoveToWindow]; -} - -// Called after the current theme has changed. -- (void)themeDidChangeNotification:(NSNotification*)aNotification { - ThemeProvider* themeProvider = - static_cast<ThemeProvider*>([[aNotification object] pointerValue]); - [self updateTheme:themeProvider]; -} - -// Adapt appearance to the current theme. Called after theme changes and before -// this is shown for the first time. -- (void)updateTheme:(ThemeProvider*)themeProvider { - if (!themeProvider) - return; - - NSColor* color = - themeProvider->GetNSColor(BrowserThemeProvider::COLOR_BOOKMARK_TEXT, - true); - [noItemTextfield_ setTextColor:color]; -} - -// Mouse down events on the bookmark bar should not allow dragging the parent -// window around. -- (BOOL)mouseDownCanMoveWindow { - return NO; -} - --(NSTextField*)noItemTextfield { - return noItemTextfield_; -} - --(NSButton*)importBookmarksButton { - return importBookmarksButton_; -} - -- (BookmarkBarController*)controller { - return controller_; -} - --(void)drawRect:(NSRect)dirtyRect { - [super drawRect:dirtyRect]; - - // Draw the bookmark-button-dragging drop indicator if necessary. - if (dropIndicatorShown_) { - const CGFloat kBarWidth = 1; - const CGFloat kBarHalfWidth = kBarWidth / 2.0; - const CGFloat kBarVertPad = 4; - const CGFloat kBarOpacity = 0.85; - - // Prevent the indicator from being clipped on the left. - CGFloat xLeft = MAX(dropIndicatorPosition_ - kBarHalfWidth, 0); - - NSRect uglyBlackBar = - NSMakeRect(xLeft, kBarVertPad, - kBarWidth, NSHeight([self bounds]) - 2 * kBarVertPad); - NSColor* uglyBlackBarColor = [[self window] themeProvider]-> - GetNSColor(BrowserThemeProvider::COLOR_BOOKMARK_TEXT, true); - [[uglyBlackBarColor colorWithAlphaComponent:kBarOpacity] setFill]; - [[NSBezierPath bezierPathWithRect:uglyBlackBar] fill]; - } -} - -// Shim function to assist in unit testing. -- (BOOL)dragClipboardContainsBookmarks { - return bookmark_pasteboard_helper_mac::DragClipboardContainsBookmarks(); -} - -// NSDraggingDestination methods - -- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info { - if ([[info draggingPasteboard] dataForType:kBookmarkButtonDragType] || - [self dragClipboardContainsBookmarks] || - [[info draggingPasteboard] containsURLData]) { - // We only show the drop indicator if we're not in a position to - // perform a hover-open since it doesn't make sense to do both. - BOOL showIt = [controller_ shouldShowIndicatorShownForPoint: - [info draggingLocation]]; - if (!showIt) { - if (dropIndicatorShown_) { - dropIndicatorShown_ = NO; - [self setNeedsDisplay:YES]; - } - } else { - CGFloat x = - [controller_ indicatorPosForDragToPoint:[info draggingLocation]]; - // Need an update if the indicator wasn't previously shown or if it has - // moved. - if (!dropIndicatorShown_ || dropIndicatorPosition_ != x) { - dropIndicatorShown_ = YES; - dropIndicatorPosition_ = x; - [self setNeedsDisplay:YES]; - } - } - - [controller_ draggingEntered:info]; // allow hover-open to work. - return [info draggingSource] ? NSDragOperationMove : NSDragOperationCopy; - } - return NSDragOperationNone; -} - -- (void)draggingExited:(id<NSDraggingInfo>)info { - // Regardless of the type of dragging which ended, we need to get rid of the - // drop indicator if one was shown. - if (dropIndicatorShown_) { - dropIndicatorShown_ = NO; - [self setNeedsDisplay:YES]; - } -} - -- (void)draggingEnded:(id<NSDraggingInfo>)info { - // For now, we just call |-draggingExited:|. - [self draggingExited:info]; -} - -- (BOOL)wantsPeriodicDraggingUpdates { - // TODO(port): This should probably return |YES| and the controller should - // slide the existing bookmark buttons interactively to the side to make - // room for the about-to-be-dropped bookmark. - return NO; -} - -- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info { - // For now it's the same as draggingEntered:. - // TODO(jrg): once we return YES for wantsPeriodicDraggingUpdates, - // this should ping the controller_ to perform animations. - return [self draggingEntered:info]; -} - -- (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info { - return YES; -} - -// Implement NSDraggingDestination protocol method -// performDragOperation: for URLs. -- (BOOL)performDragOperationForURL:(id<NSDraggingInfo>)info { - NSPasteboard* pboard = [info draggingPasteboard]; - DCHECK([pboard containsURLData]); - - NSArray* urls = nil; - NSArray* titles = nil; - [pboard getURLs:&urls andTitles:&titles convertingFilenames:YES]; - - return [controller_ addURLs:urls - withTitles:titles - at:[info draggingLocation]]; -} - -// Implement NSDraggingDestination protocol method -// performDragOperation: for bookmark buttons. -- (BOOL)performDragOperationForBookmarkButton:(id<NSDraggingInfo>)info { - BOOL rtn = NO; - NSData* data = [[info draggingPasteboard] - dataForType:kBookmarkButtonDragType]; - // [info draggingSource] is nil if not the same application. - if (data && [info draggingSource]) { - BookmarkButton* button = nil; - [data getBytes:&button length:sizeof(button)]; - BOOL copy = !([info draggingSourceOperationMask] & NSDragOperationMove); - rtn = [controller_ dragButton:button - to:[info draggingLocation] - copy:copy]; - UserMetrics::RecordAction(UserMetricsAction("BookmarkBar_DragEnd")); - } - return rtn; -} - -- (BOOL)performDragOperation:(id<NSDraggingInfo>)info { - if ([controller_ dragBookmarkData:info]) - return YES; - NSPasteboard* pboard = [info draggingPasteboard]; - if ([pboard dataForType:kBookmarkButtonDragType]) { - if ([self performDragOperationForBookmarkButton:info]) - return YES; - // Fall through.... - } - if ([pboard containsURLData]) { - if ([self performDragOperationForURL:info]) - return YES; - } - return NO; -} - -- (void)setController:(id)controller { - controller_ = controller; -} - -- (ViewID)viewID { - return VIEW_ID_BOOKMARK_BAR; -} - -@end // @implementation BookmarkBarView diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bar_view_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_bar_view_unittest.mm deleted file mode 100644 index 3d7725d..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bar_view_unittest.mm +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/scoped_nsobject.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_view.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_folder_target.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" -#import "chrome/browser/cocoa/url_drop_target.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" -#import "third_party/mozilla/NSPasteboard+Utils.h" - -namespace { - const CGFloat kFakeIndicatorPos = 7.0; -}; - -// Fake DraggingInfo, fake BookmarkBarController, fake NSPasteboard... -@interface FakeBookmarkDraggingInfo : NSObject { - @public - BOOL dragButtonToPong_; - BOOL dragURLsPong_; - BOOL dragBookmarkDataPong_; - BOOL dropIndicatorShown_; - BOOL draggingEnteredCalled_; - // Only mock one type of drag data at a time. - NSString* dragDataType_; -} -@property (nonatomic) BOOL dropIndicatorShown; -@property (nonatomic) BOOL draggingEnteredCalled; -@property (nonatomic, copy) NSString* dragDataType; -@end - -@implementation FakeBookmarkDraggingInfo - -@synthesize dropIndicatorShown = dropIndicatorShown_; -@synthesize draggingEnteredCalled = draggingEnteredCalled_; -@synthesize dragDataType = dragDataType_; - -- (id)init { - if ((self = [super init])) { - dropIndicatorShown_ = YES; - } - return self; -} - -- (void)dealloc { - [dragDataType_ release]; - [super dealloc]; -} - -- (void)reset { - [dragDataType_ release]; - dragDataType_ = nil; - dragButtonToPong_ = NO; - dragURLsPong_ = NO; - dragBookmarkDataPong_ = NO; - dropIndicatorShown_ = YES; - draggingEnteredCalled_ = NO; -} - -// NSDragInfo mocking functions. - -- (id)draggingPasteboard { - return self; -} - -// So we can look local. -- (id)draggingSource { - return self; -} - -- (NSDragOperation)draggingSourceOperationMask { - return NSDragOperationCopy | NSDragOperationMove; -} - -- (NSPoint)draggingLocation { - return NSMakePoint(10, 10); -} - -// NSPasteboard mocking functions. - -- (BOOL)containsURLData { - NSArray* urlTypes = [URLDropTargetHandler handledDragTypes]; - if (dragDataType_) - return [urlTypes containsObject:dragDataType_]; - return NO; -} - -- (NSData*)dataForType:(NSString*)type { - if (dragDataType_ && [dragDataType_ isEqualToString:type]) - return [NSData data]; // Return something, anything. - return nil; -} - -// Fake a controller for callback ponging - -- (BOOL)dragButton:(BookmarkButton*)button to:(NSPoint)point copy:(BOOL)copy { - dragButtonToPong_ = YES; - return YES; -} - -- (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point { - dragURLsPong_ = YES; - return YES; -} - -- (void)getURLs:(NSArray**)outUrls - andTitles:(NSArray**)outTitles - convertingFilenames:(BOOL)convertFilenames { -} - -- (BOOL)dragBookmarkData:(id<NSDraggingInfo>)info { - dragBookmarkDataPong_ = YES; - return NO; -} - -// Confirm the pongs. - -- (BOOL)dragButtonToPong { - return dragButtonToPong_; -} - -- (BOOL)dragURLsPong { - return dragURLsPong_; -} - -- (BOOL)dragBookmarkDataPong { - return dragBookmarkDataPong_; -} - -- (CGFloat)indicatorPosForDragToPoint:(NSPoint)point { - return kFakeIndicatorPos; -} - -- (BOOL)shouldShowIndicatorShownForPoint:(NSPoint)point { - return dropIndicatorShown_; -} - -- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info { - draggingEnteredCalled_ = YES; - return NSDragOperationNone; -} - -@end - -namespace { - -class BookmarkBarViewTest : public CocoaTest { - public: - virtual void SetUp() { - CocoaTest::SetUp(); - view_.reset([[BookmarkBarView alloc] init]); - } - - scoped_nsobject<BookmarkBarView> view_; -}; - -TEST_F(BookmarkBarViewTest, CanDragWindow) { - EXPECT_FALSE([view_ mouseDownCanMoveWindow]); -} - -TEST_F(BookmarkBarViewTest, BookmarkButtonDragAndDrop) { - scoped_nsobject<FakeBookmarkDraggingInfo> - info([[FakeBookmarkDraggingInfo alloc] init]); - [view_ setController:info.get()]; - [info reset]; - - [info setDragDataType:kBookmarkButtonDragType]; - EXPECT_EQ([view_ draggingEntered:(id)info.get()], NSDragOperationMove); - EXPECT_TRUE([view_ performDragOperation:(id)info.get()]); - EXPECT_TRUE([info dragButtonToPong]); - EXPECT_FALSE([info dragURLsPong]); - EXPECT_TRUE([info dragBookmarkDataPong]); -} - -TEST_F(BookmarkBarViewTest, URLDragAndDrop) { - scoped_nsobject<FakeBookmarkDraggingInfo> - info([[FakeBookmarkDraggingInfo alloc] init]); - [view_ setController:info.get()]; - [info reset]; - - NSArray* dragTypes = [URLDropTargetHandler handledDragTypes]; - for (NSString* type in dragTypes) { - [info setDragDataType:type]; - EXPECT_EQ([view_ draggingEntered:(id)info.get()], NSDragOperationMove); - EXPECT_TRUE([view_ performDragOperation:(id)info.get()]); - EXPECT_FALSE([info dragButtonToPong]); - EXPECT_TRUE([info dragURLsPong]); - EXPECT_TRUE([info dragBookmarkDataPong]); - [info reset]; - } -} - -TEST_F(BookmarkBarViewTest, BookmarkButtonDropIndicator) { - scoped_nsobject<FakeBookmarkDraggingInfo> - info([[FakeBookmarkDraggingInfo alloc] init]); - [view_ setController:info.get()]; - - [info reset]; - [info setDragDataType:kBookmarkButtonDragType]; - EXPECT_FALSE([info draggingEnteredCalled]); - EXPECT_EQ([view_ draggingEntered:(id)info.get()], NSDragOperationMove); - EXPECT_TRUE([info draggingEnteredCalled]); // Ensure controller pinged. - EXPECT_TRUE([view_ dropIndicatorShown]); - EXPECT_EQ([view_ dropIndicatorPosition], kFakeIndicatorPos); - - [info setDropIndicatorShown:NO]; - EXPECT_EQ([view_ draggingEntered:(id)info.get()], NSDragOperationMove); - EXPECT_FALSE([view_ dropIndicatorShown]); -} - -} // namespace diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bubble_controller.h b/chrome/browser/cocoa/bookmarks/bookmark_bubble_controller.h deleted file mode 100644 index 9ceee17..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bubble_controller.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#import "base/cocoa_protocols_mac.h" -#include "base/scoped_ptr.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_model_observer_for_cocoa.h" - -class BookmarkBubbleNotificationBridge; -class BookmarkModel; -class BookmarkNode; -@class BookmarkBubbleController; -@class InfoBubbleView; - - -// Controller for the bookmark bubble. The bookmark bubble is a -// bubble that pops up when clicking on the STAR next to the URL to -// add or remove it as a bookmark. This bubble allows for editing of -// the bookmark in various ways (name, folder, etc.) -@interface BookmarkBubbleController : NSWindowController<NSWindowDelegate> { - @private - NSWindow* parentWindow_; // weak - - // Both weak; owned by the current browser's profile - BookmarkModel* model_; // weak - const BookmarkNode* node_; // weak - - // The bookmark node whose button we asked to pulse. - const BookmarkNode* pulsingBookmarkNode_; // weak - - BOOL alreadyBookmarked_; - - // Ping me when the bookmark model changes out from under us. - scoped_ptr<BookmarkModelObserverForCocoa> bookmark_observer_; - - // Ping me when other Chrome things change out from under us. - scoped_ptr<BookmarkBubbleNotificationBridge> chrome_observer_; - - IBOutlet NSTextField* bigTitle_; // "Bookmark" or "Bookmark Added!" - IBOutlet NSTextField* nameTextField_; - IBOutlet NSPopUpButton* folderPopUpButton_; - IBOutlet InfoBubbleView* bubble_; // to set arrow position -} - -@property (readonly, nonatomic) const BookmarkNode* node; - -// |node| is the bookmark node we edit in this bubble. -// |alreadyBookmarked| tells us if the node was bookmarked before the -// user clicked on the star. (if NO, this is a brand new bookmark). -// The owner of this object is responsible for showing the bubble if -// it desires it to be visible on the screen. It is not shown by the -// init routine. Closing of the window happens implicitly on dealloc. -- (id)initWithParentWindow:(NSWindow*)parentWindow - model:(BookmarkModel*)model - node:(const BookmarkNode*)node - alreadyBookmarked:(BOOL)alreadyBookmarked; - -// Actions for buttons in the dialog. -- (IBAction)ok:(id)sender; -- (IBAction)remove:(id)sender; -- (IBAction)cancel:(id)sender; - -// These actions send a -editBookmarkNode: action up the responder chain. -- (IBAction)edit:(id)sender; -- (IBAction)folderChanged:(id)sender; - -@end - - -// Exposed only for unit testing. -@interface BookmarkBubbleController(ExposedForUnitTesting) -- (void)addFolderNodes:(const BookmarkNode*)parent - toPopUpButton:(NSPopUpButton*)button - indentation:(int)indentation; -- (void)setTitle:(NSString*)title parentFolder:(const BookmarkNode*)parent; -- (void)setParentFolderSelection:(const BookmarkNode*)parent; -+ (NSString*)chooseAnotherFolderString; -- (NSPopUpButton*)folderPopUpButton; -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bubble_controller.mm b/chrome/browser/cocoa/bookmarks/bookmark_bubble_controller.mm deleted file mode 100644 index d367ff9..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bubble_controller.mm +++ /dev/null @@ -1,428 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_bubble_controller.h" - -#include "app/l10n_util_mac.h" -#include "base/mac_util.h" -#include "base/sys_string_conversions.h" -#include "base/utf_string_conversions.h" // TODO(viettrungluu): remove -#include "chrome/browser/bookmarks/bookmark_model.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button.h" -#import "chrome/browser/cocoa/browser_window_controller.h" -#import "chrome/browser/cocoa/info_bubble_view.h" -#include "chrome/browser/metrics/user_metrics.h" -#include "chrome/common/notification_observer.h" -#include "chrome/common/notification_registrar.h" -#include "chrome/common/notification_service.h" -#include "grit/generated_resources.h" - - -// Simple class to watch for tab creation/destruction and close the bubble. -// Bridge between Chrome-style notifications and ObjC-style notifications. -class BookmarkBubbleNotificationBridge : public NotificationObserver { - public: - BookmarkBubbleNotificationBridge(BookmarkBubbleController* controller, - SEL selector); - virtual ~BookmarkBubbleNotificationBridge() {} - void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - private: - NotificationRegistrar registrar_; - BookmarkBubbleController* controller_; // weak; owns us. - SEL selector_; // SEL sent to controller_ on notification. -}; - -BookmarkBubbleNotificationBridge::BookmarkBubbleNotificationBridge( - BookmarkBubbleController* controller, SEL selector) - : controller_(controller), selector_(selector) { - // registrar_ will automatically RemoveAll() when destroyed so we - // don't need to do so explicitly. - registrar_.Add(this, NotificationType::TAB_CONTENTS_CONNECTED, - NotificationService::AllSources()); - registrar_.Add(this, NotificationType::TAB_CLOSED, - NotificationService::AllSources()); -} - -// At this time all notifications instigate the same behavior (go -// away) so we don't bother checking which notification came in. -void BookmarkBubbleNotificationBridge::Observe( - NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - [controller_ performSelector:selector_ withObject:controller_]; -} - - -// An object to represent the ChooseAnotherFolder item in the pop up. -@interface ChooseAnotherFolder : NSObject -@end - -@implementation ChooseAnotherFolder -@end - -@interface BookmarkBubbleController (PrivateAPI) -- (void)updateBookmarkNode; -- (void)fillInFolderList; -- (void)parentWindowWillClose:(NSNotification*)notification; -@end - -@implementation BookmarkBubbleController - -@synthesize node = node_; - -+ (id)chooseAnotherFolderObject { - // Singleton object to act as a representedObject for the "choose another - // folder" item in the pop up. - static ChooseAnotherFolder* object = nil; - if (!object) { - object = [[ChooseAnotherFolder alloc] init]; - } - return object; -} - -- (id)initWithParentWindow:(NSWindow*)parentWindow - model:(BookmarkModel*)model - node:(const BookmarkNode*)node - alreadyBookmarked:(BOOL)alreadyBookmarked { - NSString* nibPath = - [mac_util::MainAppBundle() pathForResource:@"BookmarkBubble" - ofType:@"nib"]; - if ((self = [super initWithWindowNibPath:nibPath owner:self])) { - parentWindow_ = parentWindow; - model_ = model; - node_ = node; - alreadyBookmarked_ = alreadyBookmarked; - - // Watch to see if the parent window closes, and if so, close this one. - NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; - [center addObserver:self - selector:@selector(parentWindowWillClose:) - name:NSWindowWillCloseNotification - object:parentWindow_]; - } - return self; -} - -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [super dealloc]; -} - -// If this is a new bookmark somewhere visible (e.g. on the bookmark -// bar), pulse it. Else, call ourself recursively with our parent -// until we find something visible to pulse. -- (void)startPulsingBookmarkButton:(const BookmarkNode*)node { - while (node) { - if ((node->GetParent() == model_->GetBookmarkBarNode()) || - (node == model_->other_node())) { - pulsingBookmarkNode_ = node; - NSValue *value = [NSValue valueWithPointer:node]; - NSDictionary *dict = [NSDictionary - dictionaryWithObjectsAndKeys:value, - bookmark_button::kBookmarkKey, - [NSNumber numberWithBool:YES], - bookmark_button::kBookmarkPulseFlagKey, - nil]; - [[NSNotificationCenter defaultCenter] - postNotificationName:bookmark_button::kPulseBookmarkButtonNotification - object:self - userInfo:dict]; - return; - } - node = node->GetParent(); - } -} - -- (void)stopPulsingBookmarkButton { - if (!pulsingBookmarkNode_) - return; - NSValue *value = [NSValue valueWithPointer:pulsingBookmarkNode_]; - pulsingBookmarkNode_ = NULL; - NSDictionary *dict = [NSDictionary - dictionaryWithObjectsAndKeys:value, - bookmark_button::kBookmarkKey, - [NSNumber numberWithBool:NO], - bookmark_button::kBookmarkPulseFlagKey, - nil]; - [[NSNotificationCenter defaultCenter] - postNotificationName:bookmark_button::kPulseBookmarkButtonNotification - object:self - userInfo:dict]; -} - -// Close the bookmark bubble without changing anything. Unlike a -// typical dialog's OK/Cancel, where Cancel is "do nothing", all -// buttons on the bubble have the capacity to change the bookmark -// model. This is an IBOutlet-looking entry point to remove the -// dialog without touching the model. -- (void)dismissWithoutEditing:(id)sender { - [self close]; -} - -- (void)parentWindowWillClose:(NSNotification*)notification { - [self close]; -} - -- (void)windowWillClose:(NSNotification*)notification { - // We caught a close so we don't need to watch for the parent closing. - [[NSNotificationCenter defaultCenter] removeObserver:self]; - bookmark_observer_.reset(NULL); - chrome_observer_.reset(NULL); - [self stopPulsingBookmarkButton]; - [self autorelease]; -} - -// We want this to be a child of a browser window. addChildWindow: -// (called from this function) will bring the window on-screen; -// unfortunately, [NSWindowController showWindow:] will also bring it -// on-screen (but will cause unexpected changes to the window's -// position). We cannot have an addChildWindow: and a subsequent -// showWindow:. Thus, we have our own version. -- (void)showWindow:(id)sender { - BrowserWindowController* bwc = - [BrowserWindowController browserWindowControllerForWindow:parentWindow_]; - [bwc lockBarVisibilityForOwner:self withAnimation:NO delay:NO]; - NSWindow* window = [self window]; // completes nib load - [bubble_ setArrowLocation:info_bubble::kTopRight]; - // Insure decent positioning even in the absence of a browser controller, - // which will occur for some unit tests. - NSPoint arrowtip = bwc ? [bwc bookmarkBubblePoint] : - NSMakePoint([window frame].size.width, [window frame].size.height); - NSPoint origin = [parentWindow_ convertBaseToScreen:arrowtip]; - NSPoint bubbleArrowtip = [bubble_ arrowTip]; - bubbleArrowtip = [bubble_ convertPoint:bubbleArrowtip toView:nil]; - origin.y -= bubbleArrowtip.y; - origin.x -= bubbleArrowtip.x; - [window setFrameOrigin:origin]; - [parentWindow_ addChildWindow:window ordered:NSWindowAbove]; - // Default is IDS_BOOMARK_BUBBLE_PAGE_BOOKMARK; "Bookmark". - // If adding for the 1st time the string becomes "Bookmark Added!" - if (!alreadyBookmarked_) { - NSString* title = - l10n_util::GetNSString(IDS_BOOMARK_BUBBLE_PAGE_BOOKMARKED); - [bigTitle_ setStringValue:title]; - } - - [self fillInFolderList]; - - // Ping me when things change out from under us. Unlike a normal - // dialog, the bookmark bubble's cancel: means "don't add this as a - // bookmark", not "cancel editing". We must take extra care to not - // touch the bookmark in this selector. - bookmark_observer_.reset(new BookmarkModelObserverForCocoa( - node_, model_, - self, - @selector(dismissWithoutEditing:))); - chrome_observer_.reset(new BookmarkBubbleNotificationBridge( - self, @selector(dismissWithoutEditing:))); - - // Pulse something interesting on the bookmark bar. - [self startPulsingBookmarkButton:node_]; - - [window makeKeyAndOrderFront:self]; -} - -- (void)close { - [[BrowserWindowController browserWindowControllerForWindow:parentWindow_] - releaseBarVisibilityForOwner:self withAnimation:YES delay:NO]; - [parentWindow_ removeChildWindow:[self window]]; - - // If you quit while the bubble is open, sometimes we get a - // DidResignKey before we get our parent's WindowWillClose and - // sometimes not. We protect against a multiple close (or reference - // to parentWindow_ at a bad time) by clearing it out once we're - // done, and by removing ourself from future notifications. - [[NSNotificationCenter defaultCenter] - removeObserver:self - name:NSWindowWillCloseNotification - object:parentWindow_]; - parentWindow_ = nil; - - [super close]; -} - -// Shows the bookmark editor sheet for more advanced editing. -- (void)showEditor { - [self ok:self]; - // Send the action up through the responder chain. - [NSApp sendAction:@selector(editBookmarkNode:) to:nil from:self]; -} - -- (IBAction)edit:(id)sender { - UserMetrics::RecordAction(UserMetricsAction("BookmarkBubble_Edit"), - model_->profile()); - [self showEditor]; -} - -- (IBAction)ok:(id)sender { - [self stopPulsingBookmarkButton]; // before parent changes - [self updateBookmarkNode]; - [self close]; -} - -// By implementing this, ESC causes the window to go away. If clicking the -// star was what prompted this bubble to appear (i.e., not already bookmarked), -// remove the bookmark. -- (IBAction)cancel:(id)sender { - if (!alreadyBookmarked_) { - // |-remove:| calls |-close| so don't do it. - [self remove:sender]; - } else { - [self ok:sender]; - } -} - -- (IBAction)remove:(id)sender { - [self stopPulsingBookmarkButton]; - // TODO(viettrungluu): get rid of conversion and utf_string_conversions.h. - model_->SetURLStarred(node_->GetURL(), node_->GetTitle(), false); - UserMetrics::RecordAction(UserMetricsAction("BookmarkBubble_Unstar"), - model_->profile()); - node_ = NULL; // no longer valid - [self ok:sender]; -} - -// The controller is the target of the pop up button box action so it can -// handle when "choose another folder" was picked. -- (IBAction)folderChanged:(id)sender { - DCHECK([sender isEqual:folderPopUpButton_]); - // It is possible that due to model change our parent window has been closed - // but the popup is still showing and able to notify the controller of a - // folder change. We ignore the sender in this case. - if (!parentWindow_) - return; - NSMenuItem* selected = [folderPopUpButton_ selectedItem]; - ChooseAnotherFolder* chooseItem = [[self class] chooseAnotherFolderObject]; - if ([[selected representedObject] isEqual:chooseItem]) { - UserMetrics::RecordAction( - UserMetricsAction("BookmarkBubble_EditFromCombobox"), - model_->profile()); - [self showEditor]; - } -} - -// The controller is the delegate of the window so it receives did resign key -// notifications. When key is resigned mirror Windows behavior and close the -// window. -- (void)windowDidResignKey:(NSNotification*)notification { - NSWindow* window = [self window]; - DCHECK_EQ([notification object], window); - if ([window isVisible]) { - // If the window isn't visible, it is already closed, and this notification - // has been sent as part of the closing operation, so no need to close. - [self ok:self]; - } -} - -// Look at the dialog; if the user has changed anything, update the -// bookmark node to reflect this. -- (void)updateBookmarkNode { - if (!node_) return; - - // First the title... - NSString* oldTitle = base::SysUTF16ToNSString(node_->GetTitle()); - NSString* newTitle = [nameTextField_ stringValue]; - if (![oldTitle isEqual:newTitle]) { - model_->SetTitle(node_, base::SysNSStringToUTF16(newTitle)); - UserMetrics::RecordAction( - UserMetricsAction("BookmarkBubble_ChangeTitleInBubble"), - model_->profile()); - } - // Then the parent folder. - const BookmarkNode* oldParent = node_->GetParent(); - NSMenuItem* selectedItem = [folderPopUpButton_ selectedItem]; - id representedObject = [selectedItem representedObject]; - if ([representedObject isEqual:[[self class] chooseAnotherFolderObject]]) { - // "Choose another folder..." - return; - } - const BookmarkNode* newParent = - static_cast<const BookmarkNode*>([representedObject pointerValue]); - DCHECK(newParent); - if (oldParent != newParent) { - int index = newParent->GetChildCount(); - model_->Move(node_, newParent, index); - UserMetrics::RecordAction(UserMetricsAction("BookmarkBubble_ChangeParent"), - model_->profile()); - } -} - -// Fill in all information related to the folder pop up button. -- (void)fillInFolderList { - [nameTextField_ setStringValue:base::SysUTF16ToNSString(node_->GetTitle())]; - DCHECK([folderPopUpButton_ numberOfItems] == 0); - [self addFolderNodes:model_->root_node() - toPopUpButton:folderPopUpButton_ - indentation:0]; - NSMenu* menu = [folderPopUpButton_ menu]; - NSString* title = [[self class] chooseAnotherFolderString]; - NSMenuItem *item = [menu addItemWithTitle:title - action:NULL - keyEquivalent:@""]; - ChooseAnotherFolder* obj = [[self class] chooseAnotherFolderObject]; - [item setRepresentedObject:obj]; - // Finally, select the current parent. - NSValue* parentValue = [NSValue valueWithPointer:node_->GetParent()]; - NSInteger idx = [menu indexOfItemWithRepresentedObject:parentValue]; - [folderPopUpButton_ selectItemAtIndex:idx]; -} - -@end // BookmarkBubbleController - - -@implementation BookmarkBubbleController(ExposedForUnitTesting) - -+ (NSString*)chooseAnotherFolderString { - return l10n_util::GetNSStringWithFixup( - IDS_BOOMARK_BUBBLE_CHOOSER_ANOTHER_FOLDER); -} - -// For the given folder node, walk the tree and add folder names to -// the given pop up button. -- (void)addFolderNodes:(const BookmarkNode*)parent - toPopUpButton:(NSPopUpButton*)button - indentation:(int)indentation { - if (!model_->is_root(parent)) { - NSString* title = base::SysUTF16ToNSString(parent->GetTitle()); - NSMenu* menu = [button menu]; - NSMenuItem* item = [menu addItemWithTitle:title - action:NULL - keyEquivalent:@""]; - [item setRepresentedObject:[NSValue valueWithPointer:parent]]; - [item setIndentationLevel:indentation]; - ++indentation; - } - for (int i = 0; i < parent->GetChildCount(); i++) { - const BookmarkNode* child = parent->GetChild(i); - if (child->is_folder()) - [self addFolderNodes:child - toPopUpButton:button - indentation:indentation]; - } -} - -- (void)setTitle:(NSString*)title parentFolder:(const BookmarkNode*)parent { - [nameTextField_ setStringValue:title]; - [self setParentFolderSelection:parent]; -} - -// Pick a specific parent node in the selection by finding the right -// pop up button index. -- (void)setParentFolderSelection:(const BookmarkNode*)parent { - // Expectation: There is a parent mapping for all items in the - // folderPopUpButton except the last one ("Choose another folder..."). - NSMenu* menu = [folderPopUpButton_ menu]; - NSValue* parentValue = [NSValue valueWithPointer:parent]; - NSInteger idx = [menu indexOfItemWithRepresentedObject:parentValue]; - DCHECK(idx != -1); - [folderPopUpButton_ selectItemAtIndex:idx]; -} - -- (NSPopUpButton*)folderPopUpButton { - return folderPopUpButton_; -} - -@end // implementation BookmarkBubbleController(ExposedForUnitTesting) diff --git a/chrome/browser/cocoa/bookmarks/bookmark_bubble_controller_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_bubble_controller_unittest.mm deleted file mode 100644 index 4a5b600..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_bubble_controller_unittest.mm +++ /dev/null @@ -1,490 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#include "base/basictypes.h" -#include "base/scoped_nsobject.h" -#include "base/utf_string_conversions.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bubble_controller.h" -#include "chrome/browser/cocoa/browser_test_helper.h" -#include "chrome/browser/cocoa/browser_window_controller.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" -#import "chrome/browser/cocoa/info_bubble_window.h" -#include "chrome/common/notification_service.h" -#include "testing/gtest/include/gtest/gtest.h" -#import "testing/gtest_mac.h" -#include "testing/platform_test.h" - -// Watch for bookmark pulse notifications so we can confirm they were sent. -@interface BookmarkPulseObserver : NSObject { - int notifications_; -} -@property (assign, nonatomic) int notifications; -@end - - -@implementation BookmarkPulseObserver - -@synthesize notifications = notifications_; - -- (id)init { - if ((self = [super init])) { - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(pulseBookmarkNotification:) - name:bookmark_button::kPulseBookmarkButtonNotification - object:nil]; - } - return self; -} - -- (void)pulseBookmarkNotification:(NSNotificationCenter *)notification { - notifications_++; -} - -- (void)dealloc { - [[NSNotificationCenter defaultCenter] removeObserver:self]; - [super dealloc]; -} - -@end - - -namespace { - -class BookmarkBubbleControllerTest : public CocoaTest { - public: - static int edits_; - BrowserTestHelper helper_; - BookmarkBubbleController* controller_; - - BookmarkBubbleControllerTest() : controller_(nil) { - edits_ = 0; - } - - virtual void TearDown() { - [controller_ close]; - CocoaTest::TearDown(); - } - - // Returns a controller but ownership not transferred. - // Only one of these will be valid at a time. - BookmarkBubbleController* ControllerForNode(const BookmarkNode* node) { - if (controller_ && !IsWindowClosing()) { - [controller_ close]; - controller_ = nil; - } - controller_ = [[BookmarkBubbleController alloc] - initWithParentWindow:test_window() - model:helper_.profile()->GetBookmarkModel() - node:node - alreadyBookmarked:YES]; - EXPECT_TRUE([controller_ window]); - // The window must be gone or we'll fail a unit test with windows left open. - [static_cast<InfoBubbleWindow*>([controller_ window]) setDelayOnClose:NO]; - [controller_ showWindow:nil]; - return controller_; - } - - BookmarkModel* GetBookmarkModel() { - return helper_.profile()->GetBookmarkModel(); - } - - bool IsWindowClosing() { - return [static_cast<InfoBubbleWindow*>([controller_ window]) isClosing]; - } -}; - -// static -int BookmarkBubbleControllerTest::edits_; - -// Confirm basics about the bubble window (e.g. that it is inside the -// parent window) -TEST_F(BookmarkBubbleControllerTest, TestBubbleWindow) { - BookmarkModel* model = GetBookmarkModel(); - const BookmarkNode* node = model->AddURL(model->GetBookmarkBarNode(), - 0, - ASCIIToUTF16("Bookie markie title"), - GURL("http://www.google.com")); - BookmarkBubbleController* controller = ControllerForNode(node); - EXPECT_TRUE(controller); - NSWindow* window = [controller window]; - EXPECT_TRUE(window); - EXPECT_TRUE(NSContainsRect([test_window() frame], - [window frame])); -} - -// Test that we can handle closing the parent window -TEST_F(BookmarkBubbleControllerTest, TestClosingParentWindow) { - BookmarkModel* model = GetBookmarkModel(); - const BookmarkNode* node = model->AddURL(model->GetBookmarkBarNode(), - 0, - ASCIIToUTF16("Bookie markie title"), - GURL("http://www.google.com")); - BookmarkBubbleController* controller = ControllerForNode(node); - EXPECT_TRUE(controller); - NSWindow* window = [controller window]; - EXPECT_TRUE(window); - base::mac::ScopedNSAutoreleasePool pool; - [test_window() performClose:NSApp]; -} - - -// Confirm population of folder list -TEST_F(BookmarkBubbleControllerTest, TestFillInFolder) { - // Create some folders, including a nested folder - BookmarkModel* model = GetBookmarkModel(); - EXPECT_TRUE(model); - const BookmarkNode* bookmarkBarNode = model->GetBookmarkBarNode(); - EXPECT_TRUE(bookmarkBarNode); - const BookmarkNode* node1 = model->AddGroup(bookmarkBarNode, 0, - ASCIIToUTF16("one")); - EXPECT_TRUE(node1); - const BookmarkNode* node2 = model->AddGroup(bookmarkBarNode, 1, - ASCIIToUTF16("two")); - EXPECT_TRUE(node2); - const BookmarkNode* node3 = model->AddGroup(bookmarkBarNode, 2, - ASCIIToUTF16("three")); - EXPECT_TRUE(node3); - const BookmarkNode* node4 = model->AddGroup(node2, 0, ASCIIToUTF16("sub")); - EXPECT_TRUE(node4); - const BookmarkNode* node5 = model->AddURL(node1, 0, ASCIIToUTF16("title1"), - GURL("http://www.google.com")); - EXPECT_TRUE(node5); - const BookmarkNode* node6 = model->AddURL(node3, 0, ASCIIToUTF16("title2"), - GURL("http://www.google.com")); - EXPECT_TRUE(node6); - const BookmarkNode* node7 = model->AddURL(node4, 0, ASCIIToUTF16("title3"), - GURL("http://www.google.com/reader")); - EXPECT_TRUE(node7); - - BookmarkBubbleController* controller = ControllerForNode(node4); - EXPECT_TRUE(controller); - - NSArray* titles = - [[[controller folderPopUpButton] itemArray] valueForKey:@"title"]; - EXPECT_TRUE([titles containsObject:@"one"]); - EXPECT_TRUE([titles containsObject:@"two"]); - EXPECT_TRUE([titles containsObject:@"three"]); - EXPECT_TRUE([titles containsObject:@"sub"]); - EXPECT_FALSE([titles containsObject:@"title1"]); - EXPECT_FALSE([titles containsObject:@"title2"]); -} - -// Confirm ability to handle folders with blank name. -TEST_F(BookmarkBubbleControllerTest, TestFolderWithBlankName) { - // Create some folders, including a nested folder - BookmarkModel* model = GetBookmarkModel(); - EXPECT_TRUE(model); - const BookmarkNode* bookmarkBarNode = model->GetBookmarkBarNode(); - EXPECT_TRUE(bookmarkBarNode); - const BookmarkNode* node1 = model->AddGroup(bookmarkBarNode, 0, - ASCIIToUTF16("one")); - EXPECT_TRUE(node1); - const BookmarkNode* node2 = model->AddGroup(bookmarkBarNode, 1, - ASCIIToUTF16("")); - EXPECT_TRUE(node2); - const BookmarkNode* node3 = model->AddGroup(bookmarkBarNode, 2, - ASCIIToUTF16("three")); - EXPECT_TRUE(node3); - const BookmarkNode* node2_1 = model->AddURL(node2, 0, ASCIIToUTF16("title1"), - GURL("http://www.google.com")); - EXPECT_TRUE(node2_1); - - BookmarkBubbleController* controller = ControllerForNode(node1); - EXPECT_TRUE(controller); - - // One of the items should be blank and its node should be node2. - NSArray* items = [[controller folderPopUpButton] itemArray]; - EXPECT_GT([items count], 4U); - BOOL blankFolderFound = NO; - for (NSMenuItem* item in [[controller folderPopUpButton] itemArray]) { - if ([[item title] length] == 0 && - static_cast<const BookmarkNode*>([[item representedObject] - pointerValue]) == node2) { - blankFolderFound = YES; - break; - } - } - EXPECT_TRUE(blankFolderFound); -} - - -// Click on edit; bubble gets closed. -TEST_F(BookmarkBubbleControllerTest, TestEdit) { - BookmarkModel* model = GetBookmarkModel(); - const BookmarkNode* node = model->AddURL(model->GetBookmarkBarNode(), - 0, - ASCIIToUTF16("Bookie markie title"), - GURL("http://www.google.com")); - BookmarkBubbleController* controller = ControllerForNode(node); - EXPECT_TRUE(controller); - - EXPECT_EQ(edits_, 0); - EXPECT_FALSE(IsWindowClosing()); - [controller edit:controller]; - EXPECT_EQ(edits_, 1); - EXPECT_TRUE(IsWindowClosing()); -} - -// CallClose; bubble gets closed. -// Also confirm pulse notifications get sent. -TEST_F(BookmarkBubbleControllerTest, TestClose) { - BookmarkModel* model = GetBookmarkModel(); - const BookmarkNode* node = model->AddURL( - model->GetBookmarkBarNode(), 0, ASCIIToUTF16("Bookie markie title"), - GURL("http://www.google.com")); - EXPECT_EQ(edits_, 0); - - scoped_nsobject<BookmarkPulseObserver> observer([[BookmarkPulseObserver alloc] - init]); - EXPECT_EQ([observer notifications], 0); - BookmarkBubbleController* controller = ControllerForNode(node); - EXPECT_TRUE(controller); - EXPECT_FALSE(IsWindowClosing()); - EXPECT_EQ([observer notifications], 1); - [controller ok:controller]; - EXPECT_EQ(edits_, 0); - EXPECT_TRUE(IsWindowClosing()); - EXPECT_EQ([observer notifications], 2); -} - -// User changes title and parent folder in the UI -TEST_F(BookmarkBubbleControllerTest, TestUserEdit) { - BookmarkModel* model = GetBookmarkModel(); - EXPECT_TRUE(model); - const BookmarkNode* bookmarkBarNode = model->GetBookmarkBarNode(); - EXPECT_TRUE(bookmarkBarNode); - const BookmarkNode* node = model->AddURL(bookmarkBarNode, - 0, - ASCIIToUTF16("short-title"), - GURL("http://www.google.com")); - const BookmarkNode* grandma = model->AddGroup(bookmarkBarNode, 0, - ASCIIToUTF16("grandma")); - EXPECT_TRUE(grandma); - const BookmarkNode* grandpa = model->AddGroup(bookmarkBarNode, 0, - ASCIIToUTF16("grandpa")); - EXPECT_TRUE(grandpa); - - BookmarkBubbleController* controller = ControllerForNode(node); - EXPECT_TRUE(controller); - - // simulate a user edit - [controller setTitle:@"oops" parentFolder:grandma]; - [controller edit:controller]; - - // Make sure bookmark has changed - EXPECT_EQ(node->GetTitle(), ASCIIToUTF16("oops")); - EXPECT_EQ(node->GetParent()->GetTitle(), ASCIIToUTF16("grandma")); -} - -// Confirm happiness with parent nodes that have the same name. -TEST_F(BookmarkBubbleControllerTest, TestNewParentSameName) { - BookmarkModel* model = GetBookmarkModel(); - EXPECT_TRUE(model); - const BookmarkNode* bookmarkBarNode = model->GetBookmarkBarNode(); - EXPECT_TRUE(bookmarkBarNode); - for (int i=0; i<2; i++) { - const BookmarkNode* node = model->AddURL(bookmarkBarNode, - 0, - ASCIIToUTF16("short-title"), - GURL("http://www.google.com")); - EXPECT_TRUE(node); - const BookmarkNode* group = model->AddGroup(bookmarkBarNode, 0, - ASCIIToUTF16("NAME")); - EXPECT_TRUE(group); - group = model->AddGroup(bookmarkBarNode, 0, ASCIIToUTF16("NAME")); - EXPECT_TRUE(group); - group = model->AddGroup(bookmarkBarNode, 0, ASCIIToUTF16("NAME")); - EXPECT_TRUE(group); - BookmarkBubbleController* controller = ControllerForNode(node); - EXPECT_TRUE(controller); - - // simulate a user edit - [controller setParentFolderSelection:bookmarkBarNode->GetChild(i)]; - [controller edit:controller]; - - // Make sure bookmark has changed, and that the parent is what we - // expect. This proves nobody did searching based on name. - EXPECT_EQ(node->GetParent(), bookmarkBarNode->GetChild(i)); - } -} - -// Confirm happiness with nodes with the same Name -TEST_F(BookmarkBubbleControllerTest, TestDuplicateNodeNames) { - BookmarkModel* model = GetBookmarkModel(); - const BookmarkNode* bookmarkBarNode = model->GetBookmarkBarNode(); - EXPECT_TRUE(bookmarkBarNode); - const BookmarkNode* node1 = model->AddGroup(bookmarkBarNode, 0, - ASCIIToUTF16("NAME")); - EXPECT_TRUE(node1); - const BookmarkNode* node2 = model->AddGroup(bookmarkBarNode, 0, - ASCIIToUTF16("NAME")); - EXPECT_TRUE(node2); - BookmarkBubbleController* controller = ControllerForNode(bookmarkBarNode); - EXPECT_TRUE(controller); - - NSPopUpButton* button = [controller folderPopUpButton]; - [controller setParentFolderSelection:node1]; - NSMenuItem* item = [button selectedItem]; - id itemObject = [item representedObject]; - EXPECT_NSEQ([NSValue valueWithPointer:node1], itemObject); - [controller setParentFolderSelection:node2]; - item = [button selectedItem]; - itemObject = [item representedObject]; - EXPECT_NSEQ([NSValue valueWithPointer:node2], itemObject); -} - -// Click the "remove" button -TEST_F(BookmarkBubbleControllerTest, TestRemove) { - BookmarkModel* model = GetBookmarkModel(); - GURL gurl("http://www.google.com"); - const BookmarkNode* node = model->AddURL(model->GetBookmarkBarNode(), - 0, - ASCIIToUTF16("Bookie markie title"), - gurl); - BookmarkBubbleController* controller = ControllerForNode(node); - EXPECT_TRUE(controller); - EXPECT_TRUE(model->IsBookmarked(gurl)); - - [controller remove:controller]; - EXPECT_FALSE(model->IsBookmarked(gurl)); - EXPECT_TRUE(IsWindowClosing()); -} - -// Confirm picking "choose another folder" caused edit: to be called. -TEST_F(BookmarkBubbleControllerTest, PopUpSelectionChanged) { - BookmarkModel* model = GetBookmarkModel(); - GURL gurl("http://www.google.com"); - const BookmarkNode* node = model->AddURL(model->GetBookmarkBarNode(), - 0, ASCIIToUTF16("super-title"), - gurl); - BookmarkBubbleController* controller = ControllerForNode(node); - EXPECT_TRUE(controller); - - NSPopUpButton* button = [controller folderPopUpButton]; - [button selectItemWithTitle:[[controller class] chooseAnotherFolderString]]; - EXPECT_EQ(edits_, 0); - [button sendAction:[button action] to:[button target]]; - EXPECT_EQ(edits_, 1); -} - -// Create a controller that simulates the bookmark just now being created by -// the user clicking the star, then sending the "cancel" command to represent -// them pressing escape. The bookmark should not be there. -TEST_F(BookmarkBubbleControllerTest, EscapeRemovesNewBookmark) { - BookmarkModel* model = GetBookmarkModel(); - GURL gurl("http://www.google.com"); - const BookmarkNode* node = model->AddURL(model->GetBookmarkBarNode(), - 0, - ASCIIToUTF16("Bookie markie title"), - gurl); - BookmarkBubbleController* controller = - [[BookmarkBubbleController alloc] - initWithParentWindow:test_window() - model:helper_.profile()->GetBookmarkModel() - node:node - alreadyBookmarked:NO]; // The last param is the key difference. - EXPECT_TRUE([controller window]); - // Calls release on controller. - [controller cancel:nil]; - EXPECT_FALSE(model->IsBookmarked(gurl)); -} - -// Create a controller where the bookmark already existed prior to clicking -// the star and test that sending a cancel command doesn't change the state -// of the bookmark. -TEST_F(BookmarkBubbleControllerTest, EscapeDoesntTouchExistingBookmark) { - BookmarkModel* model = GetBookmarkModel(); - GURL gurl("http://www.google.com"); - const BookmarkNode* node = model->AddURL(model->GetBookmarkBarNode(), - 0, - ASCIIToUTF16("Bookie markie title"), - gurl); - BookmarkBubbleController* controller = ControllerForNode(node); - EXPECT_TRUE(controller); - - [(id)controller cancel:nil]; - EXPECT_TRUE(model->IsBookmarked(gurl)); -} - -// Confirm indentation of items in pop-up menu -TEST_F(BookmarkBubbleControllerTest, TestMenuIndentation) { - // Create some folders, including a nested folder - BookmarkModel* model = GetBookmarkModel(); - EXPECT_TRUE(model); - const BookmarkNode* bookmarkBarNode = model->GetBookmarkBarNode(); - EXPECT_TRUE(bookmarkBarNode); - const BookmarkNode* node1 = model->AddGroup(bookmarkBarNode, 0, - ASCIIToUTF16("one")); - EXPECT_TRUE(node1); - const BookmarkNode* node2 = model->AddGroup(bookmarkBarNode, 1, - ASCIIToUTF16("two")); - EXPECT_TRUE(node2); - const BookmarkNode* node2_1 = model->AddGroup(node2, 0, - ASCIIToUTF16("two dot one")); - EXPECT_TRUE(node2_1); - const BookmarkNode* node3 = model->AddGroup(bookmarkBarNode, 2, - ASCIIToUTF16("three")); - EXPECT_TRUE(node3); - - BookmarkBubbleController* controller = ControllerForNode(node1); - EXPECT_TRUE(controller); - - // Compare the menu item indents against expectations. - static const int kExpectedIndent[] = {0, 1, 1, 2, 1, 0}; - NSArray* items = [[controller folderPopUpButton] itemArray]; - ASSERT_GE([items count], 6U); - for(int itemNo = 0; itemNo < 6; itemNo++) { - NSMenuItem* item = [items objectAtIndex:itemNo]; - EXPECT_EQ(kExpectedIndent[itemNo], [item indentationLevel]) - << "Unexpected indent for menu item #" << itemNo; - } -} - -// Confirm bubble goes away when a new tab is created. -TEST_F(BookmarkBubbleControllerTest, BubbleGoesAwayOnNewTab) { - - BookmarkModel* model = GetBookmarkModel(); - const BookmarkNode* node = model->AddURL(model->GetBookmarkBarNode(), - 0, - ASCIIToUTF16("Bookie markie title"), - GURL("http://www.google.com")); - EXPECT_EQ(edits_, 0); - - BookmarkBubbleController* controller = ControllerForNode(node); - EXPECT_TRUE(controller); - EXPECT_FALSE(IsWindowClosing()); - - // We can't actually create a new tab here, e.g. - // helper_.browser()->AddTabWithURL(...); - // Many of our browser objects (Browser, Profile, RequestContext) - // are "just enough" to run tests without being complete. Instead - // we fake the notification that would be triggered by a tab - // creation. - NotificationService::current()->Notify( - NotificationType::TAB_CONTENTS_CONNECTED, - Source<TabContentsDelegate>(NULL), - Details<TabContents>(NULL)); - - // Confirm bubble going bye-bye. - EXPECT_TRUE(IsWindowClosing()); -} - - -} // namespace - -@implementation NSApplication (BookmarkBubbleUnitTest) -// Add handler for the editBookmarkNode: action to NSApp for testing purposes. -// Normally this would be sent up the responder tree correctly, but since -// tests run in the background, key window and main window are never set on -// NSApplication. Adding it to NSApplication directly removes the need for -// worrying about what the current window with focus is. -- (void)editBookmarkNode:(id)sender { - EXPECT_TRUE([sender respondsToSelector:@selector(node)]); - BookmarkBubbleControllerTest::edits_++; -} - -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_button.h b/chrome/browser/cocoa/bookmarks/bookmark_button.h deleted file mode 100644 index 5d8574c..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_button.h +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> -#include <vector> -#import "chrome/browser/cocoa/draggable_button.h" -#include "webkit/glue/window_open_disposition.h" - -@class BookmarkBarFolderController; -@class BookmarkButton; -struct BookmarkNodeData; -class BookmarkModel; -class BookmarkNode; -@class BrowserWindowController; -class ThemeProvider; - -// Protocol for a BookmarkButton's delegate, responsible for doing -// things on behalf of a bookmark button. -@protocol BookmarkButtonDelegate - -// Fill the given pasteboard with appropriate data when the given button is -// dragged. Since the delegate has no way of providing pasteboard data later, -// all data must actually be put into the pasteboard and not merely promised. -- (void)fillPasteboard:(NSPasteboard*)pboard - forDragOfButton:(BookmarkButton*)button; - -// Bookmark buttons pass mouseEntered: and mouseExited: events to -// their delegate. This allows the delegate to decide (for example) -// which one, if any, should perform a hover-open. -- (void)mouseEnteredButton:(id)button event:(NSEvent*)event; -- (void)mouseExitedButton:(id)button event:(NSEvent*)event; - -// Returns YES if a drag operation should lock the fullscreen overlay bar -// visibility before starting. For example, dragging a bookmark button should -// not lock the overlay if the bookmark bar is currently showing in detached -// mode on the NTP. -- (BOOL)dragShouldLockBarVisibility; - -// Returns the top-level window for this button. -- (NSWindow*)browserWindow; - -// Returns YES if the bookmark button can be dragged to the trash, NO otherwise. -- (BOOL)canDragBookmarkButtonToTrash:(BookmarkButton*)button; - -// This is called after the user has dropped the bookmark button on the trash. -// The delegate can use this event to delete the bookmark. -- (void)didDragBookmarkToTrash:(BookmarkButton*)button; - -@end - - -// Protocol to be implemented by controllers that logically own -// bookmark buttons. The controller may be either an NSViewController -// or NSWindowController. The BookmarkButton doesn't use this -// protocol directly; it is used when BookmarkButton controllers talk -// to each other. -// -// Other than the top level owner (the bookmark bar), all bookmark -// button controllers have a parent controller. -@protocol BookmarkButtonControllerProtocol - -// Close all bookmark folders, walking up the ownership chain. -- (void)closeAllBookmarkFolders; - -// Close just my bookmark folder. -- (void)closeBookmarkFolder:(id)sender; - -// Return the bookmark model for this controller. -- (BookmarkModel*)bookmarkModel; - -// Perform drag enter/exit operations, such as hover-open and hover-close. -- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info; -- (void)draggingExited:(id<NSDraggingInfo>)info; - -// Returns YES if a drag operation should lock the fullscreen overlay bar -// visibility before starting. For example, dragging a bookmark button should -// not lock the overlay if the bookmark bar is currently showing in detached -// mode on the NTP. -- (BOOL)dragShouldLockBarVisibility; - -// Perform the actual DnD of a bookmark or bookmark button. - -// |point| is in the base coordinate system of the destination window; -// |it comes from an id<NSDraggingInfo>. |copy| is YES if a copy is to be -// made and inserted into the new location while leaving the bookmark in -// the old location, otherwise move the bookmark by removing from its old -// location and inserting into the new location. -- (BOOL)dragButton:(BookmarkButton*)sourceButton - to:(NSPoint)point - copy:(BOOL)copy; - -// Determine if the pasteboard from |info| has dragging data containing -// bookmark(s) and perform the drag and return YES, otherwise return NO. -- (BOOL)dragBookmarkData:(id<NSDraggingInfo>)info; - -// Determine if the drag pasteboard has any drag data of type -// kBookmarkDictionaryListPboardType and, if so, return those elements -// otherwise return an empty vector. -- (std::vector<const BookmarkNode*>)retrieveBookmarkNodeData; - -// Return YES if we should show the drop indicator, else NO. In some -// cases (e.g. hover open) we don't want to show the drop indicator. -// |point| is in the base coordinate system of the destination window; -// |it comes from an id<NSDraggingInfo>. -- (BOOL)shouldShowIndicatorShownForPoint:(NSPoint)point; - -// The x or y coordinate of (the middle of) the indicator to draw for -// a drag of the source button to the given point (given in window -// coordinates). -// |point| is in the base coordinate system of the destination window; -// |it comes from an id<NSDraggingInfo>. -// TODO(viettrungluu,jrg): instead of this, make buttons move around. -// http://crbug.com/35968 -- (CGFloat)indicatorPosForDragToPoint:(NSPoint)point; - -// Return the theme provider associated with this browser window. -- (ThemeProvider*)themeProvider; - -// Called just before a child folder puts itself on screen. -- (void)childFolderWillShow:(id<BookmarkButtonControllerProtocol>)child; - -// Called just before a child folder closes. -- (void)childFolderWillClose:(id<BookmarkButtonControllerProtocol>)child; - -// Return a controller's folder controller for a subfolder, or nil. -- (BookmarkBarFolderController*)folderController; - -// Add a new folder controller as triggered by the given folder button. -// If there is a current folder controller, close it. -- (void)addNewFolderControllerWithParentButton:(BookmarkButton*)parentButton; - -// Open all of the nodes for the given node with disposition. -- (void)openAll:(const BookmarkNode*)node - disposition:(WindowOpenDisposition)disposition; - -// There are several operations which may affect the contents of a bookmark -// button controller after it has been created, primary of which are -// cut/paste/delete and drag/drop. Such changes may involve coordinating -// the bookmark button contents of two controllers (such as when a bookmark is -// dragged from one folder to another). The bookmark bar controller -// coordinates in response to notifications propogated by the bookmark model -// through BookmarkBarBridge calls. The following three functions are -// implemented by the controllers and are dispatched by the bookmark bar -// controller in response to notifications coming in from the BookmarkBarBridge. - -// Add a button for the given node to the bar or folder menu. This is safe -// to call when a folder menu window is open as that window will be updated. -// And index of -1 means to append to the end (bottom). -- (void)addButtonForNode:(const BookmarkNode*)node - atIndex:(NSInteger)buttonIndex; - -// Given a list or |urls| and |titles|, create new bookmark nodes and add -// them to the bookmark model such that they will be 1) added to the folder -// represented by the button at |point| if it is a folder, or 2) inserted -// into the parent of the non-folder bookmark at |point| in front of that -// button. Returns YES if at least one bookmark was added. -// TODO(mrossetti): Change function to use a pair-like structure for -// URLs and titles. http://crbug.com/44411 -- (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point; - -// Move a button from one place in the menu to another. This is safe -// to call when a folder menu window is open as that window will be updated. -- (void)moveButtonFromIndex:(NSInteger)fromIndex toIndex:(NSInteger)toIndex; - -// Remove the bookmark button at the given index. Show the poof animation -// if |animate:| is YES. It may be obvious, but this is safe -// to call when a folder menu window is open as that window will be updated. -- (void)removeButton:(NSInteger)buttonIndex animate:(BOOL)poof; - -// Determine the controller containing the button representing |node|, if any. -- (id<BookmarkButtonControllerProtocol>)controllerForNode: - (const BookmarkNode*)node; - -@end // @protocol BookmarkButtonControllerProtocol - - -// Class for bookmark bar buttons that can be drag sources. -@interface BookmarkButton : DraggableButton { - @private - IBOutlet NSObject<BookmarkButtonDelegate>* delegate_; // Weak. - - // Saved pointer to the BWC for the browser window that contains this button. - // Used to lock and release bar visibility during a drag. The pointer is - // saved because the bookmark button is no longer a part of a window at the - // end of a drag operation (or, in fact, can be dragged to a completely - // different window), so there is no way to retrieve the same BWC object after - // a drag. - BrowserWindowController* visibilityDelegate_; // weak - - NSPoint dragMouseOffset_; - NSPoint dragEndScreenLocation_; - BOOL dragPending_; -} - -@property(assign, nonatomic) NSObject<BookmarkButtonDelegate>* delegate; - -// Return the bookmark node associated with this button, or NULL. -- (const BookmarkNode*)bookmarkNode; - -// Return YES if this is a folder button (the node has subnodes). -- (BOOL)isFolder; - -// At this time we represent an empty folder (e.g. the string -// '(empty)') as a disabled button with no associated node. -// -// TODO(jrg): improve; things work but are slightly ugly since "empty" -// and "one disabled button" are not the same thing. -// http://crbug.com/35967 -- (BOOL)isEmpty; - -// Turn on or off pulsing of a bookmark button. -// Triggered by the bookmark bubble. -- (void)setIsContinuousPulsing:(BOOL)flag; - -// Return continuous pulse state. -- (BOOL)isContinuousPulsing; - -// Return the location in screen coordinates where the remove animation should -// be displayed. -- (NSPoint)screenLocationForRemoveAnimation; - -@end // @interface BookmarkButton - - -@interface BookmarkButton(TestingAPI) -- (void)beginDrag:(NSEvent*)event; -@end - -namespace bookmark_button { - -// Notifications for pulsing of bookmarks. -extern NSString* const kPulseBookmarkButtonNotification; - -// Key for userInfo dict of a kPulseBookmarkButtonNotification. -// Value is a [NSValue valueWithPointer:]; pointer is a (const BookmarkNode*). -extern NSString* const kBookmarkKey; - -// Key for userInfo dict of a kPulseBookmarkButtonNotification. -// Value is a [NSNumber numberWithBool:] to turn pulsing on or off. -extern NSString* const kBookmarkPulseFlagKey; - -}; diff --git a/chrome/browser/cocoa/bookmarks/bookmark_button.mm b/chrome/browser/cocoa/bookmarks/bookmark_button.mm deleted file mode 100644 index 9728e67..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_button.mm +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_button.h" - -#include "base/logging.h" -#import "base/scoped_nsobject.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button_cell.h" -#import "chrome/browser/cocoa/browser_window_controller.h" -#import "chrome/browser/cocoa/view_id_util.h" -#include "chrome/browser/metrics/user_metrics.h" - -// The opacity of the bookmark button drag image. -static const CGFloat kDragImageOpacity = 0.7; - - -namespace bookmark_button { - -NSString* const kPulseBookmarkButtonNotification = - @"PulseBookmarkButtonNotification"; -NSString* const kBookmarkKey = @"BookmarkKey"; -NSString* const kBookmarkPulseFlagKey = @"BookmarkPulseFlagKey"; - -}; - -@interface BookmarkButton(Private) - -// Make a drag image for the button. -- (NSImage*)dragImage; - -@end // @interface BookmarkButton(Private) - - -@implementation BookmarkButton - -@synthesize delegate = delegate_; - -- (id)initWithFrame:(NSRect)frameRect { - // BookmarkButton's ViewID may be changed to VIEW_ID_OTHER_BOOKMARKS in - // BookmarkBarController, so we can't just override -viewID method to return - // it. - if ((self = [super initWithFrame:frameRect])) - view_id_util::SetID(self, VIEW_ID_BOOKMARK_BAR_ELEMENT); - return self; -} - -- (void)dealloc { - if ([[self cell] respondsToSelector:@selector(safelyStopPulsing)]) - [[self cell] safelyStopPulsing]; - view_id_util::UnsetID(self); - [super dealloc]; -} - -- (const BookmarkNode*)bookmarkNode { - return [[self cell] bookmarkNode]; -} - -- (BOOL)isFolder { - const BookmarkNode* node = [self bookmarkNode]; - return (node && node->is_folder()); -} - -- (BOOL)isEmpty { - return [self bookmarkNode] ? NO : YES; -} - -- (void)setIsContinuousPulsing:(BOOL)flag { - [[self cell] setIsContinuousPulsing:flag]; -} - -- (BOOL)isContinuousPulsing { - return [[self cell] isContinuousPulsing]; -} - -- (NSPoint)screenLocationForRemoveAnimation { - NSPoint point; - - if (dragPending_) { - // Use the position of the mouse in the drag image as the location. - point = dragEndScreenLocation_; - point.x += dragMouseOffset_.x; - if ([self isFlipped]) { - point.y += [self bounds].size.height - dragMouseOffset_.y; - } else { - point.y += dragMouseOffset_.y; - } - } else { - // Use the middle of this button as the location. - NSRect bounds = [self bounds]; - point = NSMakePoint(NSMidX(bounds), NSMidY(bounds)); - point = [self convertPoint:point toView:nil]; - point = [[self window] convertBaseToScreen:point]; - } - - return point; -} - -// By default, NSButton ignores middle-clicks. -// But we want them. -- (void)otherMouseUp:(NSEvent*)event { - [self performClick:self]; -} - -// Overridden from DraggableButton. -- (void)beginDrag:(NSEvent*)event { - // Don't allow a drag of the empty node. - // The empty node is a placeholder for "(empty)", to be revisited. - if ([self isEmpty]) - return; - - if (![self delegate]) { - NOTREACHED(); - return; - } - // Ask our delegate to fill the pasteboard for us. - NSPasteboard* pboard = [NSPasteboard pasteboardWithName:NSDragPboard]; - [[self delegate] fillPasteboard:pboard forDragOfButton:self]; - - // At the moment, moving bookmarks causes their buttons (like me!) - // to be destroyed and rebuilt. Make sure we don't go away while on - // the stack. - [self retain]; - - // Lock bar visibility, forcing the overlay to stay visible if we are in - // fullscreen mode. - if ([[self delegate] dragShouldLockBarVisibility]) { - DCHECK(!visibilityDelegate_); - NSWindow* window = [[self delegate] browserWindow]; - visibilityDelegate_ = - [BrowserWindowController browserWindowControllerForWindow:window]; - [visibilityDelegate_ lockBarVisibilityForOwner:self - withAnimation:NO - delay:NO]; - } - const BookmarkNode* node = [self bookmarkNode]; - const BookmarkNode* parent = node ? node->GetParent() : NULL; - if (parent && parent->type() == BookmarkNode::FOLDER) { - UserMetrics::RecordAction(UserMetricsAction("BookmarkBarFolder_DragStart")); - } else { - UserMetrics::RecordAction(UserMetricsAction("BookmarkBar_DragStart")); - } - - dragMouseOffset_ = [self convertPointFromBase:[event locationInWindow]]; - dragPending_ = YES; - - CGFloat yAt = [self bounds].size.height; - NSSize dragOffset = NSMakeSize(0.0, 0.0); - [self dragImage:[self dragImage] at:NSMakePoint(0, yAt) offset:dragOffset - event:event pasteboard:pboard source:self slideBack:YES]; - - // And we're done. - dragPending_ = NO; - [self autorelease]; -} - -// Overridden to release bar visibility. -- (void)endDrag { - // visibilityDelegate_ can be nil if we're detached, and that's fine. - [visibilityDelegate_ releaseBarVisibilityForOwner:self - withAnimation:YES - delay:YES]; - visibilityDelegate_ = nil; - [super endDrag]; -} - -- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal { - NSDragOperation operation = NSDragOperationCopy; - if (isLocal) { - operation |= NSDragOperationMove; - } - if ([delegate_ canDragBookmarkButtonToTrash:self]) { - operation |= NSDragOperationDelete; - } - return operation; -} - -- (void)draggedImage:(NSImage *)anImage - endedAt:(NSPoint)aPoint - operation:(NSDragOperation)operation { - if (operation & NSDragOperationDelete) { - dragEndScreenLocation_ = aPoint; - [delegate_ didDragBookmarkToTrash:self]; - } -} - -// mouseEntered: and mouseExited: are called from our -// BookmarkButtonCell. We redirect this information to our delegate. -// The controller can then perform menu-like actions (e.g. "hover over -// to open menu"). -- (void)mouseEntered:(NSEvent*)event { - [delegate_ mouseEnteredButton:self event:event]; -} - -// See comments above mouseEntered:. -- (void)mouseExited:(NSEvent*)event { - [delegate_ mouseExitedButton:self event:event]; -} - -@end - -@implementation BookmarkButton(Private) - -- (NSImage*)dragImage { - NSRect bounds = [self bounds]; - - // Grab the image from the screen and put it in an |NSImage|. We can't use - // this directly since we need to clip it and set its opacity. This won't work - // if the source view is clipped. Fortunately, we don't display clipped - // bookmark buttons. - [self lockFocus]; - scoped_nsobject<NSBitmapImageRep> - bitmap([[NSBitmapImageRep alloc] initWithFocusedViewRect:bounds]); - [self unlockFocus]; - scoped_nsobject<NSImage> image([[NSImage alloc] initWithSize:[bitmap size]]); - [image addRepresentation:bitmap]; - - // Make an autoreleased |NSImage|, which will be returned, and draw into it. - // By default, the |NSImage| will be completely transparent. - NSImage* dragImage = - [[[NSImage alloc] initWithSize:[bitmap size]] autorelease]; - [dragImage lockFocus]; - - // Draw the image with the appropriate opacity, clipping it tightly. - GradientButtonCell* cell = static_cast<GradientButtonCell*>([self cell]); - DCHECK([cell isKindOfClass:[GradientButtonCell class]]); - [[cell clipPathForFrame:bounds inView:self] setClip]; - [image drawAtPoint:NSMakePoint(0, 0) - fromRect:NSMakeRect(0, 0, NSWidth(bounds), NSHeight(bounds)) - operation:NSCompositeSourceOver - fraction:kDragImageOpacity]; - - [dragImage unlockFocus]; - return dragImage; -} - -@end // @implementation BookmarkButton(Private) diff --git a/chrome/browser/cocoa/bookmarks/bookmark_button_cell.h b/chrome/browser/cocoa/bookmarks/bookmark_button_cell.h deleted file mode 100644 index 45d83e3..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_button_cell.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BUTTON_CELL_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BUTTON_CELL_H_ -#pragma once - -#import "base/cocoa_protocols_mac.h" -#import "chrome/browser/cocoa/gradient_button_cell.h" - -class BookmarkNode; - -// A button cell that handles drawing/highlighting of buttons in the -// bookmark bar. This cell forwards mouseEntered/mouseExited events -// to its control view so that pseudo-menu operations -// (e.g. hover-over to open) can be implemented. -@interface BookmarkButtonCell : GradientButtonCell<NSMenuDelegate> { - @private - BOOL empty_; // is this an "empty" button placeholder button cell? - - // Starting index of bookmarkFolder children that we care to use. - int startingChildIndex_; - - // Should we draw the folder arrow as needed? Not used for the bar - // itself but used on the folder windows. - BOOL drawFolderArrow_; - - // Arrow for folders - scoped_nsobject<NSImage> arrowImage_; -} - -@property (nonatomic, readwrite, assign) const BookmarkNode* bookmarkNode; -@property (nonatomic, readwrite, assign) int startingChildIndex; -@property (nonatomic, readwrite, assign) BOOL drawFolderArrow; - -// Create a button cell which draws with a theme. -+ (id)buttonCellForNode:(const BookmarkNode*)node - contextMenu:(NSMenu*)contextMenu - cellText:(NSString*)cellText - cellImage:(NSImage*)cellImage; - -// Initialize a button cell which draws with a theme. -// Designated initializer. -- (id)initForNode:(const BookmarkNode*)node - contextMenu:(NSMenu*)contextMenu - cellText:(NSString*)cellText - cellImage:(NSImage*)cellImage; - -- (BOOL)empty; // returns YES if empty. -- (void)setEmpty:(BOOL)empty; - -// |-setBookmarkCellText:image:| is used to set the text and image of -// a BookmarkButtonCell, and align the image to the left (NSImageLeft) -// if there is text in the title, and centered (NSImageCenter) if -// there is not. If |title| is nil, do not reset the title. -- (void)setBookmarkCellText:(NSString*)title - image:(NSImage*)image; - -// Set the color of text in this cell. -- (void)setTextColor:(NSColor*)color; - -@end - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_BUTTON_CELL_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_button_cell.mm b/chrome/browser/cocoa/bookmarks/bookmark_button_cell.mm deleted file mode 100644 index 8cb59e9..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_button_cell.mm +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_button_cell.h" - -#include "app/l10n_util_mac.h" -#include "base/logging.h" -#include "base/nsimage_cache_mac.h" -#include "base/sys_string_conversions.h" -#import "chrome/browser/bookmarks/bookmark_model.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_menu.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button.h" -#import "chrome/browser/cocoa/image_utils.h" -#include "chrome/browser/metrics/user_metrics.h" -#include "grit/generated_resources.h" - - -@interface BookmarkButtonCell(Private) -- (void)configureBookmarkButtonCell; -@end - - -@implementation BookmarkButtonCell - -@synthesize startingChildIndex = startingChildIndex_; -@synthesize drawFolderArrow = drawFolderArrow_; - -+ (id)buttonCellForNode:(const BookmarkNode*)node - contextMenu:(NSMenu*)contextMenu - cellText:(NSString*)cellText - cellImage:(NSImage*)cellImage { - id buttonCell = - [[[BookmarkButtonCell alloc] initForNode:node - contextMenu:contextMenu - cellText:cellText - cellImage:cellImage] - autorelease]; - return buttonCell; -} - -- (id)initForNode:(const BookmarkNode*)node - contextMenu:(NSMenu*)contextMenu - cellText:(NSString*)cellText - cellImage:(NSImage*)cellImage { - if ((self = [super initTextCell:cellText])) { - [self configureBookmarkButtonCell]; - - [self setBookmarkNode:node]; - - if (node) { - NSString* title = base::SysUTF16ToNSString(node->GetTitle()); - [self setBookmarkCellText:title image:cellImage]; - [self setMenu:contextMenu]; - } else { - [self setEmpty:YES]; - [self setBookmarkCellText:l10n_util::GetNSString(IDS_MENU_EMPTY_SUBMENU) - image:nil]; - } - } - - return self; -} - -- (id)initTextCell:(NSString*)string { - return [self initForNode:nil contextMenu:nil cellText:string cellImage:nil]; -} - -// Used by the off-the-side menu, the only case where a -// BookmarkButtonCell is loaded from a nib. -- (void)awakeFromNib { - [self configureBookmarkButtonCell]; -} - -// Perform all normal init routines specific to the BookmarkButtonCell. -- (void)configureBookmarkButtonCell { - [self setButtonType:NSMomentaryPushInButton]; - [self setBezelStyle:NSShadowlessSquareBezelStyle]; - [self setShowsBorderOnlyWhileMouseInside:YES]; - [self setControlSize:NSSmallControlSize]; - [self setAlignment:NSLeftTextAlignment]; - [self setFont:[NSFont systemFontOfSize:[NSFont smallSystemFontSize]]]; - [self setWraps:NO]; - // NSLineBreakByTruncatingMiddle seems more common on OSX but let's - // try to match Windows for a bit to see what happens. - [self setLineBreakMode:NSLineBreakByTruncatingTail]; - - // Theming doesn't work for bookmark buttons yet (cell text is chucked). - [super setShouldTheme:NO]; -} - -- (BOOL)empty { - return empty_; -} - -- (void)setEmpty:(BOOL)empty { - empty_ = empty; - [self setShowsBorderOnlyWhileMouseInside:!empty]; -} - -- (NSSize)cellSizeForBounds:(NSRect)aRect { - NSSize size = [super cellSizeForBounds:aRect]; - // Cocoa seems to slightly underestimate how much space we need, so we - // compensate here to avoid a clipped rendering. - size.width += 2; - size.height += 4; - return size; -} - -- (void)setBookmarkCellText:(NSString*)title - image:(NSImage*)image { - title = [title stringByReplacingOccurrencesOfString:@"\n" - withString:@" "]; - title = [title stringByReplacingOccurrencesOfString:@"\r" - withString:@" "]; - // If there is no title, squeeze things tight by displaying only the image; by - // default, Cocoa leaves extra space in an attempt to display an empty title. - if ([title length]) { - [self setImagePosition:NSImageLeft]; - [self setTitle:title]; - } else { - [self setImagePosition:NSImageOnly]; - } - - if (image) - [self setImage:image]; -} - -- (void)setBookmarkNode:(const BookmarkNode*)node { - [self setRepresentedObject:[NSValue valueWithPointer:node]]; -} - -- (const BookmarkNode*)bookmarkNode { - return static_cast<const BookmarkNode*>([[self representedObject] - pointerValue]); -} - -// We share the context menu among all bookmark buttons. To allow us -// to disambiguate when needed (e.g. "open bookmark"), we set the -// menu's associated bookmark node ID to be our represented object. -- (NSMenu*)menu { - if (empty_) - return nil; - BookmarkMenu* menu = (BookmarkMenu*)[super menu]; - const BookmarkNode* node = - static_cast<const BookmarkNode*>([[self representedObject] pointerValue]); - - if (node->GetParent() && node->GetParent()->type() == BookmarkNode::FOLDER) { - UserMetrics::RecordAction(UserMetricsAction("BookmarkBarFolder_CtxMenu")); - } else { - UserMetrics::RecordAction(UserMetricsAction("BookmarkBar_CtxMenu")); - } - - [menu setRepresentedObject:[NSNumber numberWithLongLong:node->id()]]; - - return menu; -} - -// Unfortunately, NSCell doesn't already have something like this. -// TODO(jrg): consider placing in GTM. -- (void)setTextColor:(NSColor*)color { - - // We can't properly set the cell's text color without a control. - // In theory we could just save the next for later and wait until - // the cell is moved to a control, but there is no obvious way to - // accomplish that (e.g. no "cellDidMoveToControl" notification.) - DCHECK([self controlView]); - - scoped_nsobject<NSMutableParagraphStyle> style([NSMutableParagraphStyle new]); - [style setAlignment:NSLeftTextAlignment]; - NSDictionary* dict = [NSDictionary - dictionaryWithObjectsAndKeys:color, - NSForegroundColorAttributeName, - [self font], NSFontAttributeName, - style.get(), NSParagraphStyleAttributeName, - nil]; - scoped_nsobject<NSAttributedString> ats([[NSAttributedString alloc] - initWithString:[self title] - attributes:dict]); - NSButton* button = static_cast<NSButton*>([self controlView]); - if (button) { - DCHECK([button isKindOfClass:[NSButton class]]); - [button setAttributedTitle:ats.get()]; - } -} - -// To implement "hover open a bookmark button to open the folder" -// which feels like menus, we override NSButtonCell's mouseEntered: -// and mouseExited:, then and pass them along to our owning control. -// Note: as verified in a debugger, mouseEntered: does NOT increase -// the retainCount of the cell or its owning control. -- (void)mouseEntered:(NSEvent*)event { - [super mouseEntered:event]; - [[self controlView] mouseEntered:event]; -} - -// See comment above mouseEntered:, above. -- (void)mouseExited:(NSEvent*)event { - [[self controlView] mouseExited:event]; - [super mouseExited:event]; -} - -- (void)setDrawFolderArrow:(BOOL)draw { - drawFolderArrow_ = draw; - if (draw && !arrowImage_) { - arrowImage_.reset([nsimage_cache::ImageNamed(@"menu_hierarchy_arrow.pdf") - retain]); - } -} - -// Add extra size for the arrow so it doesn't overlap the text. -// Does not sanity check to be sure this is actually a folder node. -- (NSSize)cellSize { - NSSize cellSize = [super cellSize]; - if (drawFolderArrow_) { - cellSize.width += [arrowImage_ size].width; // plus margin? - } - return cellSize; -} - -// Override cell drawing to add a submenu arrow like a real menu. -- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { - // First draw "everything else". - [super drawInteriorWithFrame:cellFrame inView:controlView]; - - // If asked to do so, and if a folder, draw the arrow. - if (!drawFolderArrow_) - return; - BookmarkButton* button = static_cast<BookmarkButton*>([self controlView]); - DCHECK([button respondsToSelector:@selector(isFolder)]); - if ([button isFolder]) { - NSRect imageRect = NSZeroRect; - imageRect.size = [arrowImage_ size]; - NSRect drawRect = NSOffsetRect(imageRect, - NSWidth(cellFrame) - NSWidth(imageRect), - (NSHeight(cellFrame) / 2.0) - - (NSHeight(imageRect) / 2.0)); - [arrowImage_ drawInRect:drawRect - fromRect:imageRect - operation:NSCompositeSourceOver - fraction:[self isEnabled] ? 1.0 : 0.5 - neverFlipped:YES]; - } -} - -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_button_cell_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_button_cell_unittest.mm deleted file mode 100644 index 1c4e89f..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_button_cell_unittest.mm +++ /dev/null @@ -1,183 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "app/resource_bundle.h" -#include "base/scoped_nsobject.h" -#include "base/utf_string_conversions.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button_cell.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_menu.h" -#include "chrome/browser/cocoa/browser_test_helper.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" -#include "grit/app_resources.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" - -// Simple class to remember how many mouseEntered: and mouseExited: -// calls it gets. Only used by BookmarkMouseForwarding but placed -// at the top of the file to keep it outside the anon namespace. -@interface ButtonRemembersMouseEnterExit : NSButton { - @public - int enters_; - int exits_; -} -@end - -@implementation ButtonRemembersMouseEnterExit -- (void)mouseEntered:(NSEvent*)event { - enters_++; -} -- (void)mouseExited:(NSEvent*)event { - exits_++; -} -@end - - -namespace { - -class BookmarkButtonCellTest : public CocoaTest { - public: - BrowserTestHelper helper_; -}; - -// Make sure it's not totally bogus -TEST_F(BookmarkButtonCellTest, SizeForBounds) { - NSRect frame = NSMakeRect(0, 0, 50, 30); - scoped_nsobject<NSButton> view([[NSButton alloc] initWithFrame:frame]); - scoped_nsobject<BookmarkButtonCell> cell( - [[BookmarkButtonCell alloc] initTextCell:@"Testing"]); - [view setCell:cell.get()]; - [[test_window() contentView] addSubview:view]; - - NSRect r = NSMakeRect(0, 0, 100, 100); - NSSize size = [cell.get() cellSizeForBounds:r]; - EXPECT_TRUE(size.width > 0 && size.height > 0); - EXPECT_TRUE(size.width < 200 && size.height < 200); -} - -// Make sure icon-only buttons are squeezed tightly. -TEST_F(BookmarkButtonCellTest, IconOnlySqueeze) { - NSRect frame = NSMakeRect(0, 0, 50, 30); - scoped_nsobject<NSButton> view([[NSButton alloc] initWithFrame:frame]); - scoped_nsobject<BookmarkButtonCell> cell( - [[BookmarkButtonCell alloc] initTextCell:@"Testing"]); - [view setCell:cell.get()]; - [[test_window() contentView] addSubview:view]; - - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - scoped_nsobject<NSImage> image([rb.GetNativeImageNamed(IDR_DEFAULT_FAVICON) - retain]); - EXPECT_TRUE(image.get()); - - NSRect r = NSMakeRect(0, 0, 100, 100); - [cell setBookmarkCellText:@" " image:image]; - CGFloat two_space_width = [cell.get() cellSizeForBounds:r].width; - [cell setBookmarkCellText:@" " image:image]; - CGFloat one_space_width = [cell.get() cellSizeForBounds:r].width; - [cell setBookmarkCellText:@"" image:image]; - CGFloat zero_space_width = [cell.get() cellSizeForBounds:r].width; - - // Make sure the switch to "no title" is more significant than we - // would otherwise see by decreasing the length of the title. - CGFloat delta1 = two_space_width - one_space_width; - CGFloat delta2 = one_space_width - zero_space_width; - EXPECT_GT(delta2, delta1); - -} - -// Make sure the default from the base class is overridden. -TEST_F(BookmarkButtonCellTest, MouseEnterStuff) { - scoped_nsobject<BookmarkButtonCell> cell( - [[BookmarkButtonCell alloc] initTextCell:@"Testing"]); - // Setting the menu should have no affect since we either share or - // dynamically compose the menu given a node. - [cell setMenu:[[[BookmarkMenu alloc] initWithTitle:@"foo"] autorelease]]; - EXPECT_FALSE([cell menu]); - - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* node = model->GetBookmarkBarNode(); - [cell setEmpty:NO]; - [cell setBookmarkNode:node]; - EXPECT_TRUE([cell showsBorderOnlyWhileMouseInside]); - EXPECT_TRUE([cell menu]); - - [cell setEmpty:YES]; - EXPECT_FALSE([cell.get() showsBorderOnlyWhileMouseInside]); - EXPECT_FALSE([cell menu]); -} - -TEST_F(BookmarkButtonCellTest, BookmarkNode) { - BookmarkModel& model(*(helper_.profile()->GetBookmarkModel())); - scoped_nsobject<BookmarkButtonCell> cell( - [[BookmarkButtonCell alloc] initTextCell:@"Testing"]); - - const BookmarkNode* node = model.GetBookmarkBarNode(); - [cell setBookmarkNode:node]; - EXPECT_EQ(node, [cell bookmarkNode]); - - node = model.other_node(); - [cell setBookmarkNode:node]; - EXPECT_EQ(node, [cell bookmarkNode]); -} - -TEST_F(BookmarkButtonCellTest, BookmarkMouseForwarding) { - scoped_nsobject<BookmarkButtonCell> cell( - [[BookmarkButtonCell alloc] initTextCell:@"Testing"]); - scoped_nsobject<ButtonRemembersMouseEnterExit> - button([[ButtonRemembersMouseEnterExit alloc] - initWithFrame:NSMakeRect(0,0,50,50)]); - [button setCell:cell.get()]; - EXPECT_EQ(0, button.get()->enters_); - EXPECT_EQ(0, button.get()->exits_); - NSEvent* event = [NSEvent mouseEventWithType:NSMouseMoved - location:NSMakePoint(10,10) - modifierFlags:0 - timestamp:0 - windowNumber:0 - context:nil - eventNumber:0 - clickCount:0 - pressure:0]; - [cell mouseEntered:event]; - EXPECT_TRUE(button.get()->enters_ && !button.get()->exits_); - - for (int i = 0; i < 3; i++) - [cell mouseExited:event]; - EXPECT_EQ(button.get()->enters_, 1); - EXPECT_EQ(button.get()->exits_, 3); -} - -// Confirms a cell created in a nib is initialized properly -TEST_F(BookmarkButtonCellTest, Awake) { - scoped_nsobject<BookmarkButtonCell> cell([[BookmarkButtonCell alloc] init]); - [cell awakeFromNib]; - EXPECT_EQ(NSLeftTextAlignment, [cell alignment]); -} - -// Subfolder arrow details. -TEST_F(BookmarkButtonCellTest, FolderArrow) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* bar = model->GetBookmarkBarNode(); - const BookmarkNode* node = model->AddURL(bar, bar->GetChildCount(), - ASCIIToUTF16("title"), - GURL("http://www.google.com")); - scoped_nsobject<BookmarkButtonCell> cell( - [[BookmarkButtonCell alloc] initForNode:node - contextMenu:nil - cellText:@"small" - cellImage:nil]); - EXPECT_TRUE(cell.get()); - - NSSize size = [cell cellSize]; - // sanity check - EXPECT_GE(size.width, 2); - EXPECT_GE(size.height, 2); - - // Once we turn on arrow drawing make sure there is now room for it. - [cell setDrawFolderArrow:YES]; - NSSize arrowSize = [cell cellSize]; - EXPECT_GT(arrowSize.width, size.width); -} - -} // namespace diff --git a/chrome/browser/cocoa/bookmarks/bookmark_button_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_button_unittest.mm deleted file mode 100644 index ed8964a..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_button_unittest.mm +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/scoped_nsobject.h" -#include "base/utf_string_conversions.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button_cell.h" -#import "chrome/browser/cocoa/browser_test_helper.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" -#import "chrome/browser/cocoa/test_event_utils.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" - -// Fake BookmarkButton delegate to get a pong on mouse entered/exited -@interface FakeButtonDelegate : NSObject<BookmarkButtonDelegate> { - @public - int entered_; - int exited_; - BOOL canDragToTrash_; - int didDragToTrashCount_; -} -@end - -@implementation FakeButtonDelegate - -- (void)fillPasteboard:(NSPasteboard*)pboard - forDragOfButton:(BookmarkButton*)button { -} - -- (void)mouseEnteredButton:(id)buton event:(NSEvent*)event { - entered_++; -} - -- (void)mouseExitedButton:(id)buton event:(NSEvent*)event { - exited_++; -} - -- (BOOL)dragShouldLockBarVisibility { - return NO; -} - -- (NSWindow*)browserWindow { - return nil; -} - -- (BOOL)canDragBookmarkButtonToTrash:(BookmarkButton*)button { - return canDragToTrash_; -} - -- (void)didDragBookmarkToTrash:(BookmarkButton*)button { - didDragToTrashCount_++; -} - -@end - -namespace { - -class BookmarkButtonTest : public CocoaTest { -}; - -// Make sure nothing leaks -TEST_F(BookmarkButtonTest, Create) { - scoped_nsobject<BookmarkButton> button; - button.reset([[BookmarkButton alloc] initWithFrame:NSMakeRect(0,0,500,500)]); -} - -// Test folder and empty node queries. -TEST_F(BookmarkButtonTest, FolderAndEmptyOrNot) { - BrowserTestHelper helper_; - scoped_nsobject<BookmarkButton> button; - scoped_nsobject<BookmarkButtonCell> cell; - - button.reset([[BookmarkButton alloc] initWithFrame:NSMakeRect(0,0,500,500)]); - cell.reset([[BookmarkButtonCell alloc] initTextCell:@"hi mom"]); - [button setCell:cell]; - - EXPECT_TRUE([button isEmpty]); - EXPECT_FALSE([button isFolder]); - EXPECT_FALSE([button bookmarkNode]); - - NSEvent* downEvent = - test_event_utils::LeftMouseDownAtPoint(NSMakePoint(10,10)); - // Since this returns (does not actually begin a modal drag), success! - [button beginDrag:downEvent]; - - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* node = model->GetBookmarkBarNode(); - [cell setBookmarkNode:node]; - EXPECT_FALSE([button isEmpty]); - EXPECT_TRUE([button isFolder]); - EXPECT_EQ([button bookmarkNode], node); - - node = model->AddURL(node, 0, ASCIIToUTF16("hi mom"), - GURL("http://www.google.com")); - [cell setBookmarkNode:node]; - EXPECT_FALSE([button isEmpty]); - EXPECT_FALSE([button isFolder]); - EXPECT_EQ([button bookmarkNode], node); -} - -TEST_F(BookmarkButtonTest, MouseEnterExitRedirect) { - NSEvent* moveEvent = - test_event_utils::MouseEventAtPoint(NSMakePoint(10,10), NSMouseMoved, 0); - scoped_nsobject<BookmarkButton> button; - scoped_nsobject<BookmarkButtonCell> cell; - scoped_nsobject<FakeButtonDelegate> - delegate([[FakeButtonDelegate alloc] init]); - button.reset([[BookmarkButton alloc] initWithFrame:NSMakeRect(0,0,500,500)]); - cell.reset([[BookmarkButtonCell alloc] initTextCell:@"hi mom"]); - [button setCell:cell]; - [button setDelegate:delegate]; - - EXPECT_EQ(0, delegate.get()->entered_); - EXPECT_EQ(0, delegate.get()->exited_); - - [button mouseEntered:moveEvent]; - EXPECT_EQ(1, delegate.get()->entered_); - EXPECT_EQ(0, delegate.get()->exited_); - - [button mouseExited:moveEvent]; - [button mouseExited:moveEvent]; - EXPECT_EQ(1, delegate.get()->entered_); - EXPECT_EQ(2, delegate.get()->exited_); -} - -TEST_F(BookmarkButtonTest, DragToTrash) { - BrowserTestHelper helper_; - - scoped_nsobject<BookmarkButton> button; - scoped_nsobject<BookmarkButtonCell> cell; - scoped_nsobject<FakeButtonDelegate> - delegate([[FakeButtonDelegate alloc] init]); - button.reset([[BookmarkButton alloc] initWithFrame:NSMakeRect(0,0,500,500)]); - cell.reset([[BookmarkButtonCell alloc] initTextCell:@"hi mom"]); - [button setCell:cell]; - [button setDelegate:delegate]; - - // Add a deletable bookmark to the button. - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* barNode = model->GetBookmarkBarNode(); - const BookmarkNode* node = model->AddURL(barNode, 0, ASCIIToUTF16("hi mom"), - GURL("http://www.google.com")); - [cell setBookmarkNode:node]; - - // Verify that if canDragBookmarkButtonToTrash is NO then the button can't - // be dragged to the trash. - delegate.get()->canDragToTrash_ = NO; - NSDragOperation operation = [button draggingSourceOperationMaskForLocal:NO]; - EXPECT_EQ(0u, operation & NSDragOperationDelete); - operation = [button draggingSourceOperationMaskForLocal:YES]; - EXPECT_EQ(0u, operation & NSDragOperationDelete); - - // Verify that if canDragBookmarkButtonToTrash is YES then the button can - // be dragged to the trash. - delegate.get()->canDragToTrash_ = YES; - operation = [button draggingSourceOperationMaskForLocal:NO]; - EXPECT_EQ(NSDragOperationDelete, operation & NSDragOperationDelete); - operation = [button draggingSourceOperationMaskForLocal:YES]; - EXPECT_EQ(NSDragOperationDelete, operation & NSDragOperationDelete); - - // Verify that canDragBookmarkButtonToTrash is called when expected. - delegate.get()->canDragToTrash_ = YES; - EXPECT_EQ(0, delegate.get()->didDragToTrashCount_); - [button draggedImage:nil endedAt:NSZeroPoint operation:NSDragOperationCopy]; - EXPECT_EQ(0, delegate.get()->didDragToTrashCount_); - [button draggedImage:nil endedAt:NSZeroPoint operation:NSDragOperationMove]; - EXPECT_EQ(0, delegate.get()->didDragToTrashCount_); - [button draggedImage:nil endedAt:NSZeroPoint operation:NSDragOperationDelete]; - EXPECT_EQ(1, delegate.get()->didDragToTrashCount_); -} - -} diff --git a/chrome/browser/cocoa/bookmarks/bookmark_drag_source.h b/chrome/browser/cocoa/bookmarks/bookmark_drag_source.h deleted file mode 100644 index 5c33797..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_drag_source.h +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#include "chrome/browser/bookmarks/bookmark_node_data.h" -#include "chrome/browser/cocoa/web_contents_drag_source.h" - -// A class that handles tracking and event processing for a drag and drop -// originating from the content area. -@interface BookmarkDragSource : WebContentsDragSource { - @private - // Our drop data. Should only be initialized once. - std::vector<BookmarkNodeData::Element> dropData_; - - Profile* profile_; -} - -// Initialize a DragDataSource object for a drag (originating on the given -// contentsView and with the given dropData and pboard). Fill the pasteboard -// with data types appropriate for dropData. -- (id)initWithContentsView:(TabContentsViewCocoa*)contentsView - dropData: - (const std::vector<BookmarkNodeData::Element>&)dropData - profile:(Profile*)profile - pasteboard:(NSPasteboard*)pboard - dragOperationMask:(NSDragOperation)dragOperationMask; - -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_drag_source.mm b/chrome/browser/cocoa/bookmarks/bookmark_drag_source.mm deleted file mode 100644 index 64f9de2..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_drag_source.mm +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_drag_source.h" - -#include "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h" -#include "chrome/browser/profile.h" -#include "chrome/browser/tab_contents/tab_contents_view_mac.h" - -@implementation BookmarkDragSource - -- (id)initWithContentsView:(TabContentsViewCocoa*)contentsView - dropData: - (const std::vector<BookmarkNodeData::Element>&)dropData - profile:(Profile*)profile - pasteboard:(NSPasteboard*)pboard - dragOperationMask:(NSDragOperation)dragOperationMask { - self = [super initWithContentsView:contentsView - pasteboard:pboard - dragOperationMask:dragOperationMask]; - if (self) { - dropData_ = dropData; - profile_ = profile; - } - - return self; -} - -- (void)fillPasteboard { - bookmark_pasteboard_helper_mac::WriteToDragClipboard(dropData_, - profile_->GetPath().value()); -} - -- (NSImage*)dragImage { - // TODO(feldstein): Do something better than this. Should have badging - // and a single drag image. - // http://crbug.com/37264 - return [NSImage imageNamed:NSImageNameMultipleDocuments]; -} - -@end // @implementation BookmarkDragSource - diff --git a/chrome/browser/cocoa/bookmarks/bookmark_editor_base_controller.h b/chrome/browser/cocoa/bookmarks/bookmark_editor_base_controller.h deleted file mode 100644 index 5880d79..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_editor_base_controller.h +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_EDITOR_BASE_CONTROLLER_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_EDITOR_BASE_CONTROLLER_H_ -#pragma once - -#import <Cocoa/Cocoa.h> - -#import "base/cocoa_protocols_mac.h" -#include "base/scoped_ptr.h" -#include "base/scoped_nsobject.h" -#include "chrome/browser/bookmarks/bookmark_editor.h" - -class BookmarkEditorBaseControllerBridge; -class BookmarkModel; -@class BookmarkTreeBrowserCell; - -// A base controller class for bookmark creation and editing dialogs which -// present the current bookmark folder structure in a tree view. Do not -// instantiate this controller directly -- use one of its derived classes. -// NOTE: If a derived class is intended to be dispatched via the -// BookmarkEditor::Show static function found in the accompanying -// implementation, that function will need to be update. -@interface BookmarkEditorBaseController : NSWindowController { - @private - IBOutlet NSButton* newFolderButton_; - IBOutlet NSButton* okButton_; // Used for unit testing only. - IBOutlet NSTreeController* folderTreeController_; - IBOutlet NSOutlineView* folderTreeView_; - - NSWindow* parentWindow_; // weak - Profile* profile_; // weak - const BookmarkNode* parentNode_; // weak; owned by the model - BookmarkEditor::Configuration configuration_; - NSString* initialName_; - NSString* displayName_; // Bound to a text field in the dialog. - BOOL okEnabled_; // Bound to the OK button. - // An array of BookmarkFolderInfo where each item describes a folder in the - // BookmarkNode structure. - scoped_nsobject<NSArray> folderTreeArray_; - // Bound to the table view giving a path to the current selections, of which - // there should only ever be one. - scoped_nsobject<NSArray> tableSelectionPaths_; - // C++ bridge object that observes the BookmarkModel for me. - scoped_ptr<BookmarkEditorBaseControllerBridge> observer_; -} - -@property (nonatomic, copy) NSString* initialName; -@property (nonatomic, copy) NSString* displayName; -@property (nonatomic, assign) BOOL okEnabled; -@property (nonatomic, retain, readonly) NSArray* folderTreeArray; -@property (nonatomic, copy) NSArray* tableSelectionPaths; - -// Designated initializer. Derived classes should call through to this init. -- (id)initWithParentWindow:(NSWindow*)parentWindow - nibName:(NSString*)nibName - profile:(Profile*)profile - parent:(const BookmarkNode*)parent - configuration:(BookmarkEditor::Configuration)configuration; - -// Run the bookmark editor as a modal sheet. Does not block. -- (void)runAsModalSheet; - -// Create a new folder at the end of the selected parent folder, give it -// an untitled name, and put it into editing mode. -- (IBAction)newFolder:(id)sender; - -// The cancel action will dismiss the dialog. Derived classes which -// override cancel:, must call this after accessing any dialog-related -// data. -- (IBAction)cancel:(id)sender; - -// The OK action will dismiss the dialog. This action is bound -// to the OK button of a dialog which presents a tree view of a profile's -// folder hierarchy and allows the creation of new folders within that tree. -// When the OK button is pressed, this function will: 1) call the derived -// class's -[willCommit] function, 2) create any new folders created by -// the user while the dialog is presented, 3) call the derived class's -// -[didCommit] function, and then 4) dismiss the dialog. At least one -// of -[willCommit] and -[didCommit] must be provided by the derived class -// and should return a NSNumber containing a BOOL or nil ('nil' means YES) -// indicating if the operation should be allowed to continue. -// Note: A derived class should not override the ok: action. -- (IBAction)ok:(id)sender; - -// Methods for use by derived classes only. - -// Determine and returns the rightmost selected/highlighted element (node) -// in the bookmark tree view if the tree view is showing, otherwise returns -// the original |parentNode_|. If the tree view is showing but nothing is -// selected then the root node is returned. -- (const BookmarkNode*)selectedNode; - -// Select/highlight the given node within the browser tree view. If the -// node is nil then select the bookmark bar node. Exposed for unit test. -- (void)selectNodeInBrowser:(const BookmarkNode*)node; - -// Notifications called when the BookmarkModel changes out from under me. -- (void)nodeRemoved:(const BookmarkNode*)node - fromParent:(const BookmarkNode*)parent; -- (void)modelChangedPreserveSelection:(BOOL)preserve; - -// Accessors -- (BookmarkModel*)bookmarkModel; -- (const BookmarkNode*)parentNode; - -@end - -// Describes the profile's bookmark folder structure: the folder name, the -// original BookmarkNode pointer (if the folder already exists), a BOOL -// indicating if the folder is new (meaning: created during this session -// but not yet committed to the bookmark structure), and an NSArray of -// child folder BookmarkFolderInfo's following this same structure. -@interface BookmarkFolderInfo : NSObject { - @private - NSString* folderName_; - const BookmarkNode* folderNode_; // weak - NSMutableArray* children_; - BOOL newFolder_; -} - -@property (nonatomic, copy) NSString* folderName; -@property (nonatomic, assign) const BookmarkNode* folderNode; -@property (nonatomic, retain) NSMutableArray* children; -@property (nonatomic, assign) BOOL newFolder; - -// Convenience creator for adding a new folder to the editor's bookmark -// structure. This folder will be added to the bookmark model when the -// user accepts the dialog. |folderName| must be provided. -+ (id)bookmarkFolderInfoWithFolderName:(NSString*)folderName; - -// Designated initializer. |folderName| must be provided. For folders which -// already exist in the bookmark model, |folderNode| and |children| (if any -// children are already attached to this folder) must be provided and -// |newFolder| should be NO. For folders which the user has added during -// this session and which have not been committed yet, |newFolder| should be -// YES and |folderNode| and |children| should be NULL/nil. -- (id)initWithFolderName:(NSString*)folderName - folderNode:(const BookmarkNode*)folderNode - children:(NSMutableArray*)children - newFolder:(BOOL)newFolder; - -// Convenience creator used during construction of the editor's bookmark -// structure. |folderName| and |folderNode| must be provided. |children| -// is optional. Private: exposed here for unit testing purposes. -+ (id)bookmarkFolderInfoWithFolderName:(NSString*)folderName - folderNode:(const BookmarkNode*)folderNode - children:(NSMutableArray*)children; - -@end - -@interface BookmarkEditorBaseController(TestingAPI) - -@property (nonatomic, readonly) BOOL okButtonEnabled; - -// Create any newly added folders. New folders are nodes in folderTreeArray -// which are marked as being new (i.e. their kFolderTreeNewFolderKey -// dictionary item is YES). This is called by -[ok:]. -- (void)createNewFolders; - -// Select the given bookmark node within the tree view. -- (void)selectTestNodeInBrowser:(const BookmarkNode*)node; - -// Return the dictionary for the folder selected in the tree. -- (BookmarkFolderInfo*)selectedFolder; - -@end - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_EDITOR_BASE_CONTROLLER_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_editor_base_controller.mm b/chrome/browser/cocoa/bookmarks/bookmark_editor_base_controller.mm deleted file mode 100644 index 1f0274e..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_editor_base_controller.mm +++ /dev/null @@ -1,604 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stack> - -#import "chrome/browser/cocoa/bookmarks/bookmark_editor_base_controller.h" - -#include "app/l10n_util.h" -#include "app/l10n_util_mac.h" -#include "base/logging.h" -#include "base/mac_util.h" -#include "base/sys_string_conversions.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_all_tabs_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_editor_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_tree_browser_cell.h" -#import "chrome/browser/cocoa/browser_window_controller.h" -#include "chrome/browser/profile.h" -#include "grit/generated_resources.h" - -@interface BookmarkEditorBaseController () - -// Return the folder tree object for the given path. -- (BookmarkFolderInfo*)folderForIndexPath:(NSIndexPath*)path; - -// (Re)build the folder tree from the BookmarkModel's current state. -- (void)buildFolderTree; - -// Notifies the controller that the bookmark model has changed. -// |selection| specifies if the current selection should be -// maintained (usually YES). -- (void)modelChangedPreserveSelection:(BOOL)preserve; - -// Notifies the controller that a node has been removed. -- (void)nodeRemoved:(const BookmarkNode*)node - fromParent:(const BookmarkNode*)parent; - -// Given a folder node, collect an array containing BookmarkFolderInfos -// describing its subchildren which are also folders. -- (NSMutableArray*)addChildFoldersFromNode:(const BookmarkNode*)node; - -// Scan the folder tree stemming from the given tree folder and create -// any newly added folders. Pass down info for the folder which was -// selected before we began creating folders. -- (void)createNewFoldersForFolder:(BookmarkFolderInfo*)treeFolder - selectedFolderInfo:(BookmarkFolderInfo*)selectedFolderInfo; - -// Scan the folder tree looking for the given bookmark node and return -// the selection path thereto. -- (NSIndexPath*)selectionPathForNode:(const BookmarkNode*)node; - -@end - -// static; implemented for each platform. Update this function for new -// classes derived from BookmarkEditorBaseController. -void BookmarkEditor::Show(gfx::NativeWindow parent_hwnd, - Profile* profile, - const BookmarkNode* parent, - const EditDetails& details, - Configuration configuration) { - BookmarkEditorBaseController* controller = nil; - if (details.type == EditDetails::NEW_FOLDER) { - controller = [[BookmarkAllTabsController alloc] - initWithParentWindow:parent_hwnd - profile:profile - parent:parent - configuration:configuration]; - } else { - controller = [[BookmarkEditorController alloc] - initWithParentWindow:parent_hwnd - profile:profile - parent:parent - node:details.existing_node - configuration:configuration]; - } - [controller runAsModalSheet]; -} - -// Adapter to tell BookmarkEditorBaseController when bookmarks change. -class BookmarkEditorBaseControllerBridge : public BookmarkModelObserver { - public: - BookmarkEditorBaseControllerBridge(BookmarkEditorBaseController* controller) - : controller_(controller), - importing_(false) - { } - - virtual void Loaded(BookmarkModel* model) { - [controller_ modelChangedPreserveSelection:YES]; - } - - virtual void BookmarkNodeMoved(BookmarkModel* model, - const BookmarkNode* old_parent, - int old_index, - const BookmarkNode* new_parent, - int new_index) { - if (!importing_ && new_parent->GetChild(new_index)->is_folder()) - [controller_ modelChangedPreserveSelection:YES]; - } - - virtual void BookmarkNodeAdded(BookmarkModel* model, - const BookmarkNode* parent, - int index) { - if (!importing_ && parent->GetChild(index)->is_folder()) - [controller_ modelChangedPreserveSelection:YES]; - } - - virtual void BookmarkNodeRemoved(BookmarkModel* model, - const BookmarkNode* parent, - int old_index, - const BookmarkNode* node) { - [controller_ nodeRemoved:node fromParent:parent]; - if (node->is_folder()) - [controller_ modelChangedPreserveSelection:NO]; - } - - virtual void BookmarkNodeChanged(BookmarkModel* model, - const BookmarkNode* node) { - if (!importing_ && node->is_folder()) - [controller_ modelChangedPreserveSelection:YES]; - } - - virtual void BookmarkNodeChildrenReordered(BookmarkModel* model, - const BookmarkNode* node) { - if (!importing_) - [controller_ modelChangedPreserveSelection:YES]; - } - - virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model, - const BookmarkNode* node) { - // I care nothing for these 'favicons': I only show folders. - } - - virtual void BookmarkImportBeginning(BookmarkModel* model) { - importing_ = true; - } - - // Invoked after a batch import finishes. This tells observers to update - // themselves if they were waiting for the update to finish. - virtual void BookmarkImportEnding(BookmarkModel* model) { - importing_ = false; - [controller_ modelChangedPreserveSelection:YES]; - } - - private: - BookmarkEditorBaseController* controller_; // weak - bool importing_; -}; - - -#pragma mark - - -@implementation BookmarkEditorBaseController - -@synthesize initialName = initialName_; -@synthesize displayName = displayName_; -@synthesize okEnabled = okEnabled_; - -- (id)initWithParentWindow:(NSWindow*)parentWindow - nibName:(NSString*)nibName - profile:(Profile*)profile - parent:(const BookmarkNode*)parent - configuration:(BookmarkEditor::Configuration)configuration { - NSString* nibpath = [mac_util::MainAppBundle() - pathForResource:nibName - ofType:@"nib"]; - if ((self = [super initWithWindowNibPath:nibpath owner:self])) { - parentWindow_ = parentWindow; - profile_ = profile; - parentNode_ = parent; - configuration_ = configuration; - initialName_ = [@"" retain]; - observer_.reset(new BookmarkEditorBaseControllerBridge(self)); - [self bookmarkModel]->AddObserver(observer_.get()); - } - return self; -} - -- (void)dealloc { - [self bookmarkModel]->RemoveObserver(observer_.get()); - [initialName_ release]; - [displayName_ release]; - [super dealloc]; -} - -- (void)awakeFromNib { - [self setDisplayName:[self initialName]]; - - if (configuration_ != BookmarkEditor::SHOW_TREE) { - // Remember the tree view's height; we will shrink our frame by that much. - NSRect frame = [[self window] frame]; - CGFloat browserHeight = [folderTreeView_ frame].size.height; - frame.size.height -= browserHeight; - frame.origin.y += browserHeight; - // Remove the folder tree and "new folder" button. - [folderTreeView_ removeFromSuperview]; - [newFolderButton_ removeFromSuperview]; - // Finally, commit the size change. - [[self window] setFrame:frame display:YES]; - } - - // Build up a tree of the current folder configuration. - [self buildFolderTree]; -} - -- (void)windowDidLoad { - if (configuration_ == BookmarkEditor::SHOW_TREE) { - [self selectNodeInBrowser:parentNode_]; - } -} - -/* TODO(jrg): -// Implementing this informal protocol allows us to open the sheet -// somewhere other than at the top of the window. NOTE: this means -// that I, the controller, am also the window's delegate. -- (NSRect)window:(NSWindow*)window willPositionSheet:(NSWindow*)sheet - usingRect:(NSRect)rect { - // adjust rect.origin.y to be the bottom of the toolbar - return rect; -} -*/ - -// TODO(jrg): consider NSModalSession. -- (void)runAsModalSheet { - // Lock down floating bar when in full-screen mode. Don't animate - // otherwise the pane will be misplaced. - [[BrowserWindowController browserWindowControllerForWindow:parentWindow_] - lockBarVisibilityForOwner:self withAnimation:NO delay:NO]; - [NSApp beginSheet:[self window] - modalForWindow:parentWindow_ - modalDelegate:self - didEndSelector:@selector(didEndSheet:returnCode:contextInfo:) - contextInfo:nil]; -} - -- (BOOL)okEnabled { - return YES; -} - -- (IBAction)ok:(id)sender { - // At least one of these two functions should be provided by derived classes. - BOOL hasWillCommit = [self respondsToSelector:@selector(willCommit)]; - BOOL hasDidCommit = [self respondsToSelector:@selector(didCommit)]; - DCHECK(hasWillCommit || hasDidCommit); - BOOL shouldContinue = YES; - if (hasWillCommit) { - NSNumber* hasWillContinue = [self performSelector:@selector(willCommit)]; - if (hasWillContinue && [hasWillContinue isKindOfClass:[NSNumber class]]) - shouldContinue = [hasWillContinue boolValue]; - } - if (shouldContinue) - [self createNewFolders]; - if (hasDidCommit) { - NSNumber* hasDidContinue = [self performSelector:@selector(didCommit)]; - if (hasDidContinue && [hasDidContinue isKindOfClass:[NSNumber class]]) - shouldContinue = [hasDidContinue boolValue]; - } - if (shouldContinue) - [NSApp endSheet:[self window]]; -} - -- (IBAction)cancel:(id)sender { - [NSApp endSheet:[self window]]; -} - -- (void)didEndSheet:(NSWindow*)sheet - returnCode:(int)returnCode - contextInfo:(void*)contextInfo { - [sheet close]; - [[BrowserWindowController browserWindowControllerForWindow:parentWindow_] - releaseBarVisibilityForOwner:self withAnimation:YES delay:NO]; -} - -- (void)windowWillClose:(NSNotification*)notification { - [self autorelease]; -} - -#pragma mark Folder Tree Management - -- (BookmarkModel*)bookmarkModel { - return profile_->GetBookmarkModel(); -} - -- (const BookmarkNode*)parentNode { - return parentNode_; -} - -- (BookmarkFolderInfo*)folderForIndexPath:(NSIndexPath*)indexPath { - NSUInteger pathCount = [indexPath length]; - BookmarkFolderInfo* item = nil; - NSArray* treeNode = [self folderTreeArray]; - for (NSUInteger i = 0; i < pathCount; ++i) { - item = [treeNode objectAtIndex:[indexPath indexAtPosition:i]]; - treeNode = [item children]; - } - return item; -} - -- (NSIndexPath*)selectedIndexPath { - NSIndexPath* selectedIndexPath = nil; - NSArray* selections = [self tableSelectionPaths]; - if ([selections count]) { - DCHECK([selections count] == 1); // Should be exactly one selection. - selectedIndexPath = [selections objectAtIndex:0]; - } - return selectedIndexPath; -} - -- (BookmarkFolderInfo*)selectedFolder { - BookmarkFolderInfo* item = nil; - NSIndexPath* selectedIndexPath = [self selectedIndexPath]; - if (selectedIndexPath) { - item = [self folderForIndexPath:selectedIndexPath]; - } - return item; -} - -- (const BookmarkNode*)selectedNode { - const BookmarkNode* selectedNode = NULL; - // Determine a new parent node only if the browser is showing. - if (configuration_ == BookmarkEditor::SHOW_TREE) { - BookmarkFolderInfo* folderInfo = [self selectedFolder]; - if (folderInfo) - selectedNode = [folderInfo folderNode]; - } else { - // If the tree is not showing then we use the original parent. - selectedNode = parentNode_; - } - return selectedNode; -} - -- (NSArray*)folderTreeArray { - return folderTreeArray_.get(); -} - -- (NSArray*)tableSelectionPaths { - return tableSelectionPaths_.get(); -} - -- (void)setTableSelectionPath:(NSIndexPath*)tableSelectionPath { - [self setTableSelectionPaths:[NSArray arrayWithObject:tableSelectionPath]]; -} - -- (void)setTableSelectionPaths:(NSArray*)tableSelectionPaths { - tableSelectionPaths_.reset([tableSelectionPaths retain]); -} - -- (void)selectNodeInBrowser:(const BookmarkNode*)node { - DCHECK(configuration_ == BookmarkEditor::SHOW_TREE); - NSIndexPath* selectionPath = [self selectionPathForNode:node]; - [self willChangeValueForKey:@"okEnabled"]; - [self setTableSelectionPath:selectionPath]; - [self didChangeValueForKey:@"okEnabled"]; -} - -- (NSIndexPath*)selectionPathForNode:(const BookmarkNode*)desiredNode { - // Back up the parent chaing for desiredNode, building up a stack - // of ancestor nodes. Then crawl down the folderTreeArray looking - // for each ancestor in order while building up the selectionPath. - std::stack<const BookmarkNode*> nodeStack; - BookmarkModel* model = profile_->GetBookmarkModel(); - const BookmarkNode* rootNode = model->root_node(); - const BookmarkNode* node = desiredNode; - while (node != rootNode) { - DCHECK(node); - nodeStack.push(node); - node = node->GetParent(); - } - NSUInteger stackSize = nodeStack.size(); - - NSIndexPath* path = nil; - NSArray* folders = [self folderTreeArray]; - while (!nodeStack.empty()) { - node = nodeStack.top(); - nodeStack.pop(); - // Find node in the current folders array. - NSUInteger i = 0; - for (BookmarkFolderInfo *folderInfo in folders) { - const BookmarkNode* testNode = [folderInfo folderNode]; - if (testNode == node) { - path = path ? [path indexPathByAddingIndex:i] : - [NSIndexPath indexPathWithIndex:i]; - folders = [folderInfo children]; - break; - } - ++i; - } - } - DCHECK([path length] == stackSize); - return path; -} - -- (NSMutableArray*)addChildFoldersFromNode:(const BookmarkNode*)node { - NSMutableArray* childFolders = nil; - int childCount = node->GetChildCount(); - for (int i = 0; i < childCount; ++i) { - const BookmarkNode* childNode = node->GetChild(i); - if (childNode->type() != BookmarkNode::URL) { - NSString* childName = base::SysUTF16ToNSString(childNode->GetTitle()); - NSMutableArray* children = [self addChildFoldersFromNode:childNode]; - BookmarkFolderInfo* folderInfo = - [BookmarkFolderInfo bookmarkFolderInfoWithFolderName:childName - folderNode:childNode - children:children]; - if (!childFolders) - childFolders = [NSMutableArray arrayWithObject:folderInfo]; - else - [childFolders addObject:folderInfo]; - } - } - return childFolders; -} - -- (void)buildFolderTree { - // Build up a tree of the current folder configuration. - BookmarkModel* model = profile_->GetBookmarkModel(); - const BookmarkNode* rootNode = model->root_node(); - NSMutableArray* baseArray = [self addChildFoldersFromNode:rootNode]; - DCHECK(baseArray); - [self willChangeValueForKey:@"folderTreeArray"]; - folderTreeArray_.reset([baseArray retain]); - [self didChangeValueForKey:@"folderTreeArray"]; -} - -- (void)modelChangedPreserveSelection:(BOOL)preserve { - const BookmarkNode* selectedNode = [self selectedNode]; - [self buildFolderTree]; - if (preserve && - selectedNode && - configuration_ == BookmarkEditor::SHOW_TREE) - [self selectNodeInBrowser:selectedNode]; -} - -- (void)nodeRemoved:(const BookmarkNode*)node - fromParent:(const BookmarkNode*)parent { - if (node->is_folder()) { - if (parentNode_ == node || parentNode_->HasAncestor(node)) { - parentNode_ = [self bookmarkModel]->GetBookmarkBarNode(); - if (configuration_ != BookmarkEditor::SHOW_TREE) { - // The user can't select a different folder, so just close up shop. - [self cancel:self]; - return; - } - } - - if (configuration_ == BookmarkEditor::SHOW_TREE) { - // For safety's sake, in case deleted node was an ancestor of selection, - // go back to a known safe place. - [self selectNodeInBrowser:parentNode_]; - } - } -} - -#pragma mark New Folder Handler - -- (void)createNewFoldersForFolder:(BookmarkFolderInfo*)folderInfo - selectedFolderInfo:(BookmarkFolderInfo*)selectedFolderInfo { - NSArray* subfolders = [folderInfo children]; - const BookmarkNode* parentNode = [folderInfo folderNode]; - DCHECK(parentNode); - NSUInteger i = 0; - for (BookmarkFolderInfo* subFolderInfo in subfolders) { - if ([subFolderInfo newFolder]) { - BookmarkModel* model = [self bookmarkModel]; - const BookmarkNode* newFolder = - model->AddGroup(parentNode, i, - base::SysNSStringToUTF16([subFolderInfo folderName])); - // Update our dictionary with the actual folder node just created. - [subFolderInfo setFolderNode:newFolder]; - [subFolderInfo setNewFolder:NO]; - // If the newly created folder was selected, update the selection path. - if (subFolderInfo == selectedFolderInfo) { - NSIndexPath* selectionPath = [self selectionPathForNode:newFolder]; - [self setTableSelectionPath:selectionPath]; - } - } - [self createNewFoldersForFolder:subFolderInfo - selectedFolderInfo:selectedFolderInfo]; - ++i; - } -} - -- (IBAction)newFolder:(id)sender { - // Create a new folder off of the selected folder node. - BookmarkFolderInfo* parentInfo = [self selectedFolder]; - if (parentInfo) { - NSIndexPath* selection = [self selectedIndexPath]; - NSString* newFolderName = - l10n_util::GetNSStringWithFixup(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME); - BookmarkFolderInfo* folderInfo = - [BookmarkFolderInfo bookmarkFolderInfoWithFolderName:newFolderName]; - [self willChangeValueForKey:@"folderTreeArray"]; - NSMutableArray* children = [parentInfo children]; - if (children) { - [children addObject:folderInfo]; - } else { - children = [NSMutableArray arrayWithObject:folderInfo]; - [parentInfo setChildren:children]; - } - [self didChangeValueForKey:@"folderTreeArray"]; - - // Expose the parent folder children. - [folderTreeView_ expandItem:parentInfo]; - - // Select the new folder node and put the folder name into edit mode. - selection = [selection indexPathByAddingIndex:[children count] - 1]; - [self setTableSelectionPath:selection]; - NSInteger row = [folderTreeView_ selectedRow]; - DCHECK(row >= 0); - [folderTreeView_ editColumn:0 row:row withEvent:nil select:YES]; - } -} - -- (void)createNewFolders { - // Turn off notifications while "importing" folders (as created in the sheet). - observer_->BookmarkImportBeginning([self bookmarkModel]); - // Scan the tree looking for nodes marked 'newFolder' and create those nodes. - NSArray* folderTreeArray = [self folderTreeArray]; - for (BookmarkFolderInfo *folderInfo in folderTreeArray) { - [self createNewFoldersForFolder:folderInfo - selectedFolderInfo:[self selectedFolder]]; - } - // Notifications back on. - observer_->BookmarkImportEnding([self bookmarkModel]); -} - -#pragma mark For Unit Test Use Only - -- (BOOL)okButtonEnabled { - return [okButton_ isEnabled]; -} - -- (void)selectTestNodeInBrowser:(const BookmarkNode*)node { - [self selectNodeInBrowser:node]; -} - -@end // BookmarkEditorBaseController - -@implementation BookmarkFolderInfo - -@synthesize folderName = folderName_; -@synthesize folderNode = folderNode_; -@synthesize children = children_; -@synthesize newFolder = newFolder_; - -+ (id)bookmarkFolderInfoWithFolderName:(NSString*)folderName - folderNode:(const BookmarkNode*)folderNode - children:(NSMutableArray*)children { - return [[[BookmarkFolderInfo alloc] initWithFolderName:folderName - folderNode:folderNode - children:children - newFolder:NO] - autorelease]; -} - -+ (id)bookmarkFolderInfoWithFolderName:(NSString*)folderName { - return [[[BookmarkFolderInfo alloc] initWithFolderName:folderName - folderNode:NULL - children:nil - newFolder:YES] - autorelease]; -} - -- (id)initWithFolderName:(NSString*)folderName - folderNode:(const BookmarkNode*)folderNode - children:(NSMutableArray*)children - newFolder:(BOOL)newFolder { - if ((self = [super init])) { - // A folderName is always required, and if newFolder is NO then there - // should be a folderNode. Children is optional. - DCHECK(folderName && (newFolder || folderNode)); - if (folderName && (newFolder || folderNode)) { - folderName_ = [folderName copy]; - folderNode_ = folderNode; - children_ = [children retain]; - newFolder_ = newFolder; - } else { - NOTREACHED(); // Invalid init. - [self release]; - self = nil; - } - } - return self; -} - -- (id)init { - NOTREACHED(); // Should never be called. - return [self initWithFolderName:nil folderNode:nil children:nil newFolder:NO]; -} - -- (void)dealloc { - [folderName_ release]; - [children_ release]; - [super dealloc]; -} - -// Implementing isEqual: allows the NSTreeController to preserve the selection -// and open/shut state of outline items when the data changes. -- (BOOL)isEqual:(id)other { - return [other isKindOfClass:[BookmarkFolderInfo class]] && - folderNode_ == [(BookmarkFolderInfo*)other folderNode]; -} - -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_editor_base_controller_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_editor_base_controller_unittest.mm deleted file mode 100644 index daac540..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_editor_base_controller_unittest.mm +++ /dev/null @@ -1,235 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#include "app/l10n_util_mac.h" -#include "base/scoped_nsobject.h" -#include "base/sys_string_conversions.h" -#include "base/utf_string_conversions.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_editor_controller.h" -#include "chrome/browser/cocoa/browser_test_helper.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" -#include "grit/generated_resources.h" -#include "testing/gtest/include/gtest/gtest.h" -#import "testing/gtest_mac.h" -#include "testing/platform_test.h" - -class BookmarkEditorBaseControllerTest : public CocoaTest { - public: - BrowserTestHelper browser_helper_; - BookmarkEditorBaseController* controller_; // weak - const BookmarkNode* group_a_; - const BookmarkNode* group_b_; - const BookmarkNode* group_b_0_; - const BookmarkNode* group_b_3_; - const BookmarkNode* group_c_; - - BookmarkEditorBaseControllerTest() { - // Set up a small bookmark hierarchy, which will look as follows: - // a b c d - // a-0 b-0 c-0 - // a-1 b-00 c-1 - // a-2 b-1 c-2 - // b-2 c-3 - // b-3 - // b-30 - // b-31 - // b-4 - BookmarkModel& model(*(browser_helper_.profile()->GetBookmarkModel())); - const BookmarkNode* root = model.GetBookmarkBarNode(); - group_a_ = model.AddGroup(root, 0, ASCIIToUTF16("a")); - model.AddURL(group_a_, 0, ASCIIToUTF16("a-0"), GURL("http://a-0.com")); - model.AddURL(group_a_, 1, ASCIIToUTF16("a-1"), GURL("http://a-1.com")); - model.AddURL(group_a_, 2, ASCIIToUTF16("a-2"), GURL("http://a-2.com")); - - group_b_ = model.AddGroup(root, 1, ASCIIToUTF16("b")); - group_b_0_ = model.AddGroup(group_b_, 0, ASCIIToUTF16("b-0")); - model.AddURL(group_b_0_, 0, ASCIIToUTF16("bb-0"), GURL("http://bb-0.com")); - model.AddURL(group_b_, 1, ASCIIToUTF16("b-1"), GURL("http://b-1.com")); - model.AddURL(group_b_, 2, ASCIIToUTF16("b-2"), GURL("http://b-2.com")); - group_b_3_ = model.AddGroup(group_b_, 3, ASCIIToUTF16("b-3")); - model.AddURL(group_b_3_, 0, ASCIIToUTF16("b-30"), GURL("http://b-30.com")); - model.AddURL(group_b_3_, 1, ASCIIToUTF16("b-31"), GURL("http://b-31.com")); - model.AddURL(group_b_, 4, ASCIIToUTF16("b-4"), GURL("http://b-4.com")); - - group_c_ = model.AddGroup(root, 2, ASCIIToUTF16("c")); - model.AddURL(group_c_, 0, ASCIIToUTF16("c-0"), GURL("http://c-0.com")); - model.AddURL(group_c_, 1, ASCIIToUTF16("c-1"), GURL("http://c-1.com")); - model.AddURL(group_c_, 2, ASCIIToUTF16("c-2"), GURL("http://c-2.com")); - model.AddURL(group_c_, 3, ASCIIToUTF16("c-3"), GURL("http://c-3.com")); - - model.AddURL(root, 3, ASCIIToUTF16("d"), GURL("http://d-0.com")); - } - - virtual BookmarkEditorBaseController* CreateController() { - return [[BookmarkEditorBaseController alloc] - initWithParentWindow:test_window() - nibName:@"BookmarkAllTabs" - profile:browser_helper_.profile() - parent:group_b_0_ - configuration:BookmarkEditor::SHOW_TREE]; - } - - virtual void SetUp() { - CocoaTest::SetUp(); - controller_ = CreateController(); - EXPECT_TRUE([controller_ window]); - [controller_ runAsModalSheet]; - } - - virtual void TearDown() { - controller_ = NULL; - CocoaTest::TearDown(); - } -}; - -TEST_F(BookmarkEditorBaseControllerTest, VerifyBookmarkTestModel) { - BookmarkModel& model(*(browser_helper_.profile()->GetBookmarkModel())); - const BookmarkNode& root(*model.GetBookmarkBarNode()); - EXPECT_EQ(4, root.GetChildCount()); - // a - const BookmarkNode* child = root.GetChild(0); - EXPECT_EQ(3, child->GetChildCount()); - const BookmarkNode* subchild = child->GetChild(0); - EXPECT_EQ(0, subchild->GetChildCount()); - subchild = child->GetChild(1); - EXPECT_EQ(0, subchild->GetChildCount()); - subchild = child->GetChild(2); - EXPECT_EQ(0, subchild->GetChildCount()); - // b - child = root.GetChild(1); - EXPECT_EQ(5, child->GetChildCount()); - subchild = child->GetChild(0); - EXPECT_EQ(1, subchild->GetChildCount()); - const BookmarkNode* subsubchild = subchild->GetChild(0); - EXPECT_EQ(0, subsubchild->GetChildCount()); - subchild = child->GetChild(1); - EXPECT_EQ(0, subchild->GetChildCount()); - subchild = child->GetChild(2); - EXPECT_EQ(0, subchild->GetChildCount()); - subchild = child->GetChild(3); - EXPECT_EQ(2, subchild->GetChildCount()); - subsubchild = subchild->GetChild(0); - EXPECT_EQ(0, subsubchild->GetChildCount()); - subsubchild = subchild->GetChild(1); - EXPECT_EQ(0, subsubchild->GetChildCount()); - subchild = child->GetChild(4); - EXPECT_EQ(0, subchild->GetChildCount()); - // c - child = root.GetChild(2); - EXPECT_EQ(4, child->GetChildCount()); - subchild = child->GetChild(0); - EXPECT_EQ(0, subchild->GetChildCount()); - subchild = child->GetChild(1); - EXPECT_EQ(0, subchild->GetChildCount()); - subchild = child->GetChild(2); - EXPECT_EQ(0, subchild->GetChildCount()); - subchild = child->GetChild(3); - EXPECT_EQ(0, subchild->GetChildCount()); - // d - child = root.GetChild(3); - EXPECT_EQ(0, child->GetChildCount()); - [controller_ cancel:nil]; -} - -TEST_F(BookmarkEditorBaseControllerTest, NodeSelection) { - EXPECT_TRUE([controller_ folderTreeArray]); - [controller_ selectTestNodeInBrowser:group_b_3_]; - const BookmarkNode* node = [controller_ selectedNode]; - EXPECT_EQ(node, group_b_3_); - [controller_ cancel:nil]; -} - -TEST_F(BookmarkEditorBaseControllerTest, CreateFolder) { - EXPECT_EQ(2, group_b_3_->GetChildCount()); - [controller_ selectTestNodeInBrowser:group_b_3_]; - NSString* expectedName = - l10n_util::GetNSStringWithFixup(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME); - [controller_ setDisplayName:expectedName]; - [controller_ newFolder:nil]; - NSArray* selectionPaths = [controller_ tableSelectionPaths]; - EXPECT_EQ(1U, [selectionPaths count]); - NSIndexPath* selectionPath = [selectionPaths objectAtIndex:0]; - EXPECT_EQ(4U, [selectionPath length]); - BookmarkFolderInfo* newFolderInfo = [controller_ selectedFolder]; - EXPECT_TRUE(newFolderInfo); - NSString* newFolderName = [newFolderInfo folderName]; - EXPECT_NSEQ(expectedName, newFolderName); - [controller_ createNewFolders]; - // Verify that the tab folder was added to the new folder. - EXPECT_EQ(3, group_b_3_->GetChildCount()); - [controller_ cancel:nil]; -} - -TEST_F(BookmarkEditorBaseControllerTest, CreateTwoFolders) { - BookmarkModel* model = browser_helper_.profile()->GetBookmarkModel(); - const BookmarkNode* bar = model->GetBookmarkBarNode(); - // Create 2 folders which are children of the bar. - [controller_ selectTestNodeInBrowser:bar]; - [controller_ newFolder:nil]; - [controller_ selectTestNodeInBrowser:bar]; - [controller_ newFolder:nil]; - // If we do NOT crash on createNewFolders, success! - // (e.g. http://crbug.com/47877 is fixed). - [controller_ createNewFolders]; - [controller_ cancel:nil]; -} - -TEST_F(BookmarkEditorBaseControllerTest, SelectedFolderDeleted) { - BookmarkModel& model(*(browser_helper_.profile()->GetBookmarkModel())); - [controller_ selectTestNodeInBrowser:group_b_3_]; - EXPECT_EQ(group_b_3_, [controller_ selectedNode]); - - // Delete the selected node, and verify it's no longer selected: - model.Remove(group_b_, 3); - EXPECT_NE(group_b_3_, [controller_ selectedNode]); - - [controller_ cancel:nil]; -} - -TEST_F(BookmarkEditorBaseControllerTest, SelectedFoldersParentDeleted) { - BookmarkModel& model(*(browser_helper_.profile()->GetBookmarkModel())); - const BookmarkNode* root = model.GetBookmarkBarNode(); - [controller_ selectTestNodeInBrowser:group_b_3_]; - EXPECT_EQ(group_b_3_, [controller_ selectedNode]); - - // Delete the selected node's parent, and verify it's no longer selected: - model.Remove(root, 1); - EXPECT_NE(group_b_3_, [controller_ selectedNode]); - - [controller_ cancel:nil]; -} - -TEST_F(BookmarkEditorBaseControllerTest, FolderAdded) { - BookmarkModel& model(*(browser_helper_.profile()->GetBookmarkModel())); - const BookmarkNode* root = model.GetBookmarkBarNode(); - - // Add a group node to the model, and verify it can be selected in the tree: - const BookmarkNode* group_added = model.AddGroup(root, 0, - ASCIIToUTF16("added")); - [controller_ selectTestNodeInBrowser:group_added]; - EXPECT_EQ(group_added, [controller_ selectedNode]); - - [controller_ cancel:nil]; -} - - -class BookmarkFolderInfoTest : public CocoaTest { }; - -TEST_F(BookmarkFolderInfoTest, Construction) { - NSMutableArray* children = [NSMutableArray arrayWithObject:@"child"]; - // We just need a pointer, and any pointer will do. - const BookmarkNode* fakeNode = - reinterpret_cast<const BookmarkNode*>(&children); - BookmarkFolderInfo* info = - [BookmarkFolderInfo bookmarkFolderInfoWithFolderName:@"name" - folderNode:fakeNode - children:children]; - EXPECT_TRUE(info); - EXPECT_EQ([info folderName], @"name"); - EXPECT_EQ([info children], children); - EXPECT_EQ([info folderNode], fakeNode); -} diff --git a/chrome/browser/cocoa/bookmarks/bookmark_editor_controller.h b/chrome/browser/cocoa/bookmarks/bookmark_editor_controller.h deleted file mode 100644 index 11bd58c..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_editor_controller.h +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_EDITOR_CONTROLLER_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_EDITOR_CONTROLLER_H_ -#pragma once - -#import "chrome/browser/cocoa/bookmarks/bookmark_editor_base_controller.h" - -// A controller for the bookmark editor, opened by 1) Edit... from the -// context menu of a bookmark button, and 2) Bookmark this Page...'s Edit -// button. -@interface BookmarkEditorController : BookmarkEditorBaseController { - @private - const BookmarkNode* node_; // weak; owned by the model - scoped_nsobject<NSString> initialUrl_; - NSString* displayURL_; // Bound to a text field in the dialog. - IBOutlet NSTextField* urlField_; -} - -@property (nonatomic, copy) NSString* displayURL; - -- (id)initWithParentWindow:(NSWindow*)parentWindow - profile:(Profile*)profile - parent:(const BookmarkNode*)parent - node:(const BookmarkNode*)node - configuration:(BookmarkEditor::Configuration)configuration; - -@end - -@interface BookmarkEditorController (UnitTesting) -- (NSColor *)urlFieldColor; -@end - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_EDITOR_CONTROLLER_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_editor_controller.mm b/chrome/browser/cocoa/bookmarks/bookmark_editor_controller.mm deleted file mode 100644 index 14500c9..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_editor_controller.mm +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_editor_controller.h" - -#include "app/l10n_util.h" -#include "base/string16.h" -#include "base/sys_string_conversions.h" -#include "chrome/browser/bookmarks/bookmark_model.h" - -@interface BookmarkEditorController (Private) - -// Grab the url from the text field and convert. -- (GURL)GURLFromUrlField; - -@end - -@implementation BookmarkEditorController - -@synthesize displayURL = displayURL_; - -+ (NSSet*)keyPathsForValuesAffectingOkEnabled { - return [NSSet setWithObject:@"displayURL"]; -} - -- (id)initWithParentWindow:(NSWindow*)parentWindow - profile:(Profile*)profile - parent:(const BookmarkNode*)parent - node:(const BookmarkNode*)node - configuration:(BookmarkEditor::Configuration)configuration { - if ((self = [super initWithParentWindow:parentWindow - nibName:@"BookmarkEditor" - profile:profile - parent:parent - configuration:configuration])) { - // "Add Page..." has no "node" so this may be NULL. - node_ = node; - } - return self; -} - -- (void)dealloc { - [displayURL_ release]; - [super dealloc]; -} - -- (void)awakeFromNib { - // Set text fields to match our bookmark. If the node is NULL we - // arrived here from an "Add Page..." item in a context menu. - if (node_) { - [self setInitialName:base::SysUTF16ToNSString(node_->GetTitle())]; - std::string url_string = node_->GetURL().possibly_invalid_spec(); - initialUrl_.reset([[NSString stringWithUTF8String:url_string.c_str()] - retain]); - } else { - initialUrl_.reset([@"" retain]); - } - [self setDisplayURL:initialUrl_]; - [super awakeFromNib]; -} - -- (void)nodeRemoved:(const BookmarkNode*)node - fromParent:(const BookmarkNode*)parent -{ - // Be conservative; it is needed (e.g. "Add Page...") - node_ = NULL; - [self cancel:self]; -} - -#pragma mark Bookmark Editing - -// If possible, return a valid GURL from the URL text field. -- (GURL)GURLFromUrlField { - NSString* url = [self displayURL]; - GURL newURL = GURL([url UTF8String]); - if (!newURL.is_valid()) { - // Mimic observed friendliness from Windows - newURL = GURL([[NSString stringWithFormat:@"http://%@", url] UTF8String]); - } - return newURL; -} - -// Enable the OK button if there is a valid URL. -- (BOOL)okEnabled { - BOOL okEnabled = NO; - if ([[self displayURL] length]) { - GURL newURL = [self GURLFromUrlField]; - okEnabled = (newURL.is_valid()) ? YES : NO; - } - if (okEnabled) - [urlField_ setBackgroundColor:[NSColor whiteColor]]; - else - [urlField_ setBackgroundColor:[NSColor colorWithCalibratedRed:1.0 - green:0.67 - blue:0.67 - alpha:1.0]]; - return okEnabled; -} - -// The the bookmark's URL is assumed to be valid (otherwise the OK button -// should not be enabled). Previously existing bookmarks for which the -// parent has not changed are updated in-place. Those for which the parent -// has changed are removed with a new node created under the new parent. -// Called by -[BookmarkEditorBaseController ok:]. -- (NSNumber*)didCommit { - NSString* name = [[self displayName] stringByTrimmingCharactersInSet: - [NSCharacterSet newlineCharacterSet]]; - string16 newTitle = base::SysNSStringToUTF16(name); - const BookmarkNode* newParentNode = [self selectedNode]; - GURL newURL = [self GURLFromUrlField]; - if (!newURL.is_valid()) { - // Shouldn't be reached -- OK button should be disabled if not valid! - NOTREACHED(); - return [NSNumber numberWithBool:NO]; - } - - // Determine where the new/replacement bookmark is to go. - BookmarkModel* model = [self bookmarkModel]; - // If there was an old node then we update the node, and move it to its new - // parent if the parent has changed (rather than deleting it from the old - // parent and adding to the new -- which also prevents the 'poofing' that - // occurs when a node is deleted). - if (node_) { - model->SetURL(node_, newURL); - model->SetTitle(node_, newTitle); - const BookmarkNode* oldParentNode = [self parentNode]; - if (newParentNode != oldParentNode) - model->Move(node_, newParentNode, newParentNode->GetChildCount()); - } else { - // Otherwise, add a new bookmark at the end of the newly selected folder. - model->AddURL(newParentNode, newParentNode->GetChildCount(), newTitle, - newURL); - } - return [NSNumber numberWithBool:YES]; -} - -- (NSColor *)urlFieldColor { - return [urlField_ backgroundColor]; -} - -@end // BookmarkEditorController - diff --git a/chrome/browser/cocoa/bookmarks/bookmark_editor_controller_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_editor_controller_unittest.mm deleted file mode 100644 index e19898d..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_editor_controller_unittest.mm +++ /dev/null @@ -1,423 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#include "base/string16.h" -#include "base/sys_string_conversions.h" -#include "base/utf_string_conversions.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_editor_controller.h" -#include "chrome/browser/cocoa/browser_test_helper.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" -#include "testing/gtest/include/gtest/gtest.h" -#import "testing/gtest_mac.h" -#include "testing/platform_test.h" - -class BookmarkEditorControllerTest : public CocoaTest { - public: - BrowserTestHelper browser_helper_; - const BookmarkNode* default_node_; - const BookmarkNode* default_parent_; - const char* default_name_; - string16 default_title_; - BookmarkEditorController* controller_; - - virtual void SetUp() { - CocoaTest::SetUp(); - BookmarkModel* model = browser_helper_.profile()->GetBookmarkModel(); - default_parent_ = model->GetBookmarkBarNode(); - default_name_ = "http://www.zim-bop-a-dee.com/"; - default_title_ = ASCIIToUTF16("ooh title"); - const BookmarkNode* default_node = model->AddURL(default_parent_, 0, - default_title_, - GURL(default_name_)); - controller_ = [[BookmarkEditorController alloc] - initWithParentWindow:test_window() - profile:browser_helper_.profile() - parent:default_parent_ - node:default_node - configuration:BookmarkEditor::NO_TREE]; - [controller_ runAsModalSheet]; - } - - virtual void TearDown() { - controller_ = NULL; - CocoaTest::TearDown(); - } -}; - -TEST_F(BookmarkEditorControllerTest, NoEdit) { - [controller_ cancel:nil]; - ASSERT_EQ(default_parent_->GetChildCount(), 1); - const BookmarkNode* child = default_parent_->GetChild(0); - EXPECT_EQ(child->GetTitle(), default_title_); - EXPECT_EQ(child->GetURL(), GURL(default_name_)); -} - -TEST_F(BookmarkEditorControllerTest, EditTitle) { - [controller_ setDisplayName:@"whamma jamma bamma"]; - [controller_ ok:nil]; - ASSERT_EQ(default_parent_->GetChildCount(), 1); - const BookmarkNode* child = default_parent_->GetChild(0); - EXPECT_EQ(child->GetTitle(), ASCIIToUTF16("whamma jamma bamma")); - EXPECT_EQ(child->GetURL(), GURL(default_name_)); -} - -TEST_F(BookmarkEditorControllerTest, EditURL) { - EXPECT_TRUE([controller_ okButtonEnabled]); - [controller_ setDisplayURL:@"http://yellow-sneakers.com/"]; - EXPECT_TRUE([controller_ okButtonEnabled]); - [controller_ ok:nil]; - ASSERT_EQ(default_parent_->GetChildCount(), 1); - const BookmarkNode* child = default_parent_->GetChild(0); - EXPECT_EQ(child->GetTitle(), default_title_); - EXPECT_EQ(child->GetURL(), GURL("http://yellow-sneakers.com/")); -} - -TEST_F(BookmarkEditorControllerTest, EditAndFixPrefix) { - [controller_ setDisplayURL:@"x"]; - [controller_ ok:nil]; - ASSERT_EQ(default_parent_->GetChildCount(), 1); - const BookmarkNode* child = default_parent_->GetChild(0); - EXPECT_TRUE(child->GetURL().is_valid()); -} - -TEST_F(BookmarkEditorControllerTest, NodeDeleted) { - // Delete the bookmark being edited and verify the sheet cancels itself: - ASSERT_TRUE([test_window() attachedSheet]); - BookmarkModel* model = browser_helper_.profile()->GetBookmarkModel(); - model->Remove(default_parent_, 0); - ASSERT_FALSE([test_window() attachedSheet]); -} - -TEST_F(BookmarkEditorControllerTest, EditAndConfirmOKButton) { - // Confirm OK button enabled/disabled as appropriate: - // First test the URL. - EXPECT_TRUE([controller_ okButtonEnabled]); - [controller_ setDisplayURL:@""]; - EXPECT_FALSE([controller_ okButtonEnabled]); - [controller_ setDisplayURL:@"http://www.cnn.com"]; - EXPECT_TRUE([controller_ okButtonEnabled]); - // Then test the name. - [controller_ setDisplayName:@""]; - EXPECT_TRUE([controller_ okButtonEnabled]); - [controller_ setDisplayName:@" "]; - EXPECT_TRUE([controller_ okButtonEnabled]); - // Then little mix of both. - [controller_ setDisplayName:@"name"]; - EXPECT_TRUE([controller_ okButtonEnabled]); - [controller_ setDisplayURL:@""]; - EXPECT_FALSE([controller_ okButtonEnabled]); - [controller_ cancel:nil]; -} - -TEST_F(BookmarkEditorControllerTest, GoodAndBadURLsChangeColor) { - // Confirm that the background color of the URL edit field changes - // based on whether it contains a valid or invalid URL. - [controller_ setDisplayURL:@"http://www.cnn.com"]; - NSColor *urlColorA = [controller_ urlFieldColor]; - EXPECT_TRUE(urlColorA); - [controller_ setDisplayURL:@""]; - NSColor *urlColorB = [controller_ urlFieldColor]; - EXPECT_TRUE(urlColorB); - EXPECT_NSNE(urlColorA, urlColorB); - [controller_ setDisplayURL:@"http://www.google.com"]; - [controller_ cancel:nil]; - urlColorB = [controller_ urlFieldColor]; - EXPECT_TRUE(urlColorB); - EXPECT_NSEQ(urlColorA, urlColorB); -} - -class BookmarkEditorControllerNoNodeTest : public CocoaTest { - public: - BrowserTestHelper browser_helper_; - BookmarkEditorController* controller_; - - virtual void SetUp() { - CocoaTest::SetUp(); - BookmarkModel* model = browser_helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - controller_ = [[BookmarkEditorController alloc] - initWithParentWindow:test_window() - profile:browser_helper_.profile() - parent:parent - node:NULL - configuration:BookmarkEditor::NO_TREE]; - - [controller_ runAsModalSheet]; - } - - virtual void TearDown() { - controller_ = NULL; - CocoaTest::TearDown(); - } -}; - -TEST_F(BookmarkEditorControllerNoNodeTest, NoNodeNoTree) { - EXPECT_EQ(@"", [controller_ displayName]); - EXPECT_EQ(@"", [controller_ displayURL]); - EXPECT_FALSE([controller_ okButtonEnabled]); - [controller_ cancel:nil]; -} - -class BookmarkEditorControllerYesNodeTest : public CocoaTest { - public: - BrowserTestHelper browser_helper_; - string16 default_title_; - const char* url_name_; - BookmarkEditorController* controller_; - - virtual void SetUp() { - CocoaTest::SetUp(); - BookmarkModel* model = browser_helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - default_title_ = ASCIIToUTF16("wooh title"); - url_name_ = "http://www.zoom-baby-doo-da.com/"; - const BookmarkNode* node = model->AddURL(parent, 0, default_title_, - GURL(url_name_)); - controller_ = [[BookmarkEditorController alloc] - initWithParentWindow:test_window() - profile:browser_helper_.profile() - parent:parent - node:node - configuration:BookmarkEditor::NO_TREE]; - - [controller_ runAsModalSheet]; - } - - virtual void TearDown() { - controller_ = NULL; - CocoaTest::TearDown(); - } -}; - -TEST_F(BookmarkEditorControllerYesNodeTest, YesNodeShowTree) { - EXPECT_NSEQ(base::SysUTF16ToNSString(default_title_), - [controller_ displayName]); - EXPECT_NSEQ([NSString stringWithCString:url_name_ - encoding:NSUTF8StringEncoding], - [controller_ displayURL]); - [controller_ cancel:nil]; -} - -class BookmarkEditorControllerTreeTest : public CocoaTest { - - public: - BrowserTestHelper browser_helper_; - BookmarkEditorController* controller_; - const BookmarkNode* group_a_; - const BookmarkNode* group_b_; - const BookmarkNode* group_bb_; - const BookmarkNode* group_c_; - const BookmarkNode* bookmark_bb_3_; - GURL bb3_url_1_; - GURL bb3_url_2_; - - BookmarkEditorControllerTreeTest() { - // Set up a small bookmark hierarchy, which will look as follows: - // a b c d - // a-0 b-0 c-0 - // a-1 bb-0 c-1 - // a-2 bb-1 c-2 - // bb-2 - // bb-3 - // bb-4 - // b-1 - // b-2 - BookmarkModel& model(*(browser_helper_.profile()->GetBookmarkModel())); - const BookmarkNode* root = model.GetBookmarkBarNode(); - group_a_ = model.AddGroup(root, 0, ASCIIToUTF16("a")); - model.AddURL(group_a_, 0, ASCIIToUTF16("a-0"), GURL("http://a-0.com")); - model.AddURL(group_a_, 1, ASCIIToUTF16("a-1"), GURL("http://a-1.com")); - model.AddURL(group_a_, 2, ASCIIToUTF16("a-2"), GURL("http://a-2.com")); - - group_b_ = model.AddGroup(root, 1, ASCIIToUTF16("b")); - model.AddURL(group_b_, 0, ASCIIToUTF16("b-0"), GURL("http://b-0.com")); - group_bb_ = model.AddGroup(group_b_, 1, ASCIIToUTF16("bb")); - model.AddURL(group_bb_, 0, ASCIIToUTF16("bb-0"), GURL("http://bb-0.com")); - model.AddURL(group_bb_, 1, ASCIIToUTF16("bb-1"), GURL("http://bb-1.com")); - model.AddURL(group_bb_, 2, ASCIIToUTF16("bb-2"), GURL("http://bb-2.com")); - - // To find it later, this bookmark name must always have a URL - // of http://bb-3.com or https://bb-3.com - bb3_url_1_ = GURL("http://bb-3.com"); - bb3_url_2_ = GURL("https://bb-3.com"); - bookmark_bb_3_ = model.AddURL(group_bb_, 3, ASCIIToUTF16("bb-3"), - bb3_url_1_); - - model.AddURL(group_bb_, 4, ASCIIToUTF16("bb-4"), GURL("http://bb-4.com")); - model.AddURL(group_b_, 2, ASCIIToUTF16("b-1"), GURL("http://b-2.com")); - model.AddURL(group_b_, 3, ASCIIToUTF16("b-2"), GURL("http://b-3.com")); - - group_c_ = model.AddGroup(root, 2, ASCIIToUTF16("c")); - model.AddURL(group_c_, 0, ASCIIToUTF16("c-0"), GURL("http://c-0.com")); - model.AddURL(group_c_, 1, ASCIIToUTF16("c-1"), GURL("http://c-1.com")); - model.AddURL(group_c_, 2, ASCIIToUTF16("c-2"), GURL("http://c-2.com")); - model.AddURL(group_c_, 3, ASCIIToUTF16("c-3"), GURL("http://c-3.com")); - - model.AddURL(root, 3, ASCIIToUTF16("d"), GURL("http://d-0.com")); - } - - virtual BookmarkEditorController* CreateController() { - return [[BookmarkEditorController alloc] - initWithParentWindow:test_window() - profile:browser_helper_.profile() - parent:group_bb_ - node:bookmark_bb_3_ - configuration:BookmarkEditor::SHOW_TREE]; - } - - virtual void SetUp() { - controller_ = CreateController(); - [controller_ runAsModalSheet]; - } - - virtual void TearDown() { - controller_ = NULL; - CocoaTest::TearDown(); - } - - // After changing a node, pointers to the node may be invalid. This - // is because the node itself may not be updated; it may removed and - // a new one is added in that location. (Implementation detail of - // BookmarkEditorController). This method updates the class's - // bookmark_bb_3_ so that it points to the new node for testing. - void UpdateBB3() { - std::vector<const BookmarkNode*> nodes; - BookmarkModel* model = browser_helper_.profile()->GetBookmarkModel(); - model->GetNodesByURL(bb3_url_1_, &nodes); - if (nodes.size() == 0) - model->GetNodesByURL(bb3_url_2_, &nodes); - DCHECK(nodes.size()); - bookmark_bb_3_ = nodes[0]; - } - -}; - -TEST_F(BookmarkEditorControllerTreeTest, VerifyBookmarkTestModel) { - BookmarkModel& model(*(browser_helper_.profile()->GetBookmarkModel())); - model.root_node(); - const BookmarkNode& root(*model.GetBookmarkBarNode()); - EXPECT_EQ(4, root.GetChildCount()); - const BookmarkNode* child = root.GetChild(0); - EXPECT_EQ(3, child->GetChildCount()); - const BookmarkNode* subchild = child->GetChild(0); - EXPECT_EQ(0, subchild->GetChildCount()); - subchild = child->GetChild(1); - EXPECT_EQ(0, subchild->GetChildCount()); - subchild = child->GetChild(2); - EXPECT_EQ(0, subchild->GetChildCount()); - - child = root.GetChild(1); - EXPECT_EQ(4, child->GetChildCount()); - subchild = child->GetChild(0); - EXPECT_EQ(0, subchild->GetChildCount()); - subchild = child->GetChild(1); - EXPECT_EQ(5, subchild->GetChildCount()); - const BookmarkNode* subsubchild = subchild->GetChild(0); - EXPECT_EQ(0, subsubchild->GetChildCount()); - subsubchild = subchild->GetChild(1); - EXPECT_EQ(0, subsubchild->GetChildCount()); - subsubchild = subchild->GetChild(2); - EXPECT_EQ(0, subsubchild->GetChildCount()); - subsubchild = subchild->GetChild(3); - EXPECT_EQ(0, subsubchild->GetChildCount()); - subsubchild = subchild->GetChild(4); - EXPECT_EQ(0, subsubchild->GetChildCount()); - subchild = child->GetChild(2); - EXPECT_EQ(0, subchild->GetChildCount()); - subchild = child->GetChild(3); - EXPECT_EQ(0, subchild->GetChildCount()); - - child = root.GetChild(2); - EXPECT_EQ(4, child->GetChildCount()); - subchild = child->GetChild(0); - EXPECT_EQ(0, subchild->GetChildCount()); - subchild = child->GetChild(1); - EXPECT_EQ(0, subchild->GetChildCount()); - subchild = child->GetChild(2); - EXPECT_EQ(0, subchild->GetChildCount()); - subchild = child->GetChild(3); - EXPECT_EQ(0, subchild->GetChildCount()); - - child = root.GetChild(3); - EXPECT_EQ(0, child->GetChildCount()); - [controller_ cancel:nil]; -} - -TEST_F(BookmarkEditorControllerTreeTest, RenameBookmarkInPlace) { - const BookmarkNode* oldParent = bookmark_bb_3_->GetParent(); - [controller_ setDisplayName:@"NEW NAME"]; - [controller_ ok:nil]; - UpdateBB3(); - const BookmarkNode* newParent = bookmark_bb_3_->GetParent(); - ASSERT_EQ(newParent, oldParent); - int childIndex = newParent->IndexOfChild(bookmark_bb_3_); - ASSERT_EQ(3, childIndex); -} - -TEST_F(BookmarkEditorControllerTreeTest, ChangeBookmarkURLInPlace) { - const BookmarkNode* oldParent = bookmark_bb_3_->GetParent(); - [controller_ setDisplayURL:@"https://bb-3.com"]; - [controller_ ok:nil]; - UpdateBB3(); - const BookmarkNode* newParent = bookmark_bb_3_->GetParent(); - ASSERT_EQ(newParent, oldParent); - int childIndex = newParent->IndexOfChild(bookmark_bb_3_); - ASSERT_EQ(3, childIndex); -} - -TEST_F(BookmarkEditorControllerTreeTest, ChangeBookmarkGroup) { - [controller_ selectTestNodeInBrowser:group_c_]; - [controller_ ok:nil]; - UpdateBB3(); - const BookmarkNode* parent = bookmark_bb_3_->GetParent(); - ASSERT_EQ(parent, group_c_); - int childIndex = parent->IndexOfChild(bookmark_bb_3_); - ASSERT_EQ(4, childIndex); -} - -TEST_F(BookmarkEditorControllerTreeTest, ChangeNameAndBookmarkGroup) { - [controller_ setDisplayName:@"NEW NAME"]; - [controller_ selectTestNodeInBrowser:group_c_]; - [controller_ ok:nil]; - UpdateBB3(); - const BookmarkNode* parent = bookmark_bb_3_->GetParent(); - ASSERT_EQ(parent, group_c_); - int childIndex = parent->IndexOfChild(bookmark_bb_3_); - ASSERT_EQ(4, childIndex); - EXPECT_EQ(bookmark_bb_3_->GetTitle(), ASCIIToUTF16("NEW NAME")); -} - -TEST_F(BookmarkEditorControllerTreeTest, AddFolderWithGroupSelected) { - // Folders are NOT added unless the OK button is pressed. - [controller_ newFolder:nil]; - [controller_ cancel:nil]; - EXPECT_EQ(5, group_bb_->GetChildCount()); -} - -class BookmarkEditorControllerTreeNoNodeTest : - public BookmarkEditorControllerTreeTest { - public: - virtual BookmarkEditorController* CreateController() { - return [[BookmarkEditorController alloc] - initWithParentWindow:test_window() - profile:browser_helper_.profile() - parent:group_bb_ - node:nil - configuration:BookmarkEditor::SHOW_TREE]; - } - -}; - -TEST_F(BookmarkEditorControllerTreeNoNodeTest, NewBookmarkNoNode) { - [controller_ setDisplayName:@"NEW BOOKMARK"]; - [controller_ setDisplayURL:@"http://NEWURL.com"]; - [controller_ ok:nil]; - const BookmarkNode* new_node = group_bb_->GetChild(5); - ASSERT_EQ(0, new_node->GetChildCount()); - EXPECT_EQ(new_node->GetTitle(), ASCIIToUTF16("NEW BOOKMARK")); - EXPECT_EQ(new_node->GetURL(), GURL("http://NEWURL.com")); -} diff --git a/chrome/browser/cocoa/bookmarks/bookmark_folder_target.h b/chrome/browser/cocoa/bookmarks/bookmark_folder_target.h deleted file mode 100644 index c877f72..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_folder_target.h +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_FOLDER_TARGET_CONTROLLER_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_FOLDER_TARGET_CONTROLLER_H_ -#pragma once - -#import <Cocoa/Cocoa.h> - -@class BookmarkButton; -@protocol BookmarkButtonControllerProtocol; -class BookmarkNode; - -// Target (in the target/action sense) of a bookmark folder button. -// Since ObjC doesn't have multiple inheritance we use has-a instead -// of is-a to share behavior between the BookmarkBarFolderController -// (NSWindowController) and the BookmarkBarController -// (NSViewController). -// -// This class is unit tested in the context of a BookmarkBarController. -@interface BookmarkFolderTarget : NSObject { - // The owner of the bookmark folder button - id<BookmarkButtonControllerProtocol> controller_; // weak -} - -- (id)initWithController:(id<BookmarkButtonControllerProtocol>)controller; - -// Main IBAction for a button click. -- (IBAction)openBookmarkFolderFromButton:(id)sender; - -// Copies the given bookmark node to the given pasteboard, declaring appropriate -// types (to paste a URL with a title). -- (void)copyBookmarkNode:(const BookmarkNode*)node - toPasteboard:(NSPasteboard*)pboard; - -// Fill the given pasteboard with appropriate data when the given button is -// dragged. Since the delegate has no way of providing pasteboard data later, -// all data must actually be put into the pasteboard and not merely promised. -- (void)fillPasteboard:(NSPasteboard*)pboard - forDragOfButton:(BookmarkButton*)button; - -@end - -// The (internal) |NSPasteboard| type string for bookmark button drags, used for -// dragging buttons around the bookmark bar. The data for this type is just a -// pointer to the |BookmarkButton| being dragged. -extern NSString* kBookmarkButtonDragType; - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_FOLDER_TARGET_CONTROLLER_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_folder_target.mm b/chrome/browser/cocoa/bookmarks/bookmark_folder_target.mm deleted file mode 100644 index 3fb1507..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_folder_target.mm +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_folder_target.h" - -#include "base/logging.h" -#include "base/sys_string_conversions.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_button.h" -#import "chrome/browser/cocoa/event_utils.h" -#import "third_party/mozilla/NSPasteboard+Utils.h" - -NSString* kBookmarkButtonDragType = @"ChromiumBookmarkButtonDragType"; - -@implementation BookmarkFolderTarget - -- (id)initWithController:(id<BookmarkButtonControllerProtocol>)controller { - if ((self = [super init])) { - controller_ = controller; - } - return self; -} - -// This IBAction is called when the user clicks (mouseUp, really) on a -// "folder" bookmark button. (In this context, "Click" does not -// include right-click to open a context menu which follows a -// different path). Scenarios when folder X is clicked: -// *Predicate* *Action* -// (nothing) Open Folder X -// Folder X open Close folder X -// Folder Y open Close Y, open X -// Cmd-click Open All with proper disposition -// -// Note complication in which a click-drag engages drag and drop, not -// a click-to-open. Thus the path to get here is a little twisted. -- (IBAction)openBookmarkFolderFromButton:(id)sender { - DCHECK(sender); - // Watch out for a modifier click. For example, command-click - // should open all. - // - // NOTE: we cannot use [[sender cell] mouseDownFlags] because we - // thwart the normal mouse click mechanism to make buttons - // draggable. Thus we must use [NSApp currentEvent]. - // - // Holding command while using the scroll wheel (or moving around - // over a bookmark folder) can confuse us. Unless we check the - // event type, we are not sure if this is an "open folder" due to a - // hover-open or "open folder" due to a click. It doesn't matter - // (both do the same thing) unless a modifier is held, since - // command-click should "open all" but command-move should not. - // WindowOpenDispositionFromNSEvent does not consider the event - // type; only the modifiers. Thus the need for an extra - // event-type-check here. - DCHECK([sender bookmarkNode]->is_folder()); - NSEvent* event = [NSApp currentEvent]; - WindowOpenDisposition disposition = - event_utils::WindowOpenDispositionFromNSEvent(event); - if (([event type] != NSMouseEntered) && - ([event type] != NSMouseMoved) && - ([event type] != NSScrollWheel) && - (disposition == NEW_BACKGROUND_TAB)) { - [controller_ closeAllBookmarkFolders]; - [controller_ openAll:[sender bookmarkNode] disposition:disposition]; - return; - } - - // If click on same folder, close it and be done. - // Else we clicked on a different folder so more work to do. - if ([[controller_ folderController] parentButton] == sender) { - [controller_ closeBookmarkFolder:controller_]; - return; - } - - [controller_ addNewFolderControllerWithParentButton:sender]; -} - -- (void)copyBookmarkNode:(const BookmarkNode*)node - toPasteboard:(NSPasteboard*)pboard { - if (!node) { - NOTREACHED(); - return; - } - - if (node->is_folder()) { - // TODO(viettrungluu): I'm not sure what we should do, so just declare the - // "additional" types we're given for now. Maybe we want to add a list of - // URLs? Would we then have to recurse if there were subfolders? - // In the meanwhile, we *must* set it to a known state. (If this survives to - // a 10.6-only release, it can be replaced with |-clearContents|.) - [pboard declareTypes:[NSArray array] owner:nil]; - } else { - const std::string spec = node->GetURL().spec(); - NSString* url = base::SysUTF8ToNSString(spec); - NSString* title = base::SysUTF16ToNSString(node->GetTitle()); - [pboard declareURLPasteboardWithAdditionalTypes:[NSArray array] - owner:nil]; - [pboard setDataForURL:url title:title]; - } -} - -- (void)fillPasteboard:(NSPasteboard*)pboard - forDragOfButton:(BookmarkButton*)button { - if (const BookmarkNode* node = [button bookmarkNode]) { - // Put the bookmark information into the pasteboard, and then write our own - // data for |kBookmarkButtonDragType|. - [self copyBookmarkNode:node toPasteboard:pboard]; - [pboard addTypes:[NSArray arrayWithObject:kBookmarkButtonDragType] - owner:nil]; - [pboard setData:[NSData dataWithBytes:&button length:sizeof(button)] - forType:kBookmarkButtonDragType]; - } else { - NOTREACHED(); - } -} - -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_folder_target_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_folder_target_unittest.mm deleted file mode 100644 index 403002d..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_folder_target_unittest.mm +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/bookmarks/bookmark_model.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_folder_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_folder_target.h" -#include "chrome/browser/cocoa/bookmarks/bookmark_button.h" -#include "chrome/browser/cocoa/browser_test_helper.h" -#include "chrome/browser/cocoa/cocoa_test_helper.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" -#import "third_party/ocmock/OCMock/OCMock.h" - -@interface OCMockObject(PreventRetainCycle) -- (void)clearRecordersAndExpectations; -@end - -@implementation OCMockObject(PreventRetainCycle) - -// We need a mechanism to clear the invocation handlers to break a -// retain cycle (see below; search for "retain cycle"). -- (void)clearRecordersAndExpectations { - [recorders removeAllObjects]; - [expectations removeAllObjects]; -} - -@end - - -class BookmarkFolderTargetTest : public CocoaTest { - public: - virtual void SetUp() { - CocoaTest::SetUp(); - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - bmbNode_ = model->GetBookmarkBarNode(); - } - virtual void TearDown() { - pool_.Recycle(); - CocoaTest::TearDown(); - } - - BrowserTestHelper helper_; - const BookmarkNode* bmbNode_; - base::mac::ScopedNSAutoreleasePool pool_; -}; - -TEST_F(BookmarkFolderTargetTest, StartWithNothing) { - // Need a fake "button" which has a bookmark node. - id sender = [OCMockObject mockForClass:[BookmarkButton class]]; - [[[sender stub] andReturnValue:OCMOCK_VALUE(bmbNode_)] bookmarkNode]; - - // Fake controller - id controller = [OCMockObject mockForClass:[BookmarkBarFolderController - class]]; - // No current folder - [[[controller stub] andReturn:nil] folderController]; - - // Make sure we get an addNew - [[controller expect] addNewFolderControllerWithParentButton:sender]; - - scoped_nsobject<BookmarkFolderTarget> target( - [[BookmarkFolderTarget alloc] initWithController:controller]); - - [target openBookmarkFolderFromButton:sender]; - [controller verify]; -} - -TEST_F(BookmarkFolderTargetTest, ReopenSameFolder) { - // Need a fake "button" which has a bookmark node. - id sender = [OCMockObject mockForClass:[BookmarkButton class]]; - [[[sender stub] andReturnValue:OCMOCK_VALUE(bmbNode_)] bookmarkNode]; - - // Fake controller - id controller = [OCMockObject mockForClass:[BookmarkBarFolderController - class]]; - // YES a current folder. Self-mock that as well, so "same" will be - // true. Note this creates a retain cycle in OCMockObject; we - // accomodate at the end of this function. - [[[controller stub] andReturn:controller] folderController]; - [[[controller stub] andReturn:sender] parentButton]; - - // The folder is open, so a click should close just that folder (and - // any subfolders). - [[controller expect] closeBookmarkFolder:controller]; - - scoped_nsobject<BookmarkFolderTarget> target( - [[BookmarkFolderTarget alloc] initWithController:controller]); - - [target openBookmarkFolderFromButton:sender]; - [controller verify]; - - // Our use of OCMockObject means an object can return itself. This - // creates a retain cycle, since OCMock retains all objects used in - // mock creation. Clear out the invocation handlers of all - // OCMockRecorders we used to break the cycles. - [controller clearRecordersAndExpectations]; -} - -TEST_F(BookmarkFolderTargetTest, ReopenNotSame) { - // Need a fake "button" which has a bookmark node. - id sender = [OCMockObject mockForClass:[BookmarkButton class]]; - [[[sender stub] andReturnValue:OCMOCK_VALUE(bmbNode_)] bookmarkNode]; - - // Fake controller - id controller = [OCMockObject mockForClass:[BookmarkBarFolderController - class]]; - // YES a current folder but NOT same. - [[[controller stub] andReturn:controller] folderController]; - [[[controller stub] andReturn:nil] parentButton]; - - // Insure the controller gets a chance to decide which folders to - // close and open. - [[controller expect] addNewFolderControllerWithParentButton:sender]; - - scoped_nsobject<BookmarkFolderTarget> target( - [[BookmarkFolderTarget alloc] initWithController:controller]); - - [target openBookmarkFolderFromButton:sender]; - [controller verify]; - - // Break retain cycles. - [controller clearRecordersAndExpectations]; -} diff --git a/chrome/browser/cocoa/bookmarks/bookmark_menu.h b/chrome/browser/cocoa/bookmarks/bookmark_menu.h deleted file mode 100644 index d4ac001..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_menu.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#include "base/basictypes.h" - - -// The context menu for bookmark buttons needs to know which -// BookmarkNode it is talking about. For example, "Open All" is -// disabled if the bookmark node is a folder and has no children. -@interface BookmarkMenu : NSMenu { - @private - int64 id_; // id of the bookmark node we represent. -} -- (void)setRepresentedObject:(id)object; -@property (nonatomic) int64 id; -@end - diff --git a/chrome/browser/cocoa/bookmarks/bookmark_menu.mm b/chrome/browser/cocoa/bookmarks/bookmark_menu.mm deleted file mode 100644 index 9be848b..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_menu.mm +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_menu.h" - - -@implementation BookmarkMenu - -@synthesize id = id_; - -// Convention in the bookmark bar controller: the bookmark button -// cells have a BookmarkNode as their represented object. This object -// is placed in a BookmarkMenu at the time a cell is asked for its -// menu. -- (void)setRepresentedObject:(id)object { - if ([object isKindOfClass:[NSNumber class]]) { - id_ = static_cast<int64>([object longLongValue]); - } -} - -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_menu_bridge.h b/chrome/browser/cocoa/bookmarks/bookmark_menu_bridge.h deleted file mode 100644 index bc606fc..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_menu_bridge.h +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// C++ controller for the bookmark menu; one per AppController (which -// means there is only one). When bookmarks are changed, this class -// takes care of updating Cocoa bookmark menus. This is not named -// BookmarkMenuController to help avoid confusion between languages. -// This class needs to be C++, not ObjC, since it derives from -// BookmarkModelObserver. -// -// Most Chromium Cocoa menu items are static from a nib (e.g. New -// Tab), but may be enabled/disabled under certain circumstances -// (e.g. Cut and Paste). In addition, most Cocoa menu items have -// firstResponder: as a target. Unusually, bookmark menu items are -// created dynamically. They also have a target of -// BookmarkMenuCocoaController instead of firstResponder. -// See BookmarkMenuBridge::AddNodeToMenu()). - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_MENU_BRIDGE_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_MENU_BRIDGE_H_ -#pragma once - -#include <map> - -#include "base/scoped_nsobject.h" -#include "chrome/browser/bookmarks/bookmark_model_observer.h" - -class BookmarkNode; -class Profile; -@class NSImage; -@class NSMenu; -@class NSMenuItem; -@class BookmarkMenuCocoaController; - -class BookmarkMenuBridge : public BookmarkModelObserver { - public: - BookmarkMenuBridge(Profile* profile); - virtual ~BookmarkMenuBridge(); - - // Overridden from BookmarkModelObserver - virtual void Loaded(BookmarkModel* model); - virtual void BookmarkModelBeingDeleted(BookmarkModel* model); - virtual void BookmarkNodeMoved(BookmarkModel* model, - const BookmarkNode* old_parent, - int old_index, - const BookmarkNode* new_parent, - int new_index); - virtual void BookmarkNodeAdded(BookmarkModel* model, - const BookmarkNode* parent, - int index); - virtual void BookmarkNodeRemoved(BookmarkModel* model, - const BookmarkNode* parent, - int old_index, - const BookmarkNode* node); - virtual void BookmarkNodeChanged(BookmarkModel* model, - const BookmarkNode* node); - virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model, - const BookmarkNode* node); - virtual void BookmarkNodeChildrenReordered(BookmarkModel* model, - const BookmarkNode* node); - - // Rebuilds the bookmark menu, if it has been marked invalid. - void UpdateMenu(NSMenu* bookmark_menu); - - // I wish I had a "friend @class" construct. - BookmarkModel* GetBookmarkModel(); - Profile* GetProfile(); - - protected: - // Clear all bookmarks from the given bookmark menu. - void ClearBookmarkMenu(NSMenu* menu); - - // Mark the bookmark menu as being invalid. - void InvalidateMenu() { menuIsValid_ = false; } - - // Helper for adding the node as a submenu to the menu with the - // given title. - void AddNodeAsSubmenu(NSMenu* menu, - const BookmarkNode* node, - NSString* title); - - // Helper for recursively adding items to our bookmark menu - // All children of |node| will be added to |menu|. - // TODO(jrg): add a counter to enforce maximum nodes added - void AddNodeToMenu(const BookmarkNode* node, NSMenu* menu); - - // This configures an NSMenuItem with all the data from a BookmarkNode. This - // is used to update existing menu items, as well as to configure newly - // created ones, like in AddNodeToMenu(). - // |set_title| is optional since it is only needed when we get a - // node changed notification. On initial build of the menu we set - // the title as part of alloc/init. - void ConfigureMenuItem(const BookmarkNode* node, NSMenuItem* item, - bool set_title); - - // Returns the NSMenuItem for a given BookmarkNode. - NSMenuItem* MenuItemForNode(const BookmarkNode* node); - - // Return the Bookmark menu. - virtual NSMenu* BookmarkMenu(); - - // Start watching the bookmarks for changes. - void ObserveBookmarkModel(); - - private: - friend class BookmarkMenuBridgeTest; - - // True iff the menu is up-to-date with the actual BookmarkModel. - bool menuIsValid_; - - Profile* profile_; // weak - BookmarkMenuCocoaController* controller_; // strong - - // The folder image so we can use one copy for all. - scoped_nsobject<NSImage> folder_image_; - - // In order to appropriately update items in the bookmark menu, without - // forcing a rebuild, map the model's nodes to menu items. - std::map<const BookmarkNode*, NSMenuItem*> bookmark_nodes_; -}; - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_MENU_BRIDGE_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_menu_bridge.mm b/chrome/browser/cocoa/bookmarks/bookmark_menu_bridge.mm deleted file mode 100644 index 7145285..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_menu_bridge.mm +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <AppKit/AppKit.h> - -#include "app/l10n_util.h" -#include "app/resource_bundle.h" -#include "base/nsimage_cache_mac.h" -#include "base/sys_string_conversions.h" -#import "chrome/browser/app_controller_mac.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#include "chrome/browser/cocoa/bookmarks/bookmark_menu_bridge.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller.h" -#include "chrome/browser/profile.h" -#include "chrome/browser/profile_manager.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_list.h" -#include "grit/generated_resources.h" -#include "grit/theme_resources.h" -#include "skia/ext/skia_utils_mac.h" - -BookmarkMenuBridge::BookmarkMenuBridge(Profile* profile) - : menuIsValid_(false), - profile_(profile), - controller_([[BookmarkMenuCocoaController alloc] initWithBridge:this]) { - if (GetBookmarkModel()) - ObserveBookmarkModel(); -} - -BookmarkMenuBridge::~BookmarkMenuBridge() { - BookmarkModel *model = GetBookmarkModel(); - if (model) - model->RemoveObserver(this); - [controller_ release]; -} - -NSMenu* BookmarkMenuBridge::BookmarkMenu() { - return [controller_ menu]; -} - -void BookmarkMenuBridge::Loaded(BookmarkModel* model) { - InvalidateMenu(); -} - -void BookmarkMenuBridge::UpdateMenu(NSMenu* bookmark_menu) { - DCHECK(bookmark_menu); - if (menuIsValid_) - return; - BookmarkModel* model = GetBookmarkModel(); - if (!model || !model->IsLoaded()) - return; - - if (!folder_image_) { - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - folder_image_.reset( - [rb.GetNativeImageNamed(IDR_BOOKMARK_BAR_FOLDER) retain]); - } - - ClearBookmarkMenu(bookmark_menu); - - // Add bookmark bar items, if any. - const BookmarkNode* barNode = model->GetBookmarkBarNode(); - CHECK(barNode); - if (barNode->GetChildCount()) { - [bookmark_menu addItem:[NSMenuItem separatorItem]]; - AddNodeToMenu(barNode, bookmark_menu); - } - - // Create a submenu for "other bookmarks", and fill it in. - NSString* other_items_title = - l10n_util::GetNSString(IDS_BOOMARK_BAR_OTHER_FOLDER_NAME); - [bookmark_menu addItem:[NSMenuItem separatorItem]]; - AddNodeAsSubmenu(bookmark_menu, - model->other_node(), - other_items_title); - - menuIsValid_ = true; -} - -void BookmarkMenuBridge::BookmarkModelBeingDeleted(BookmarkModel* model) { - NSMenu* bookmark_menu = BookmarkMenu(); - if (bookmark_menu == nil) - return; - - ClearBookmarkMenu(bookmark_menu); -} - -void BookmarkMenuBridge::BookmarkNodeMoved(BookmarkModel* model, - const BookmarkNode* old_parent, - int old_index, - const BookmarkNode* new_parent, - int new_index) { - InvalidateMenu(); -} - -void BookmarkMenuBridge::BookmarkNodeAdded(BookmarkModel* model, - const BookmarkNode* parent, - int index) { - InvalidateMenu(); -} - -void BookmarkMenuBridge::BookmarkNodeRemoved(BookmarkModel* model, - const BookmarkNode* parent, - int old_index, - const BookmarkNode* node) { - InvalidateMenu(); -} - -void BookmarkMenuBridge::BookmarkNodeChanged(BookmarkModel* model, - const BookmarkNode* node) { - NSMenuItem* item = MenuItemForNode(node); - if (item) - ConfigureMenuItem(node, item, true); -} - -void BookmarkMenuBridge::BookmarkNodeFavIconLoaded(BookmarkModel* model, - const BookmarkNode* node) { - NSMenuItem* item = MenuItemForNode(node); - if (item) - ConfigureMenuItem(node, item, false); -} - -void BookmarkMenuBridge::BookmarkNodeChildrenReordered( - BookmarkModel* model, const BookmarkNode* node) { - InvalidateMenu(); -} - -// Watch for changes. -void BookmarkMenuBridge::ObserveBookmarkModel() { - BookmarkModel* model = GetBookmarkModel(); - model->AddObserver(this); - if (model->IsLoaded()) - Loaded(model); -} - -BookmarkModel* BookmarkMenuBridge::GetBookmarkModel() { - if (!profile_) - return NULL; - return profile_->GetBookmarkModel(); -} - -Profile* BookmarkMenuBridge::GetProfile() { - return profile_; -} - -void BookmarkMenuBridge::ClearBookmarkMenu(NSMenu* menu) { - bookmark_nodes_.clear(); - // Recursively delete all menus that look like a bookmark. Assume - // all items with submenus contain only bookmarks. Also delete all - // separator items since we explicirly add them back in. This should - // deletes everything except the first item ("Add Bookmark..."). - NSArray* items = [menu itemArray]; - for (NSMenuItem* item in items) { - // Convention: items in the bookmark list which are bookmarks have - // an action of openBookmarkMenuItem:. Also, assume all items - // with submenus are submenus of bookmarks. - if (([item action] == @selector(openBookmarkMenuItem:)) || - [item hasSubmenu] || - [item isSeparatorItem]) { - // This will eventually [obj release] all its kids, if it has - // any. - [menu removeItem:item]; - } else { - // Leave it alone. - } - } -} - -void BookmarkMenuBridge::AddNodeAsSubmenu(NSMenu* menu, - const BookmarkNode* node, - NSString* title) { - NSMenuItem* items = [[[NSMenuItem alloc] - initWithTitle:title - action:nil - keyEquivalent:@""] autorelease]; - [items setImage:folder_image_]; - [menu addItem:items]; - NSMenu* other_submenu = [[[NSMenu alloc] initWithTitle:title] - autorelease]; - [menu setSubmenu:other_submenu forItem:items]; - AddNodeToMenu(node, other_submenu); -} - -// TODO(jrg): limit the number of bookmarks in the menubar? -void BookmarkMenuBridge::AddNodeToMenu(const BookmarkNode* node, NSMenu* menu) { - int child_count = node->GetChildCount(); - if (!child_count) { - NSString* empty_string = l10n_util::GetNSString(IDS_MENU_EMPTY_SUBMENU); - NSMenuItem* item = [[[NSMenuItem alloc] initWithTitle:empty_string - action:nil - keyEquivalent:@""] autorelease]; - [menu addItem:item]; - } else for (int i = 0; i < child_count; i++) { - const BookmarkNode* child = node->GetChild(i); - NSString* title = [BookmarkMenuCocoaController menuTitleForNode:child]; - NSMenuItem* item = [[[NSMenuItem alloc] initWithTitle:title - action:nil - keyEquivalent:@""] autorelease]; - [menu addItem:item]; - bookmark_nodes_[child] = item; - if (child->is_folder()) { - [item setImage:folder_image_]; - NSMenu* submenu = [[[NSMenu alloc] initWithTitle:title] autorelease]; - [menu setSubmenu:submenu forItem:item]; - AddNodeToMenu(child, submenu); // recursive call - } else { - ConfigureMenuItem(child, item, false); - } - } -} - -void BookmarkMenuBridge::ConfigureMenuItem(const BookmarkNode* node, - NSMenuItem* item, - bool set_title) { - if (set_title) { - NSString* title = [BookmarkMenuCocoaController menuTitleForNode:node]; - [item setTitle:title]; - } - [item setTarget:controller_]; - [item setAction:@selector(openBookmarkMenuItem:)]; - [item setTag:node->id()]; - // Add a tooltip - std::string url_string = node->GetURL().possibly_invalid_spec(); - NSString* tooltip = [NSString stringWithFormat:@"%@\n%s", - base::SysUTF16ToNSString(node->GetTitle()), - url_string.c_str()]; - [item setToolTip:tooltip]; - - // Check to see if we have a favicon. - NSImage* favicon = nil; - BookmarkModel* model = GetBookmarkModel(); - if (model) { - const SkBitmap& bitmap = model->GetFavIcon(node); - if (!bitmap.isNull()) - favicon = gfx::SkBitmapToNSImage(bitmap); - } - // Either we do not have a loaded favicon or the conversion from SkBitmap - // failed. Use the default site image instead. - if (!favicon) - favicon = nsimage_cache::ImageNamed(@"nav.pdf"); - [item setImage:favicon]; -} - -NSMenuItem* BookmarkMenuBridge::MenuItemForNode(const BookmarkNode* node) { - if (!node) - return nil; - std::map<const BookmarkNode*, NSMenuItem*>::iterator it = - bookmark_nodes_.find(node); - if (it == bookmark_nodes_.end()) - return nil; - return it->second; -} diff --git a/chrome/browser/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm deleted file mode 100644 index 5f06ce5..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_menu_bridge_unittest.mm +++ /dev/null @@ -1,317 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <AppKit/AppKit.h> - -#import "base/scoped_nsobject.h" -#include "base/string16.h" -#include "base/string_util.h" -#include "base/utf_string_conversions.h" -#include "chrome/app/chrome_command_ids.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#include "chrome/browser/cocoa/bookmarks/bookmark_menu_bridge.h" -#include "chrome/browser/cocoa/browser_test_helper.h" -#include "chrome/browser/ui/browser.h" -#include "testing/gtest/include/gtest/gtest.h" -#import "testing/gtest_mac.h" -#include "testing/platform_test.h" - -class TestBookmarkMenuBridge : public BookmarkMenuBridge { - public: - TestBookmarkMenuBridge(Profile* profile) - : BookmarkMenuBridge(profile), - menu_([[NSMenu alloc] initWithTitle:@"test"]) { - } - virtual ~TestBookmarkMenuBridge() {} - - scoped_nsobject<NSMenu> menu_; - - protected: - // Overridden from BookmarkMenuBridge. - virtual NSMenu* BookmarkMenu() { - return menu_; - } -}; - -// TODO(jrg): see refactor comment in bookmark_bar_state_controller_unittest.mm -class BookmarkMenuBridgeTest : public PlatformTest { - public: - - void SetUp() { - bridge_.reset(new TestBookmarkMenuBridge(browser_test_helper_.profile())); - EXPECT_TRUE(bridge_.get()); - } - - // We are a friend of BookmarkMenuBridge (and have access to - // protected methods), but none of the classes generated by TEST_F() - // are. This (and AddNodeToMenu()) are simple wrappers to let - // derived test classes have access to protected methods. - void ClearBookmarkMenu(BookmarkMenuBridge* bridge, NSMenu* menu) { - bridge->ClearBookmarkMenu(menu); - } - - void InvalidateMenu() { bridge_->InvalidateMenu(); } - bool menu_is_valid() { return bridge_->menuIsValid_; } - - void AddNodeToMenu(BookmarkMenuBridge* bridge, const BookmarkNode* root, - NSMenu* menu) { - bridge->AddNodeToMenu(root, menu); - } - - NSMenuItem* MenuItemForNode(BookmarkMenuBridge* bridge, - const BookmarkNode* node) { - return bridge->MenuItemForNode(node); - } - - NSMenuItem* AddItemToMenu(NSMenu *menu, NSString *title, SEL selector) { - NSMenuItem *item = [[[NSMenuItem alloc] initWithTitle:title action:NULL - keyEquivalent:@""] autorelease]; - if (selector) - [item setAction:selector]; - [menu addItem:item]; - return item; - } - - BrowserTestHelper browser_test_helper_; - scoped_ptr<TestBookmarkMenuBridge> bridge_; -}; - -TEST_F(BookmarkMenuBridgeTest, TestBookmarkMenuAutoSeparator) { - BookmarkModel* model = bridge_->GetBookmarkModel(); - bridge_->Loaded(model); - NSMenu* menu = bridge_->menu_.get(); - bridge_->UpdateMenu(menu); - // The bare menu after loading has a separator and an "Other Bookmarks" - // submenu. - EXPECT_EQ(2, [menu numberOfItems]); - // Add a bookmark and reload and there should be 4 items: the previous - // menu contents plus a new separator and the new bookmark. - const BookmarkNode* parent = model->GetBookmarkBarNode(); - const char* url = "http://www.zim-bop-a-dee.com/"; - model->AddURL(parent, 0, ASCIIToUTF16("Bookmark"), GURL(url)); - bridge_->UpdateMenu(menu); - EXPECT_EQ(4, [menu numberOfItems]); - // Remove the new bookmark and reload and we should have 2 items again - // because the separator should have been removed as well. - model->Remove(parent, 0); - bridge_->UpdateMenu(menu); - EXPECT_EQ(2, [menu numberOfItems]); -} - -// Test that ClearBookmarkMenu() removes all bookmark menus. -TEST_F(BookmarkMenuBridgeTest, TestClearBookmarkMenu) { - NSMenu* menu = bridge_->menu_.get(); - - AddItemToMenu(menu, @"hi mom", nil); - AddItemToMenu(menu, @"not", @selector(openBookmarkMenuItem:)); - NSMenuItem* item = AddItemToMenu(menu, @"hi mom", nil); - [item setSubmenu:[[[NSMenu alloc] initWithTitle:@"bar"] autorelease]]; - AddItemToMenu(menu, @"not", @selector(openBookmarkMenuItem:)); - AddItemToMenu(menu, @"zippy", @selector(length)); - [menu addItem:[NSMenuItem separatorItem]]; - - ClearBookmarkMenu(bridge_.get(), menu); - - // Make sure all bookmark items are removed, all items with - // submenus removed, and all separator items are gone. - EXPECT_EQ(2, [menu numberOfItems]); - for (NSMenuItem *item in [menu itemArray]) { - EXPECT_NSNE(@"not", [item title]); - } -} - -// Test invalidation -TEST_F(BookmarkMenuBridgeTest, TestInvalidation) { - BookmarkModel* model = bridge_->GetBookmarkModel(); - bridge_->Loaded(model); - - EXPECT_FALSE(menu_is_valid()); - bridge_->UpdateMenu(bridge_->menu_); - EXPECT_TRUE(menu_is_valid()); - - InvalidateMenu(); - EXPECT_FALSE(menu_is_valid()); - InvalidateMenu(); - EXPECT_FALSE(menu_is_valid()); - bridge_->UpdateMenu(bridge_->menu_); - EXPECT_TRUE(menu_is_valid()); - bridge_->UpdateMenu(bridge_->menu_); - EXPECT_TRUE(menu_is_valid()); - - const BookmarkNode* parent = model->GetBookmarkBarNode(); - const char* url = "http://www.zim-bop-a-dee.com/"; - model->AddURL(parent, 0, ASCIIToUTF16("Bookmark"), GURL(url)); - - EXPECT_FALSE(menu_is_valid()); - bridge_->UpdateMenu(bridge_->menu_); - EXPECT_TRUE(menu_is_valid()); -} - -// Test that AddNodeToMenu() properly adds bookmark nodes as menus, -// including the recursive case. -TEST_F(BookmarkMenuBridgeTest, TestAddNodeToMenu) { - string16 empty; - NSMenu* menu = bridge_->menu_.get(); - - BookmarkModel* model = bridge_->GetBookmarkModel(); - const BookmarkNode* root = model->GetBookmarkBarNode(); - EXPECT_TRUE(model && root); - - const char* short_url = "http://foo/"; - const char* long_url = "http://super-duper-long-url--." - "that.cannot.possibly.fit.even-in-80-columns" - "or.be.reasonably-displayed-in-a-menu" - "without.looking-ridiculous.com/"; // 140 chars total - - // 3 nodes; middle one has a child, last one has a HUGE URL - // Set their titles to be the same as the URLs - const BookmarkNode* node = NULL; - model->AddURL(root, 0, ASCIIToUTF16(short_url), GURL(short_url)); - bridge_->UpdateMenu(menu); - int prev_count = [menu numberOfItems] - 1; // "extras" added at this point - node = model->AddGroup(root, 1, empty); - model->AddURL(root, 2, ASCIIToUTF16(long_url), GURL(long_url)); - - // And the submenu fo the middle one - model->AddURL(node, 0, empty, GURL("http://sub")); - bridge_->UpdateMenu(menu); - - EXPECT_EQ((NSInteger)(prev_count+3), [menu numberOfItems]); - - // Verify the 1st one is there with the right action. - NSMenuItem* item = [menu itemWithTitle:[NSString - stringWithUTF8String:short_url]]; - EXPECT_TRUE(item); - EXPECT_EQ(@selector(openBookmarkMenuItem:), [item action]); - EXPECT_EQ(NO, [item hasSubmenu]); - NSMenuItem* short_item = item; - NSMenuItem* long_item = nil; - - // Now confirm we have 2 submenus (the one we added, plus "other") - int subs = 0; - for (item in [menu itemArray]) { - if ([item hasSubmenu]) - subs++; - } - EXPECT_EQ(2, subs); - - for (item in [menu itemArray]) { - if ([[item title] hasPrefix:@"http://super-duper"]) { - long_item = item; - break; - } - } - EXPECT_TRUE(long_item); - - // Make sure a short title looks fine - NSString* s = [short_item title]; - EXPECT_NSEQ([NSString stringWithUTF8String:short_url], s); - - // Make sure a super-long title gets trimmed - s = [long_item title]; - EXPECT_TRUE([s length] < strlen(long_url)); - - // Confirm tooltips and confirm they are not trimmed (like the item - // name might be). Add tolerance for URL fixer-upping; - // e.g. http://foo becomes http://foo/) - EXPECT_GE([[short_item toolTip] length], (2*strlen(short_url) - 5)); - EXPECT_GE([[long_item toolTip] length], (2*strlen(long_url) - 5)); - - // Make sure the favicon is non-nil (should be either the default site - // icon or a favicon, if present). - EXPECT_TRUE([short_item image]); - EXPECT_TRUE([long_item image]); -} - -// Makes sure our internal map of BookmarkNode to NSMenuItem works. -TEST_F(BookmarkMenuBridgeTest, TestGetMenuItemForNode) { - string16 empty; - NSMenu* menu = bridge_->menu_.get(); - - BookmarkModel* model = bridge_->GetBookmarkModel(); - const BookmarkNode* bookmark_bar = model->GetBookmarkBarNode(); - const BookmarkNode* root = model->AddGroup(bookmark_bar, 0, empty); - EXPECT_TRUE(model && root); - - model->AddURL(root, 0, ASCIIToUTF16("Test Item"), GURL("http://test")); - AddNodeToMenu(bridge_.get(), root, menu); - EXPECT_TRUE(MenuItemForNode(bridge_.get(), root->GetChild(0))); - - model->AddURL(root, 1, ASCIIToUTF16("Test 2"), GURL("http://second-test")); - AddNodeToMenu(bridge_.get(), root, menu); - EXPECT_TRUE(MenuItemForNode(bridge_.get(), root->GetChild(0))); - EXPECT_TRUE(MenuItemForNode(bridge_.get(), root->GetChild(1))); - - const BookmarkNode* removed_node = root->GetChild(0); - EXPECT_EQ(2, root->GetChildCount()); - model->Remove(root, 0); - EXPECT_EQ(1, root->GetChildCount()); - bridge_->UpdateMenu(menu); - EXPECT_FALSE(MenuItemForNode(bridge_.get(), removed_node)); - EXPECT_TRUE(MenuItemForNode(bridge_.get(), root->GetChild(0))); - - const BookmarkNode empty_node(GURL("http://no-where/")); - EXPECT_FALSE(MenuItemForNode(bridge_.get(), &empty_node)); - EXPECT_FALSE(MenuItemForNode(bridge_.get(), NULL)); -} - -// Test that Loaded() adds both the bookmark bar nodes and the "other" nodes. -TEST_F(BookmarkMenuBridgeTest, TestAddNodeToOther) { - NSMenu* menu = bridge_->menu_.get(); - - BookmarkModel* model = bridge_->GetBookmarkModel(); - const BookmarkNode* root = model->other_node(); - EXPECT_TRUE(model && root); - - const char* short_url = "http://foo/"; - model->AddURL(root, 0, ASCIIToUTF16(short_url), GURL(short_url)); - - bridge_->UpdateMenu(menu); - ASSERT_GT([menu numberOfItems], 0); - NSMenuItem* other = [menu itemAtIndex:([menu numberOfItems]-1)]; - EXPECT_TRUE(other); - EXPECT_TRUE([other hasSubmenu]); - ASSERT_GT([[other submenu] numberOfItems], 0); - EXPECT_NSEQ(@"http://foo/", [[[other submenu] itemAtIndex:0] title]); -} - -TEST_F(BookmarkMenuBridgeTest, TestFavIconLoading) { - NSMenu* menu = bridge_->menu_; - - BookmarkModel* model = bridge_->GetBookmarkModel(); - const BookmarkNode* root = model->GetBookmarkBarNode(); - EXPECT_TRUE(model && root); - - const BookmarkNode* node = - model->AddURL(root, 0, ASCIIToUTF16("Test Item"), - GURL("http://favicon-test")); - bridge_->UpdateMenu(menu); - NSMenuItem* item = [menu itemWithTitle:@"Test Item"]; - EXPECT_TRUE([item image]); - [item setImage:nil]; - bridge_->BookmarkNodeFavIconLoaded(model, node); - EXPECT_TRUE([item image]); -} - -TEST_F(BookmarkMenuBridgeTest, TestChangeTitle) { - NSMenu* menu = bridge_->menu_; - BookmarkModel* model = bridge_->GetBookmarkModel(); - const BookmarkNode* root = model->GetBookmarkBarNode(); - EXPECT_TRUE(model && root); - - const BookmarkNode* node = - model->AddURL(root, 0, ASCIIToUTF16("Test Item"), - GURL("http://title-test")); - bridge_->UpdateMenu(menu); - NSMenuItem* item = [menu itemWithTitle:@"Test Item"]; - EXPECT_TRUE([item image]); - - model->SetTitle(node, ASCIIToUTF16("New Title")); - - item = [menu itemWithTitle:@"Test Item"]; - EXPECT_FALSE(item); - item = [menu itemWithTitle:@"New Title"]; - EXPECT_TRUE(item); -} - diff --git a/chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller.h b/chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller.h deleted file mode 100644 index dc5c221..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller.h +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Controller (MVC) for the bookmark menu. -// All bookmark menu item commands get directed here. -// Unfortunately there is already a C++ class named BookmarkMenuController. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_MENU_COCOA_CONTROLLER_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_MENU_COCOA_CONTROLLER_H_ -#pragma once - -#import <Cocoa/Cocoa.h> - -#import "base/cocoa_protocols_mac.h" - -class BookmarkNode; -class BookmarkMenuBridge; - -@interface BookmarkMenuCocoaController : NSObject<NSMenuDelegate> { - @private - BookmarkMenuBridge* bridge_; // weak; owns me -} - -// The Bookmarks menu -@property (nonatomic, readonly) NSMenu* menu; - -// Return an autoreleased string to be used as a menu title for the -// given bookmark node. -+ (NSString*)menuTitleForNode:(const BookmarkNode*)node; - -- (id)initWithBridge:(BookmarkMenuBridge *)bridge; - -// Called by any Bookmark menu item. -// The menu item's tag is the bookmark ID. -- (IBAction)openBookmarkMenuItem:(id)sender; - -@end // BookmarkMenuCocoaController - - -@interface BookmarkMenuCocoaController (ExposedForUnitTests) -- (const BookmarkNode*)nodeForIdentifier:(int)identifier; -- (void)openURLForNode:(const BookmarkNode*)node; -@end // BookmarkMenuCocoaController (ExposedForUnitTests) - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_MENU_COCOA_CONTROLLER_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller.mm b/chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller.mm deleted file mode 100644 index 93ff846..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller.mm +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller.h" - -#include "app/text_elider.h" -#include "base/sys_string_conversions.h" -#include "base/utf_string_conversions.h" -#include "chrome/app/chrome_command_ids.h" // IDC_BOOKMARK_MENU -#import "chrome/browser/app_controller_mac.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_menu_bridge.h" -#include "chrome/browser/cocoa/event_utils.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/tab_contents/tab_contents.h" -#include "chrome/browser/ui/browser.h" -#include "webkit/glue/window_open_disposition.h" - -namespace { - -// Menus more than this many pixels wide will get trimmed -// TODO(jrg): ask UI dudes what a good value is. -const NSUInteger kMaximumMenuPixelsWide = 300; - -} - -@implementation BookmarkMenuCocoaController - -+ (NSString*)menuTitleForNode:(const BookmarkNode*)node { - NSFont* nsfont = [NSFont menuBarFontOfSize:0]; // 0 means "default" - gfx::Font font(base::SysNSStringToWide([nsfont fontName]), - static_cast<int>([nsfont pointSize])); - string16 title = gfx::ElideText(node->GetTitle(), - font, - kMaximumMenuPixelsWide, - false); - return base::SysUTF16ToNSString(title); -} - -- (id)initWithBridge:(BookmarkMenuBridge *)bridge { - if ((self = [super init])) { - bridge_ = bridge; - DCHECK(bridge_); - [[self menu] setDelegate:self]; - } - return self; -} - -- (void)dealloc { - [[self menu] setDelegate:nil]; - [super dealloc]; -} - -- (NSMenu*)menu { - return [[[NSApp mainMenu] itemWithTag:IDC_BOOKMARK_MENU] submenu]; -} - -- (BOOL)validateMenuItem:(NSMenuItem*)menuItem { - AppController* controller = [NSApp delegate]; - return [controller keyWindowIsNotModal]; -} - -// NSMenu delegate method: called just before menu is displayed. -- (void)menuNeedsUpdate:(NSMenu*)menu { - bridge_->UpdateMenu(menu); -} - -// Return the a BookmarkNode that has the given id (called -// "identifier" here to avoid conflict with objc's concept of "id"). -- (const BookmarkNode*)nodeForIdentifier:(int)identifier { - return bridge_->GetBookmarkModel()->GetNodeByID(identifier); -} - -// Open the URL of the given BookmarkNode in the current tab. -- (void)openURLForNode:(const BookmarkNode*)node { - Browser* browser = Browser::GetTabbedBrowser(bridge_->GetProfile(), true); - if (!browser) - browser = Browser::Create(bridge_->GetProfile()); - WindowOpenDisposition disposition = - event_utils::WindowOpenDispositionFromNSEvent([NSApp currentEvent]); - browser->OpenURL(node->GetURL(), GURL(), disposition, - PageTransition::AUTO_BOOKMARK); -} - -- (IBAction)openBookmarkMenuItem:(id)sender { - NSInteger tag = [sender tag]; - int identifier = tag; - const BookmarkNode* node = [self nodeForIdentifier:identifier]; - DCHECK(node); - if (!node) - return; // shouldn't be reached - - [self openURLForNode:node]; -} - -@end // BookmarkMenuCocoaController - diff --git a/chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller_unittest.mm deleted file mode 100644 index 8c0b4cd..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller_unittest.mm +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/string16.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#include "chrome/browser/cocoa/browser_test_helper.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_menu_cocoa_controller.h" -#include "chrome/browser/ui/browser.h" -#include "testing/gtest/include/gtest/gtest.h" - -@interface FakeBookmarkMenuController : BookmarkMenuCocoaController { - @public - BrowserTestHelper* helper_; - const BookmarkNode* nodes_[2]; - BOOL opened_[2]; -} -@end - -@implementation FakeBookmarkMenuController - -- (id)init { - if ((self = [super init])) { - string16 empty; - helper_ = new BrowserTestHelper(); - BookmarkModel* model = helper_->browser()->profile()->GetBookmarkModel(); - const BookmarkNode* bookmark_bar = model->GetBookmarkBarNode(); - nodes_[0] = model->AddURL(bookmark_bar, 0, empty, GURL("http://0.com")); - nodes_[1] = model->AddURL(bookmark_bar, 1, empty, GURL("http://1.com")); - } - return self; -} - -- (void)dealloc { - delete helper_; - [super dealloc]; -} - -- (const BookmarkNode*)nodeForIdentifier:(int)identifier { - if ((identifier < 0) || (identifier >= 2)) - return NULL; - return nodes_[identifier]; -} - -- (void)openURLForNode:(const BookmarkNode*)node { - std::string url = node->GetURL().possibly_invalid_spec(); - if (url.find("http://0.com") != std::string::npos) - opened_[0] = YES; - if (url.find("http://1.com") != std::string::npos) - opened_[1] = YES; -} - -@end // FakeBookmarkMenuController - - -TEST(BookmarkMenuCocoaControllerTest, TestOpenItem) { - FakeBookmarkMenuController *c = [[FakeBookmarkMenuController alloc] init]; - NSMenuItem *item = [[[NSMenuItem alloc] init] autorelease]; - for (int i = 0; i < 2; i++) { - [item setTag:i]; - ASSERT_EQ(c->opened_[i], NO); - [c openBookmarkMenuItem:item]; - ASSERT_NE(c->opened_[i], NO); - } - [c release]; -} diff --git a/chrome/browser/cocoa/bookmarks/bookmark_menu_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_menu_unittest.mm deleted file mode 100644 index bed4f55..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_menu_unittest.mm +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) 2009 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/scoped_nsobject.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_menu.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" - -namespace { - -class BookmarkMenuTest : public CocoaTest { -}; - -TEST_F(BookmarkMenuTest, Basics) { - scoped_nsobject<BookmarkMenu> menu([[BookmarkMenu alloc] - initWithTitle:@"title"]); - scoped_nsobject<NSMenuItem> item([[NSMenuItem alloc] initWithTitle:@"item" - action:NULL - keyEquivalent:@""]); - [menu addItem:item]; - long long l = 103849459459598948LL; // arbitrary - NSNumber* number = [NSNumber numberWithLongLong:l]; - [menu setRepresentedObject:number]; - EXPECT_EQ(l, [menu id]); -} - -} // namespace diff --git a/chrome/browser/cocoa/bookmarks/bookmark_model_observer_for_cocoa.h b/chrome/browser/cocoa/bookmarks/bookmark_model_observer_for_cocoa.h deleted file mode 100644 index 4acc972..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_model_observer_for_cocoa.h +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// C++ bridge class to send a selector to a Cocoa object when the -// bookmark model changes. Some Cocoa objects edit the bookmark model -// and temporarily save a copy of the state (e.g. bookmark button -// editor). As a fail-safe, these objects want an easy cancel if the -// model changes out from under them. For example, if you have the -// bookmark button editor sheet open, then edit the bookmark in the -// bookmark manager, we'd want to simply cancel the editor. -// -// This class is conservative and may result in notifications which -// aren't strictly necessary. For example, node removal only needs to -// cancel an edit if the removed node is a folder (editors often have -// a list of "new parents"). But, just to be sure, notification -// happens on any removal. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_MODEL_OBSERVER_FOR_COCOA_H -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_MODEL_OBSERVER_FOR_COCOA_H -#pragma once - -#import <Cocoa/Cocoa.h> - -#include "base/basictypes.h" -#include "base/scoped_nsobject.h" -#include "chrome/browser/bookmarks/bookmark_model.h" -#include "chrome/browser/bookmarks/bookmark_model_observer.h" - -class BookmarkModelObserverForCocoa : public BookmarkModelObserver { - public: - // When |node| in |model| changes, send |selector| to |object|. - // Assumes |selector| is a selector that takes one arg, like an - // IBOutlet. The arg passed is nil. - // Many notifications happen independently of node - // (e.g. BeingDeleted), so |node| can be nil. - // - // |object| is NOT retained, since the expected use case is for - // ||object| to own the BookmarkModelObserverForCocoa and we don't - // want a retain cycle. - BookmarkModelObserverForCocoa(const BookmarkNode* node, - BookmarkModel* model, - NSObject* object, - SEL selector) { - DCHECK(model); - node_ = node; - model_ = model; - object_ = object; - selector_ = selector; - model_->AddObserver(this); - } - virtual ~BookmarkModelObserverForCocoa() { - model_->RemoveObserver(this); - } - - virtual void BookmarkModelBeingDeleted(BookmarkModel* model) { - Notify(); - } - virtual void BookmarkNodeMoved(BookmarkModel* model, - const BookmarkNode* old_parent, - int old_index, - const BookmarkNode* new_parent, - int new_index) { - // Editors often have a tree of parents, so movement of folders - // must cause a cancel. - Notify(); - } - virtual void BookmarkNodeRemoved(BookmarkModel* model, - const BookmarkNode* parent, - int old_index, - const BookmarkNode* node) { - // See comment in BookmarkNodeMoved. - Notify(); - } - virtual void BookmarkNodeChanged(BookmarkModel* model, - const BookmarkNode* node) { - if ((node_ == node) || (!node_)) - Notify(); - } - virtual void BookmarkImportBeginning(BookmarkModel* model) { - // Be conservative. - Notify(); - } - - // Some notifications we don't care about, but by being pure virtual - // in the base class we must implement them. - virtual void Loaded(BookmarkModel* model) { - } - virtual void BookmarkNodeAdded(BookmarkModel* model, - const BookmarkNode* parent, - int index) { - } - virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model, - const BookmarkNode* node) { - } - virtual void BookmarkNodeChildrenReordered(BookmarkModel* model, - const BookmarkNode* node) { - } - - virtual void BookmarkImportEnding(BookmarkModel* model) { - } - - private: - const BookmarkNode* node_; // Weak; owned by a BookmarkModel. - BookmarkModel* model_; // Weak; it is owned by a Profile. - NSObject* object_; // Weak, like a delegate. - SEL selector_; - - void Notify() { - [object_ performSelector:selector_ withObject:nil]; - } - - DISALLOW_COPY_AND_ASSIGN(BookmarkModelObserverForCocoa); -}; - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_MODEL_OBSERVER_FOR_COCOA_H diff --git a/chrome/browser/cocoa/bookmarks/bookmark_model_observer_for_cocoa_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_model_observer_for_cocoa_unittest.mm deleted file mode 100644 index a84cc53..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_model_observer_for_cocoa_unittest.mm +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#include "base/scoped_ptr.h" -#include "base/scoped_nsobject.h" -#include "base/utf_string_conversions.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_model_observer_for_cocoa.h" -#import "chrome/browser/cocoa/browser_test_helper.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" - -// Keep track of bookmark pings. -@interface ObserverPingTracker : NSObject { - @public - int pings; -} -@end - -@implementation ObserverPingTracker -- (void)pingMe:(id)sender { - pings++; -} -@end - -namespace { - -class BookmarkModelObserverForCocoaTest : public CocoaTest { - public: - BrowserTestHelper helper_; - - BookmarkModelObserverForCocoaTest() {} - virtual ~BookmarkModelObserverForCocoaTest() {} - private: - DISALLOW_COPY_AND_ASSIGN(BookmarkModelObserverForCocoaTest); -}; - - -TEST_F(BookmarkModelObserverForCocoaTest, TestCallback) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* node = model->AddURL(model->GetBookmarkBarNode(), - 0, ASCIIToUTF16("super"), - GURL("http://www.google.com")); - - scoped_nsobject<ObserverPingTracker> - pingCount([[ObserverPingTracker alloc] init]); - - scoped_ptr<BookmarkModelObserverForCocoa> - observer(new BookmarkModelObserverForCocoa(node, model, - pingCount, - @selector(pingMe:))); - - EXPECT_EQ(0, pingCount.get()->pings); - - model->SetTitle(node, ASCIIToUTF16("duper")); - EXPECT_EQ(1, pingCount.get()->pings); - model->SetURL(node, GURL("http://www.google.com/reader")); - EXPECT_EQ(2, pingCount.get()->pings); - - model->Move(node, model->other_node(), 0); - EXPECT_EQ(3, pingCount.get()->pings); - - model->Remove(node->GetParent(), 0); - EXPECT_EQ(4, pingCount.get()->pings); -} - -} // namespace diff --git a/chrome/browser/cocoa/bookmarks/bookmark_name_folder_controller.h b/chrome/browser/cocoa/bookmarks/bookmark_name_folder_controller.h deleted file mode 100644 index 7de139f..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_name_folder_controller.h +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_NAME_FOLDER_CONTROLLER_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_NAME_FOLDER_CONTROLLER_H_ -#pragma once - -#import <Cocoa/Cocoa.h> - -#include "base/scoped_nsobject.h" -#include "base/scoped_ptr.h" -#include "chrome/browser/bookmarks/bookmark_model.h" - -class BookmarkModelObserverForCocoa; - -// A controller for dialog to let the user create a new folder or -// rename an existing folder. Accessible from a context menu on a -// bookmark button or the bookmark bar. -@interface BookmarkNameFolderController : NSWindowController { - @private - IBOutlet NSTextField* nameField_; - IBOutlet NSButton* okButton_; - - NSWindow* parentWindow_; // weak - Profile* profile_; // weak - - // Weak; owned by the model. Can be NULL (see below). Either node_ - // is non-NULL (renaming a folder), or parent_ is non-NULL (adding a - // new one). - const BookmarkNode* node_; - const BookmarkNode* parent_; - int newIndex_; - - scoped_nsobject<NSString> initialName_; - - // Ping me when things change out from under us. - scoped_ptr<BookmarkModelObserverForCocoa> observer_; -} - -// Use the 1st initializer for a "rename existing folder" request. -// -// Use the 2nd initializer for an "add folder" request. If creating a -// new folder |parent| and |newIndex| specify where to put the new -// node. -- (id)initWithParentWindow:(NSWindow*)window - profile:(Profile*)profile - node:(const BookmarkNode*)node; -- (id)initWithParentWindow:(NSWindow*)window - profile:(Profile*)profile - parent:(const BookmarkNode*)parent - newIndex:(int)newIndex; -- (void)runAsModalSheet; -- (IBAction)cancel:(id)sender; -- (IBAction)ok:(id)sender; -@end - -@interface BookmarkNameFolderController(TestingAPI) -- (NSString*)folderName; -- (void)setFolderName:(NSString*)name; -- (NSButton*)okButton; -@end - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_NAME_FOLDER_CONTROLLER_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_name_folder_controller.mm b/chrome/browser/cocoa/bookmarks/bookmark_name_folder_controller.mm deleted file mode 100644 index fe8cdfd..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_name_folder_controller.mm +++ /dev/null @@ -1,123 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_name_folder_controller.h" - -#include "app/l10n_util.h" -#include "app/l10n_util_mac.h" -#include "base/mac_util.h" -#include "base/sys_string_conversions.h" -#include "chrome/browser/cocoa/bookmarks/bookmark_model_observer_for_cocoa.h" -#include "chrome/browser/profile.h" -#include "grit/generated_resources.h" - -@implementation BookmarkNameFolderController - -// Common initializer (private). -- (id)initWithParentWindow:(NSWindow*)window - profile:(Profile*)profile - node:(const BookmarkNode*)node - parent:(const BookmarkNode*)parent - newIndex:(int)newIndex { - NSString* nibpath = [mac_util::MainAppBundle() - pathForResource:@"BookmarkNameFolder" - ofType:@"nib"]; - if ((self = [super initWithWindowNibPath:nibpath owner:self])) { - parentWindow_ = window; - profile_ = profile; - node_ = node; - parent_ = parent; - newIndex_ = newIndex; - if (parent) { - DCHECK_LE(newIndex, parent->GetChildCount()); - } - if (node_) { - initialName_.reset([base::SysUTF16ToNSString(node_->GetTitle()) retain]); - } else { - NSString* newString = - l10n_util::GetNSStringWithFixup(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME); - initialName_.reset([newString retain]); - } - } - return self; -} - -- (id)initWithParentWindow:(NSWindow*)window - profile:(Profile*)profile - node:(const BookmarkNode*)node { - DCHECK(node); - return [self initWithParentWindow:window - profile:profile - node:node - parent:nil - newIndex:0]; -} - -- (id)initWithParentWindow:(NSWindow*)window - profile:(Profile*)profile - parent:(const BookmarkNode*)parent - newIndex:(int)newIndex { - DCHECK(parent); - return [self initWithParentWindow:window - profile:profile - node:nil - parent:parent - newIndex:newIndex]; -} - -- (void)awakeFromNib { - [nameField_ setStringValue:initialName_.get()]; -} - -- (void)runAsModalSheet { - // Ping me when things change out from under us. - observer_.reset(new BookmarkModelObserverForCocoa( - node_, profile_->GetBookmarkModel(), - self, - @selector(cancel:))); - [NSApp beginSheet:[self window] - modalForWindow:parentWindow_ - modalDelegate:self - didEndSelector:@selector(didEndSheet:returnCode:contextInfo:) - contextInfo:nil]; -} - -- (IBAction)cancel:(id)sender { - [NSApp endSheet:[self window]]; -} - -- (IBAction)ok:(id)sender { - NSString* name = [nameField_ stringValue]; - BookmarkModel* model = profile_->GetBookmarkModel(); - if (node_) { - model->SetTitle(node_, base::SysNSStringToUTF16(name)); - } else { - model->AddGroup(parent_, - newIndex_, - base::SysNSStringToUTF16(name)); - } - [NSApp endSheet:[self window]]; -} - -- (void)didEndSheet:(NSWindow*)sheet - returnCode:(int)returnCode - contextInfo:(void*)contextInfo { - [[self window] orderOut:self]; - observer_.reset(NULL); - [self autorelease]; -} - -- (NSString*)folderName { - return [nameField_ stringValue]; -} - -- (void)setFolderName:(NSString*)name { - [nameField_ setStringValue:name]; -} - -- (NSButton*)okButton { - return okButton_; -} - -@end // BookmarkNameFolderController diff --git a/chrome/browser/cocoa/bookmarks/bookmark_name_folder_controller_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_name_folder_controller_unittest.mm deleted file mode 100644 index 3a435f7..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_name_folder_controller_unittest.mm +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#include "base/scoped_nsobject.h" -#include "base/utf_string_conversions.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_name_folder_controller.h" -#include "chrome/browser/cocoa/browser_test_helper.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" -#include "testing/gtest/include/gtest/gtest.h" -#import "testing/gtest_mac.h" -#include "testing/platform_test.h" - -class BookmarkNameFolderControllerTest : public CocoaTest { - public: - BrowserTestHelper helper_; -}; - - -// Simple add of a node (at the end). -TEST_F(BookmarkNameFolderControllerTest, AddNew) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - EXPECT_EQ(0, parent->GetChildCount()); - - scoped_nsobject<BookmarkNameFolderController> - controller([[BookmarkNameFolderController alloc] - initWithParentWindow:test_window() - profile:helper_.profile() - parent:parent - newIndex:0]); - [controller window]; // force nib load - - // Do nothing. - [controller cancel:nil]; - EXPECT_EQ(0, parent->GetChildCount()); - - // Change name then cancel. - [controller setFolderName:@"Bozo"]; - [controller cancel:nil]; - EXPECT_EQ(0, parent->GetChildCount()); - - // Add a new folder. - [controller ok:nil]; - EXPECT_EQ(1, parent->GetChildCount()); - EXPECT_TRUE(parent->GetChild(0)->is_folder()); - EXPECT_EQ(ASCIIToUTF16("Bozo"), parent->GetChild(0)->GetTitle()); -} - -// Add new but specify a sibling. -TEST_F(BookmarkNameFolderControllerTest, AddNewWithSibling) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - - // Add 2 nodes. We will place the new folder in the middle of these. - model->AddURL(parent, 0, ASCIIToUTF16("title 1"), - GURL("http://www.google.com")); - model->AddURL(parent, 1, ASCIIToUTF16("title 3"), - GURL("http://www.google.com")); - EXPECT_EQ(2, parent->GetChildCount()); - - scoped_nsobject<BookmarkNameFolderController> - controller([[BookmarkNameFolderController alloc] - initWithParentWindow:test_window() - profile:helper_.profile() - parent:parent - newIndex:1]); - [controller window]; // force nib load - - // Add a new folder. - [controller setFolderName:@"middle"]; - [controller ok:nil]; - - // Confirm we now have 3, and that the new one is in the middle. - EXPECT_EQ(3, parent->GetChildCount()); - EXPECT_TRUE(parent->GetChild(1)->is_folder()); - EXPECT_EQ(ASCIIToUTF16("middle"), parent->GetChild(1)->GetTitle()); -} - -// Make sure we are allowed to create a folder named "New Folder". -TEST_F(BookmarkNameFolderControllerTest, AddNewDefaultName) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - EXPECT_EQ(0, parent->GetChildCount()); - - scoped_nsobject<BookmarkNameFolderController> - controller([[BookmarkNameFolderController alloc] - initWithParentWindow:test_window() - profile:helper_.profile() - parent:parent - newIndex:0]); - - [controller window]; // force nib load - - // Click OK without changing the name - [controller ok:nil]; - EXPECT_EQ(1, parent->GetChildCount()); - EXPECT_TRUE(parent->GetChild(0)->is_folder()); -} - -// Make sure we are allowed to create a folder with an empty name. -TEST_F(BookmarkNameFolderControllerTest, AddNewBlankName) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - EXPECT_EQ(0, parent->GetChildCount()); - - scoped_nsobject<BookmarkNameFolderController> - controller([[BookmarkNameFolderController alloc] - initWithParentWindow:test_window() - profile:helper_.profile() - parent:parent - newIndex:0]); - [controller window]; // force nib load - - // Change the name to blank, click OK. - [controller setFolderName:@""]; - [controller ok:nil]; - EXPECT_EQ(1, parent->GetChildCount()); - EXPECT_TRUE(parent->GetChild(0)->is_folder()); -} - -TEST_F(BookmarkNameFolderControllerTest, Rename) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - const BookmarkNode* folder = model->AddGroup(parent, - parent->GetChildCount(), - ASCIIToUTF16("group")); - - // Rename the folder by creating a controller that originates from - // the node. - scoped_nsobject<BookmarkNameFolderController> - controller([[BookmarkNameFolderController alloc] - initWithParentWindow:test_window() - profile:helper_.profile() - node:folder]); - [controller window]; // force nib load - - EXPECT_NSEQ(@"group", [controller folderName]); - [controller setFolderName:@"Zobo"]; - [controller ok:nil]; - EXPECT_EQ(1, parent->GetChildCount()); - EXPECT_TRUE(parent->GetChild(0)->is_folder()); - EXPECT_EQ(ASCIIToUTF16("Zobo"), parent->GetChild(0)->GetTitle()); -} - -TEST_F(BookmarkNameFolderControllerTest, EditAndConfirmOKButton) { - BookmarkModel* model = helper_.profile()->GetBookmarkModel(); - const BookmarkNode* parent = model->GetBookmarkBarNode(); - EXPECT_EQ(0, parent->GetChildCount()); - - scoped_nsobject<BookmarkNameFolderController> - controller([[BookmarkNameFolderController alloc] - initWithParentWindow:test_window() - profile:helper_.profile() - parent:parent - newIndex:0]); - [controller window]; // force nib load - - // We start enabled since the default "New Folder" is added for us. - EXPECT_TRUE([[controller okButton] isEnabled]); - - [controller setFolderName:@"Bozo"]; - EXPECT_TRUE([[controller okButton] isEnabled]); - [controller setFolderName:@" "]; - EXPECT_TRUE([[controller okButton] isEnabled]); - - [controller setFolderName:@""]; - EXPECT_TRUE([[controller okButton] isEnabled]); -} - diff --git a/chrome/browser/cocoa/bookmarks/bookmark_tree_browser_cell.h b/chrome/browser/cocoa/bookmarks/bookmark_tree_browser_cell.h deleted file mode 100644 index 7cdee51..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_tree_browser_cell.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_TREE_BROWSER_CELL_H_ -#define CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_TREE_BROWSER_CELL_H_ -#pragma once - -#import <Cocoa/Cocoa.h> - -class BookmarkNode; - -// Provides a custom cell as used in the BookmarkEditor.xib's folder tree -// browser view. This cell customization adds target and action support -// not provided by the NSBrowserCell as well as contextual information -// identifying the bookmark node being edited and the column matrix -// control in which is contained the cell. -@interface BookmarkTreeBrowserCell : NSBrowserCell { - @private - const BookmarkNode* bookmarkNode_; // weak - NSMatrix* matrix_; // weak - id target_; // weak - SEL action_; -} - -@property (nonatomic, assign) NSMatrix* matrix; -@property (nonatomic, assign) id target; -@property (nonatomic, assign) SEL action; - -- (const BookmarkNode*)bookmarkNode; -- (void)setBookmarkNode:(const BookmarkNode*)bookmarkNode; - -@end - -#endif // CHROME_BROWSER_COCOA_BOOKMARKS_BOOKMARK_TREE_BROWSER_CELL_H_ diff --git a/chrome/browser/cocoa/bookmarks/bookmark_tree_browser_cell.mm b/chrome/browser/cocoa/bookmarks/bookmark_tree_browser_cell.mm deleted file mode 100644 index b778858..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_tree_browser_cell.mm +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/bookmarks/bookmark_tree_browser_cell.h" - -#include "chrome/browser/bookmarks/bookmark_model.h" - -@implementation BookmarkTreeBrowserCell - -@synthesize matrix = matrix_; -@synthesize target = target_; -@synthesize action = action_; - -- (const BookmarkNode*)bookmarkNode { - return bookmarkNode_; -} - -- (void)setBookmarkNode:(const BookmarkNode*)bookmarkNode { - bookmarkNode_ = bookmarkNode; -} - -@end diff --git a/chrome/browser/cocoa/bookmarks/bookmark_tree_browser_cell_unittest.mm b/chrome/browser/cocoa/bookmarks/bookmark_tree_browser_cell_unittest.mm deleted file mode 100644 index a8418e2..0000000 --- a/chrome/browser/cocoa/bookmarks/bookmark_tree_browser_cell_unittest.mm +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import <Cocoa/Cocoa.h> - -#include "chrome/browser/bookmarks/bookmark_model.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_tree_browser_cell.h" -#import "chrome/browser/cocoa/cocoa_test_helper.h" -#include "testing/platform_test.h" - -class BookmarkTreeBrowserCellTest : public PlatformTest { - public: - BookmarkTreeBrowserCellTest() { - // Set up our mocks. - GURL gurl; - bookmarkNodeMock_.reset(new BookmarkNode(gurl)); - matrixMock_.reset([[NSMatrix alloc] init]); - targetMock_.reset([[NSObject alloc] init]); - } - - scoped_ptr<BookmarkNode> bookmarkNodeMock_; - scoped_nsobject<NSMatrix> matrixMock_; - scoped_nsobject<NSObject> targetMock_; -}; - -TEST_F(BookmarkTreeBrowserCellTest, BasicAllocDealloc) { - BookmarkTreeBrowserCell* cell = [[[BookmarkTreeBrowserCell alloc] - initTextCell:@"TEST STRING"] autorelease]; - [cell setMatrix:matrixMock_.get()]; - [cell setTarget:targetMock_.get()]; - [cell setAction:@selector(mockAction:)]; - [cell setBookmarkNode:bookmarkNodeMock_.get()]; - - NSMatrix* testMatrix = [cell matrix]; - EXPECT_EQ(testMatrix, matrixMock_.get()); - id testTarget = [cell target]; - EXPECT_EQ(testTarget, targetMock_.get()); - SEL testAction = [cell action]; - EXPECT_EQ(testAction, @selector(mockAction:)); - const BookmarkNode* testBookmarkNode = [cell bookmarkNode]; - EXPECT_EQ(testBookmarkNode, bookmarkNodeMock_.get()); -} |