diff options
author | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-01 16:34:49 +0000 |
---|---|---|
committer | ben@chromium.org <ben@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-01 16:34:49 +0000 |
commit | 7d791652c7ede4209a2014d885148e2713f49bce (patch) | |
tree | c26baf12593bed381c631b81c736106809d46b44 /chrome/browser/ui/cocoa/menu_controller.mm | |
parent | 3b94427c99bdf12836fd455eeb1499fdde511e26 (diff) | |
download | chromium_src-7d791652c7ede4209a2014d885148e2713f49bce.zip chromium_src-7d791652c7ede4209a2014d885148e2713f49bce.tar.gz chromium_src-7d791652c7ede4209a2014d885148e2713f49bce.tar.bz2 |
Move browser/cocoa to browser/ui/cocoa
BUG=none
TEST=none
TBR=brettw
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@67854 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ui/cocoa/menu_controller.mm')
-rw-r--r-- | chrome/browser/ui/cocoa/menu_controller.mm | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/chrome/browser/ui/cocoa/menu_controller.mm b/chrome/browser/ui/cocoa/menu_controller.mm new file mode 100644 index 0000000..47f0c34 --- /dev/null +++ b/chrome/browser/ui/cocoa/menu_controller.mm @@ -0,0 +1,185 @@ +// 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/ui/cocoa/menu_controller.h" + +#include "app/l10n_util_mac.h" +#include "app/menus/accelerator_cocoa.h" +#include "app/menus/simple_menu_model.h" +#include "base/logging.h" +#include "base/sys_string_conversions.h" +#include "skia/ext/skia_utils_mac.h" +#include "third_party/skia/include/core/SkBitmap.h" + +@interface MenuController (Private) +- (NSMenu*)menuFromModel:(menus::MenuModel*)model; +- (void)addSeparatorToMenu:(NSMenu*)menu + atIndex:(int)index; +@end + +@implementation MenuController + +@synthesize model = model_; +@synthesize useWithPopUpButtonCell = useWithPopUpButtonCell_; + +- (id)init { + self = [super init]; + return self; +} + +- (id)initWithModel:(menus::MenuModel*)model + useWithPopUpButtonCell:(BOOL)useWithCell { + if ((self = [super init])) { + model_ = model; + useWithPopUpButtonCell_ = useWithCell; + [self menu]; + } + return self; +} + +- (void)dealloc { + model_ = NULL; + [super dealloc]; +} + +// Creates a NSMenu from the given model. If the model has submenus, this can +// be invoked recursively. +- (NSMenu*)menuFromModel:(menus::MenuModel*)model { + NSMenu* menu = [[[NSMenu alloc] initWithTitle:@""] autorelease]; + + // The indices may not always start at zero (the windows system menu is one + // example where this is used) so just make sure we can handle it. + // SimpleMenuModel currently always starts at 0. + int firstItemIndex = model->GetFirstItemIndex(menu); + DCHECK(firstItemIndex == 0); + const int count = model->GetItemCount(); + for (int index = firstItemIndex; index < firstItemIndex + count; index++) { + int modelIndex = index - firstItemIndex; + if (model->GetTypeAt(modelIndex) == menus::MenuModel::TYPE_SEPARATOR) { + [self addSeparatorToMenu:menu atIndex:index]; + } else { + [self addItemToMenu:menu atIndex:index fromModel:model + modelIndex:modelIndex]; + } + } + + return menu; +} + +// Adds a separator item at the given index. As the separator doesn't need +// anything from the model, this method doesn't need the model index as the +// other method below does. +- (void)addSeparatorToMenu:(NSMenu*)menu + atIndex:(int)index { + NSMenuItem* separator = [NSMenuItem separatorItem]; + [menu insertItem:separator atIndex:index]; +} + +// Adds an item or a hierarchical menu to the item at the |index|, +// associated with the entry in the model indentifed by |modelIndex|. +- (void)addItemToMenu:(NSMenu*)menu + atIndex:(NSInteger)index + fromModel:(menus::MenuModel*)model + modelIndex:(int)modelIndex { + NSString* label = + l10n_util::FixUpWindowsStyleLabel(model->GetLabelAt(modelIndex)); + scoped_nsobject<NSMenuItem> item( + [[NSMenuItem alloc] initWithTitle:label + action:@selector(itemSelected:) + keyEquivalent:@""]); + + // If the menu item has an icon, set it. + SkBitmap skiaIcon; + if (model->GetIconAt(modelIndex, &skiaIcon) && !skiaIcon.isNull()) { + NSImage* icon = gfx::SkBitmapToNSImage(skiaIcon); + if (icon) { + [item setImage:icon]; + } + } + + menus::MenuModel::ItemType type = model->GetTypeAt(modelIndex); + if (type == menus::MenuModel::TYPE_SUBMENU) { + // Recursively build a submenu from the sub-model at this index. + [item setTarget:nil]; + [item setAction:nil]; + menus::MenuModel* submenuModel = model->GetSubmenuModelAt(modelIndex); + NSMenu* submenu = + [self menuFromModel:(menus::SimpleMenuModel*)submenuModel]; + [item setSubmenu:submenu]; + } else { + // The MenuModel works on indexes so we can't just set the command id as the + // tag like we do in other menus. Also set the represented object to be + // the model so hierarchical menus check the correct index in the correct + // model. Setting the target to |self| allows this class to participate + // in validation of the menu items. + [item setTag:modelIndex]; + [item setTarget:self]; + NSValue* modelObject = [NSValue valueWithPointer:model]; + [item setRepresentedObject:modelObject]; // Retains |modelObject|. + menus::AcceleratorCocoa accelerator; + if (model->GetAcceleratorAt(modelIndex, &accelerator)) { + [item setKeyEquivalent:accelerator.characters()]; + [item setKeyEquivalentModifierMask:accelerator.modifiers()]; + } + } + [menu insertItem:item atIndex:index]; +} + +// Called before the menu is to be displayed to update the state (enabled, +// radio, etc) of each item in the menu. Also will update the title if +// the item is marked as "dynamic". +- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item { + SEL action = [item action]; + if (action != @selector(itemSelected:)) + return NO; + + NSInteger modelIndex = [item tag]; + menus::MenuModel* model = + static_cast<menus::MenuModel*>( + [[(id)item representedObject] pointerValue]); + DCHECK(model); + if (model) { + BOOL checked = model->IsItemCheckedAt(modelIndex); + DCHECK([(id)item isKindOfClass:[NSMenuItem class]]); + [(id)item setState:(checked ? NSOnState : NSOffState)]; + [(id)item setHidden:(!model->IsVisibleAt(modelIndex))]; + if (model->IsLabelDynamicAt(modelIndex)) { + NSString* label = + l10n_util::FixUpWindowsStyleLabel(model->GetLabelAt(modelIndex)); + [(id)item setTitle:label]; + } + return model->IsEnabledAt(modelIndex); + } + return NO; +} + +// Called when the user chooses a particular menu item. |sender| is the menu +// item chosen. +- (void)itemSelected:(id)sender { + NSInteger modelIndex = [sender tag]; + menus::MenuModel* model = + static_cast<menus::MenuModel*>( + [[sender representedObject] pointerValue]); + DCHECK(model); + if (model) + model->ActivatedAt(modelIndex); +} + +- (NSMenu*)menu { + if (!menu_ && model_) { + menu_.reset([[self menuFromModel:model_] retain]); + // If this is to be used with a NSPopUpButtonCell, add an item at the 0th + // position that's empty. Doing it after the menu has been constructed won't + // complicate creation logic, and since the tags are model indexes, they + // are unaffected by the extra item. + if (useWithPopUpButtonCell_) { + scoped_nsobject<NSMenuItem> blankItem( + [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]); + [menu_ insertItem:blankItem atIndex:0]; + } + } + return menu_.get(); +} + +@end |