| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. Use of this |
| 2 // source code is governed by a BSD-style license that can be found in the |
| 3 // LICENSE file. |
| 4 |
| 5 #import "chrome/browser/cocoa/menu_controller.h" |
| 6 |
| 7 #include "app/l10n_util_mac.h" |
| 8 #include "app/menus/simple_menu_model.h" |
| 9 #include "base/logging.h" |
| 10 #include "base/sys_string_conversions.h" |
| 11 |
| 12 @interface MenuController(Private) |
| 13 - (NSMenu*)menuFromModel:(menus::MenuModel*)model; |
| 14 - (void)addSeparatorToMenu:(NSMenu*)menu |
| 15 atIndex:(int)index; |
| 16 - (void)addItemToMenu:(NSMenu*)menu |
| 17 atIndex:(int)index |
| 18 fromModel:(menus::MenuModel*)model |
| 19 modelIndex:(int)modelIndex; |
| 20 @end |
| 21 |
| 22 @implementation MenuController |
| 23 |
| 24 - (id)initWithModel:(menus::MenuModel*)model |
| 25 useWithPopUpButtonCell:(BOOL)useWithCell { |
| 26 if ((self = [super init])) { |
| 27 menu_.reset([[self menuFromModel:model] retain]); |
| 28 // If this is to be used with a NSPopUpButtonCell, add an item at the 0th |
| 29 // position that's empty. Doing it after the menu has been constructed won't |
| 30 // complicate creation logic, and since the tags are model indexes, they |
| 31 // are unaffected by the extra item. |
| 32 if (useWithCell) { |
| 33 scoped_nsobject<NSMenuItem> blankItem( |
| 34 [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]); |
| 35 [menu_ insertItem:blankItem atIndex:0]; |
| 36 } |
| 37 } |
| 38 return self; |
| 39 } |
| 40 |
| 41 // Creates a NSMenu from the given model. If the model has submenus, this can |
| 42 // be invoked recursively. |
| 43 - (NSMenu*)menuFromModel:(menus::MenuModel*)model { |
| 44 NSMenu* menu = [[[NSMenu alloc] initWithTitle:@""] autorelease]; |
| 45 |
| 46 // The indices may not always start at zero (the windows system menu is one |
| 47 // example where this is used) so just make sure we can handle it. |
| 48 // SimpleMenuModel currently always starts at 0. |
| 49 int firstItemIndex = model->GetFirstItemIndex(menu); |
| 50 DCHECK(firstItemIndex == 0); |
| 51 const int count = model->GetItemCount(); |
| 52 for (int index = firstItemIndex; index < firstItemIndex + count; index++) { |
| 53 int modelIndex = index - firstItemIndex; |
| 54 if (model->GetTypeAt(modelIndex) == menus::MenuModel::TYPE_SEPARATOR) { |
| 55 [self addSeparatorToMenu:menu atIndex:index]; |
| 56 } else { |
| 57 [self addItemToMenu:menu atIndex:index fromModel:model |
| 58 modelIndex:modelIndex]; |
| 59 } |
| 60 } |
| 61 |
| 62 return menu; |
| 63 } |
| 64 |
| 65 // Adds a separator item at the given index. As the separator doesn't need |
| 66 // anything from the model, this method doesn't need the model index as the |
| 67 // other method below does. |
| 68 - (void)addSeparatorToMenu:(NSMenu*)menu |
| 69 atIndex:(int)index { |
| 70 NSMenuItem* separator = [NSMenuItem separatorItem]; |
| 71 [menu insertItem:separator atIndex:index]; |
| 72 } |
| 73 |
| 74 // Adds an item or a hierarchical menu to the item at the |index|, |
| 75 // associated with the entry in the model indentifed by |modelIndex|. |
| 76 - (void)addItemToMenu:(NSMenu*)menu |
| 77 atIndex:(int)index |
| 78 fromModel:(menus::MenuModel*)model |
| 79 modelIndex:(int)modelIndex { |
| 80 NSString* label = |
| 81 l10n_util::FixUpWindowsStyleLabel(model->GetLabelAt(modelIndex)); |
| 82 scoped_nsobject<NSMenuItem> item( |
| 83 [[NSMenuItem alloc] initWithTitle:label |
| 84 action:@selector(itemSelected:) |
| 85 keyEquivalent:@""]); |
| 86 menus::MenuModel::ItemType type = model->GetTypeAt(modelIndex); |
| 87 if (type == menus::MenuModel::TYPE_SUBMENU) { |
| 88 // Recursively build a submenu from the sub-model at this index. |
| 89 [item setTarget:nil]; |
| 90 [item setAction:nil]; |
| 91 menus::MenuModel* submenuModel = model->GetSubmenuModelAt(modelIndex); |
| 92 NSMenu* submenu = |
| 93 [self menuFromModel:(menus::SimpleMenuModel*)submenuModel]; |
| 94 [item setSubmenu:submenu]; |
| 95 } else { |
| 96 // The MenuModel works on indexes so we can't just set the command id as the |
| 97 // tag like we do in other menus. Also set the represented object to be |
| 98 // the model so hierarchical menus check the correct index in the correct |
| 99 // model. |
| 100 [item setTag:modelIndex]; |
| 101 [item setTarget:self]; |
| 102 NSValue* modelObject = [NSValue valueWithPointer:model]; |
| 103 [item setRepresentedObject:modelObject]; // Retains |modelObject|. |
| 104 } |
| 105 [menu insertItem:item atIndex:index]; |
| 106 } |
| 107 |
| 108 // Called before the menu is to be displayed to update the state (enabled, |
| 109 // radio, etc) of each item in the menu. |
| 110 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item { |
| 111 SEL action = [item action]; |
| 112 if (action != @selector(itemSelected:)) |
| 113 return NO; |
| 114 |
| 115 NSInteger modelIndex = [item tag]; |
| 116 menus::MenuModel* model = |
| 117 static_cast<menus::MenuModel*>( |
| 118 [[(id)item representedObject] pointerValue]); |
| 119 DCHECK(model); |
| 120 if (model) { |
| 121 BOOL checked = model->IsItemCheckedAt(modelIndex); |
| 122 DCHECK([(id)item isKindOfClass:[NSMenuItem class]]); |
| 123 [(id)item setState:(checked ? NSOnState : NSOffState)]; |
| 124 return model->IsEnabledAt(modelIndex); |
| 125 } |
| 126 return NO; |
| 127 } |
| 128 |
| 129 // Called when the user chooses a particular menu item. |sender| is the menu |
| 130 // item chosen. |
| 131 - (void)itemSelected:(id)sender { |
| 132 NSInteger modelIndex = [sender tag]; |
| 133 menus::MenuModel* model = |
| 134 static_cast<menus::MenuModel*>( |
| 135 [[sender representedObject] pointerValue]); |
| 136 DCHECK(model); |
| 137 if (model); |
| 138 model->ActivatedAt(modelIndex); |
| 139 } |
| 140 |
| 141 - (NSMenu*)menu { |
| 142 return menu_.get(); |
| 143 } |
| 144 |
| 145 @end |
| OLD | NEW |