summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa/bookmark_bar_controller.mm
diff options
context:
space:
mode:
authorjrg@chromium.org <jrg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-23 23:56:19 +0000
committerjrg@chromium.org <jrg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-23 23:56:19 +0000
commitcb514b8024f425b974c680b06dcace135b32de39 (patch)
tree79a1530164118302eee0562b6bdf58f3a022f880 /chrome/browser/cocoa/bookmark_bar_controller.mm
parentb1e8cb31f85869171dcb383c7ec5e8176833ec04 (diff)
downloadchromium_src-cb514b8024f425b974c680b06dcace135b32de39.zip
chromium_src-cb514b8024f425b974c680b06dcace135b32de39.tar.gz
chromium_src-cb514b8024f425b974c680b06dcace135b32de39.tar.bz2
Added menus for bookmark bar folders. This is NOT based on the Cole
prototype; it is an attempt to get something functional in the short term, and have a visual baseline before doing something new. Added folder icons for bookmark bar folder buttons. Added an "off the side" button/menu for bookmark buttons which don't fit on the bar. Updated "Add page..." item to allow creating bookmarks in the folders (if selected over a folder button). BUG=http://crbug.com/8381 TEST=Here we go: 1) Make sure bookmark bar folders have the "folder" icon. 2) Right click on a folder --> Add Page, and add a bookmark. Make sure bookmark is now in the folder, not at the top level. 3) (Oh, you just implicitly verified you can open bookmark folders!) 4) Add 5 bookmarks then shrink the window thinner so all bookmark buttons don't fit. Make sure "off the right" button gets enabled (on right side of bar) and shows bookmarks in a pop-up menu (when clicked) that don't completely fit on the bar. 5) Make it super-wide so the all fit and make sure "off the right" button is disabled. 6) Add a bunch of bookmarks to a folder; make sure they all work. 7) Add nested folders (by editing the bookmark pref file and restarting Chrome) and make sure bookmark folder buttons have nested/cascading menus. Review URL: http://codereview.chromium.org/159286 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21479 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/cocoa/bookmark_bar_controller.mm')
-rw-r--r--chrome/browser/cocoa/bookmark_bar_controller.mm208
1 files changed, 193 insertions, 15 deletions
diff --git a/chrome/browser/cocoa/bookmark_bar_controller.mm b/chrome/browser/cocoa/bookmark_bar_controller.mm
index fd2e0fe..d32ecbf 100644
--- a/chrome/browser/cocoa/bookmark_bar_controller.mm
+++ b/chrome/browser/cocoa/bookmark_bar_controller.mm
@@ -10,10 +10,11 @@
#include "chrome/browser/browser_list.h"
#import "chrome/browser/cocoa/bookmark_bar_bridge.h"
#import "chrome/browser/cocoa/bookmark_bar_controller.h"
-#import "chrome/browser/cocoa/bookmark_bar_view.h"
#import "chrome/browser/cocoa/bookmark_button_cell.h"
#import "chrome/browser/cocoa/bookmark_editor_controller.h"
#import "chrome/browser/cocoa/bookmark_name_folder_controller.h"
+#import "chrome/browser/cocoa/bookmark_menu_cocoa_controller.h"
+#include "chrome/browser/cocoa/nsimage_cache.h"
#include "chrome/browser/profile.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/pref_service.h"
@@ -22,6 +23,10 @@
@interface BookmarkBarController(Private)
- (void)applyContentAreaOffset:(BOOL)apply immediately:(BOOL)immediately;
- (void)showBookmarkBar:(BOOL)enable immediately:(BOOL)immediately;
+- (void)addNode:(const BookmarkNode*)child toMenu:(NSMenu*)menu;
+- (void)addFolderNode:(const BookmarkNode*)node toMenu:(NSMenu*)menu;
+- (void)tagEmptyMenu:(NSMenu*)menu;
+- (void)clearMenuTagMap;
@end
namespace {
@@ -62,6 +67,11 @@ const CGFloat kBookmarkHorizontalPadding = 1.0;
return self;
}
+- (void)dealloc {
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [super dealloc];
+}
+
- (void)awakeFromNib {
// We default to NOT open, which means height=0.
DCHECK([[self view] isHidden]); // Hidden so it's OK to change.
@@ -81,6 +91,40 @@ const CGFloat kBookmarkHorizontalPadding = 1.0;
// 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_));
+
+ // 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]];
+}
+
+// Check if we should enable the off-the-side button.
+// TODO(jrg): when we are smarter about creating buttons (e.g. don't
+// bother creating buttons which aren't visible), we'll have to be
+// smarter here too.
+- (void)checkEnableOffTheSideButton {
+ NSButton* button = [buttons_ lastObject];
+ if ((!button) ||
+ (NSMaxX([button frame]) <=
+ NSMaxX([[button superview] frame]))) {
+ [offTheSideButton_ setEnabled:NO];
+ } else {
+ [offTheSideButton_ setEnabled:YES];
+ }
+}
+
+- (BOOL)offTheSideButtonIsEnabled {
+ return [offTheSideButton_ isEnabled];
+}
+
+// Called when our controlled frame has changed size.
+// TODO(jrg): be smarter (e.g. add/remove buttons as appropriate).
+- (void)frameDidChange {
+ [self checkEnableOffTheSideButton];
}
// Show or hide the bar based on the value of |show|. Handles
@@ -218,6 +262,120 @@ const CGFloat kBookmarkHorizontalPadding = 1.0;
[delegate_ openBookmarkURL:node->GetURL() disposition:CURRENT_TAB];
}
+// Given a NSMenuItem tag, return the appropriate bookmark node id.
+- (int64)nodeIdFromMenuTag:(int32)tag {
+ return menuTagMap_[tag];
+}
+
+// Create and return a new tag for the given node id.
+- (int32)menuTagFromNodeId:(int64)menuid {
+ int tag = seedId_++;
+ menuTagMap_[tag] = menuid;
+ return tag;
+}
+
+- (void)clearMenuTagMap {
+ seedId_ = 0;
+ menuTagMap_.clear();
+}
+
+// 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];
+ 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::SysWideToNSString(child->GetTitle()),
+ url_string.c_str()];
+ [item setToolTip:tooltip];
+
+ }
+}
+
+// Empty menus are odd; if empty, add something to look at.
+// Matches windows behavior.
+// TODO(jrg): localize.
+- (void)tagEmptyMenu:(NSMenu*)menu {
+ [menu addItem:[[[NSMenuItem alloc] initWithTitle:@"(empty)"
+ 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::SysWideToNSString(node->GetTitle());
+ NSMenu* menu = [[[NSMenu alloc] initWithTitle:title] autorelease];
+ [self addFolderNode:node toMenu:menu];
+
+ if (![menu numberOfItems]) {
+ [self tagEmptyMenu:menu];
+ }
+ return menu;
+}
+
+// Called from a Folder bookmark button.
+- (IBAction)openFolderMenuFromButton:(id)sender {
+ NSMenu* menu = [self menuForFolderNode:[self nodeFromButton:sender]];
+ if (menu) {
+ [NSMenu popUpContextMenu:menu
+ withEvent:[NSApp currentEvent]
+ forView:sender];
+ }
+}
+
+// TODO(jrg): cache the menu so we don't need to build it every time.
+// TODO(jrg): if we get smarter such that we don't even bother
+// creating buttons which aren't visible, we'll need to be smarter
+// here.
+- (IBAction)openOffTheSideMenuFromButton:(id)sender {
+ scoped_nsobject<NSMenu> menu([[NSMenu alloc] initWithTitle:@""]);
+ for (NSButton* each_button in buttons_.get()) {
+ if (NSMaxX([each_button frame]) >
+ NSMaxX([[each_button superview] frame])) {
+ [self addNode:[self nodeFromButton:each_button] toMenu:menu.get()];
+ }
+ }
+
+ // TODO(jrg): once we disable the button when the menu should be
+ // empty, remove this 'helper'.
+ if (![menu numberOfItems]) {
+ [self tagEmptyMenu:menu];
+ }
+
+ [NSMenu popUpContextMenu:menu
+ withEvent:[NSApp currentEvent]
+ forView:sender];
+}
+
// As a convention we set the menu's delegate to be the button's cell
// so we can easily obtain bookmark info. Convention applied in
// -[BookmarkButtonCell menu].
@@ -289,10 +447,15 @@ const CGFloat kBookmarkHorizontalPadding = 1.0;
[self openBookmarkNodesRecursive:node];
}
+// 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] contentView],
profile_,
- bookmarkModel_->GetBookmarkBarNode(),
+ parent,
nil,
BookmarkEditor::SHOW_TREE,
nil);
@@ -314,10 +477,16 @@ const CGFloat kBookmarkHorizontalPadding = 1.0;
// ends.
}
-// Delete all bookmarks from the bookmark bar.
+- (NSView*)buttonView {
+ return buttonView_;
+}
+
+// Delete all bookmarks from the bookmark bar, and reset knowledge of
+// bookmarks.
- (void)clearBookmarkBar {
[buttons_ makeObjectsPerformSelector:@selector(removeFromSuperview)];
[buttons_ removeAllObjects];
+ [self clearMenuTagMap];
}
// Return an autoreleased NSCell suitable for a bookmark button.
@@ -329,16 +498,19 @@ const CGFloat kBookmarkHorizontalPadding = 1.0;
DCHECK(cell);
[cell setRepresentedObject:[NSValue valueWithPointer:node]];
- // The favicon may be NULL if we haven't loaded it yet. Bookmarks
- // (and their icons) are loaded on the IO thread to speed launch.
- const SkBitmap& favicon = bookmarkModel_->GetFavIcon(node);
- if (!favicon.isNull()) {
- NSImage* image = gfx::SkBitmapToNSImage(favicon);
- if (image) {
- [cell setImage:image];
- [cell setImagePosition:NSImageLeft];
+ NSImage* image = NULL;
+ if (node->is_folder()) {
+ image = nsimage_cache::ImageNamed(@"bookmark_bar_folder.png");
+ } else {
+ const SkBitmap& favicon = bookmarkModel_->GetFavIcon(node);
+ if (!favicon.isNull()) {
+ image = gfx::SkBitmapToNSImage(favicon);
}
}
+ if (image) {
+ [cell setImage:image];
+ [cell setImagePosition:NSImageLeft];
+ }
[cell setTitle:title];
[cell setMenu:buttonContextMenu_];
return cell;
@@ -397,6 +569,12 @@ const CGFloat kBookmarkHorizontalPadding = 1.0;
}
}
+- (IBAction)openBookmarkMenuItem:(id)sender {
+ int64 tag = [self nodeIdFromMenuTag:[sender tag]];
+ const BookmarkNode* node = bookmarkModel_->GetNodeByID(tag);
+ [delegate_ openBookmarkURL:node->GetURL() disposition:CURRENT_TAB];
+}
+
// Add all items from the given model to our bookmark bar.
// TODO(jrg): lots of things!
// - bookmark folders (e.g. menu from the button)
@@ -427,9 +605,8 @@ const CGFloat kBookmarkHorizontalPadding = 1.0;
[button setCell:cell];
if (child->is_folder()) {
- // For now just disable the button if it's a folder.
- // TODO(jrg): recurse.
- [button setEnabled:NO];
+ [button setTarget:self];
+ [button setAction:@selector(openFolderMenuFromButton:)];
} else {
// Make the button do something
[button setTarget:self];
@@ -442,7 +619,7 @@ const CGFloat kBookmarkHorizontalPadding = 1.0;
[button setToolTip:tooltip];
}
// Finally, add it to the bookmark bar.
- [[self view] addSubview:button];
+ [buttonView_ addSubview:button];
}
}
@@ -456,6 +633,7 @@ const CGFloat kBookmarkHorizontalPadding = 1.0;
const BookmarkNode* node = model->GetBookmarkBarNode();
[self clearBookmarkBar];
[self addNodesToBar:node];
+ [self checkEnableOffTheSideButton];
}
- (void)beingDeleted:(BookmarkModel*)model {