Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.h" | 5 #import "chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.h" |
| 6 | 6 |
| 7 #include "apps/app_shim/extension_app_shim_handler_mac.h" | 7 #include "apps/app_shim/extension_app_shim_handler_mac.h" |
| 8 #include "apps/shell_window.h" | 8 #include "apps/shell_window.h" |
| 9 #include "apps/shell_window_registry.h" | 9 #include "apps/shell_window_registry.h" |
| 10 #include "base/strings/sys_string_conversions.h" | 10 #include "base/strings/sys_string_conversions.h" |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 46 NSInteger menu_tag, | 46 NSInteger menu_tag, |
| 47 NSInteger item_tag) { | 47 NSInteger item_tag) { |
| 48 base::scoped_nsobject<NSMenuItem> item( | 48 base::scoped_nsobject<NSMenuItem> item( |
| 49 [GetItemByTag(menu_tag, item_tag) copy]); | 49 [GetItemByTag(menu_tag, item_tag) copy]); |
| 50 DCHECK(item); | 50 DCHECK(item); |
| 51 [[top_level_item submenu] addItem:item]; | 51 [[top_level_item submenu] addItem:item]; |
| 52 } | 52 } |
| 53 | 53 |
| 54 } // namespace | 54 } // namespace |
| 55 | 55 |
| 56 // Used by AppShimMenuController to manage menu items that are a copy of a | |
| 57 // Chrome menu item but with a different action. This manages unsetting and | |
| 58 // restoring the original item's key equivalent, so that we can use the same | |
| 59 // key equivalent in the copied item with a different action. | |
| 60 @interface DoppelgangerMenuItem : NSObject { | |
| 61 @private | |
| 62 base::scoped_nsobject<NSMenuItem> menuItem_; | |
| 63 base::scoped_nsobject<NSMenuItem> sourceItem_; | |
| 64 int resourceId_; | |
| 65 } | |
| 66 | |
| 67 @property(readonly, nonatomic) NSMenuItem* menuItem; | |
| 68 | |
| 69 // Get the source item using the tags and create the menu item. | |
| 70 - (id)initWithController:(AppShimMenuController*)controller | |
| 71 menuTag:(NSInteger)menuTag | |
| 72 itemTag:(NSInteger)itemTag | |
| 73 resourceId:(int)resourceId | |
| 74 action:(SEL)action | |
| 75 keyEquivalent:(NSString*)keyEquivalent; | |
| 76 // Set the title using |resourceId_| and unset the source item's key equivalent. | |
| 77 - (void)enableForApp:(const extensions::Extension*)app; | |
| 78 // Restore the source item's key equivalent. | |
| 79 - (void)disable; | |
| 80 @end | |
| 81 | |
| 82 @implementation DoppelgangerMenuItem | |
| 83 | |
| 84 - (NSMenuItem*)menuItem { | |
| 85 return menuItem_; | |
| 86 } | |
| 87 | |
| 88 - (id)initWithController:(AppShimMenuController*)controller | |
| 89 menuTag:(NSInteger)menuTag | |
| 90 itemTag:(NSInteger)itemTag | |
| 91 resourceId:(int)resourceId | |
| 92 action:(SEL)action | |
| 93 keyEquivalent:(NSString*)keyEquivalent { | |
|
Nico
2013/09/04 15:55:11
Is it worth adding some DCHECk that catches if two
jackhou1
2013/09/06 05:25:18
I can't think of an easy way to do this without ad
| |
| 94 if ((self = [super init])) { | |
| 95 sourceItem_.reset([GetItemByTag(menuTag, itemTag) retain]); | |
| 96 DCHECK(sourceItem_); | |
| 97 menuItem_.reset([[NSMenuItem alloc] | |
| 98 initWithTitle:@"" | |
| 99 action:action | |
| 100 keyEquivalent:keyEquivalent]); | |
| 101 [menuItem_ setTarget:controller]; | |
| 102 [menuItem_ setTag:itemTag]; | |
| 103 resourceId_ = resourceId; | |
| 104 } | |
| 105 return self; | |
| 106 } | |
| 107 | |
| 108 - (void)enableForApp:(const extensions::Extension*)app { | |
| 109 // It seems that two menu items that have the same key equivalent must also | |
| 110 // have the same action for the keyboard shortcut to work. (This refers to the | |
| 111 // original keyboard shortcut, regardless of any overrides set in OSX). | |
| 112 // In order to let the app menu items have a different action, we remove the | |
| 113 // key equivalent of the original items and restore them later. | |
| 114 [sourceItem_ setKeyEquivalent:@""]; | |
| 115 if (!resourceId_) | |
| 116 return; | |
| 117 | |
| 118 [menuItem_ setTitle:l10n_util::GetNSStringF(resourceId_, | |
| 119 base::UTF8ToUTF16(app->name()))]; | |
| 120 } | |
| 121 | |
| 122 - (void)disable { | |
| 123 // Restore the keyboard shortcut to Chrome. This just needs to be set back to | |
| 124 // the original keyboard shortcut, regardless of any overrides in OSX. The | |
| 125 // overrides still work as they are based on the title of the menu item. | |
| 126 [sourceItem_ setKeyEquivalent:[menuItem_ keyEquivalent]]; | |
| 127 } | |
| 128 | |
| 129 @end | |
| 130 | |
| 56 @interface AppShimMenuController () | 131 @interface AppShimMenuController () |
| 57 // Construct the NSMenuItems for apps. | 132 // Construct the NSMenuItems for apps. |
| 58 - (void)buildAppMenuItems; | 133 - (void)buildAppMenuItems; |
| 59 // Register for NSWindow notifications. | 134 // Register for NSWindow notifications. |
| 60 - (void)registerEventHandlers; | 135 - (void)registerEventHandlers; |
| 61 // If the window is an app window, add or remove menu items. | 136 // If the window is an app window, add or remove menu items. |
| 62 - (void)windowMainStatusChanged:(NSNotification*)notification; | 137 - (void)windowMainStatusChanged:(NSNotification*)notification; |
| 63 // Add menu items for an app and hide Chrome menu items. | 138 // Add menu items for an app and hide Chrome menu items. |
| 64 - (void)addMenuItems:(const extensions::Extension*)app; | 139 - (void)addMenuItems:(const extensions::Extension*)app; |
| 65 // If the window belongs to the currently focused app, remove the menu items and | 140 // If the window belongs to the currently focused app, remove the menu items and |
| 66 // unhide Chrome menu items. | 141 // unhide Chrome menu items. |
| 67 - (void)removeMenuItems:(NSString*)appId; | 142 - (void)removeMenuItems:(NSString*)appId; |
| 68 // If the currently focused window belongs to a platform app, quit the app. | 143 // If the currently focused window belongs to a platform app, quit the app. |
| 69 - (void)quitCurrentPlatformApp; | 144 - (void)quitCurrentPlatformApp; |
| 145 // If the currently focused window belongs to a platform app, hide the app. | |
| 146 - (void)hideCurrentPlatformApp; | |
| 70 @end | 147 @end |
| 71 | 148 |
| 72 @implementation AppShimMenuController | 149 @implementation AppShimMenuController |
| 73 | 150 |
| 74 - (id)init { | 151 - (id)init { |
| 75 if ((self = [super init])) { | 152 if ((self = [super init])) { |
| 76 [self buildAppMenuItems]; | 153 [self buildAppMenuItems]; |
| 77 [self registerEventHandlers]; | 154 [self registerEventHandlers]; |
| 78 } | 155 } |
| 79 return self; | 156 return self; |
| 80 } | 157 } |
| 81 | 158 |
| 82 - (void)dealloc { | 159 - (void)dealloc { |
| 83 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 160 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
| 84 [super dealloc]; | 161 [super dealloc]; |
| 85 } | 162 } |
| 86 | 163 |
| 87 - (void)buildAppMenuItems { | 164 - (void)buildAppMenuItems { |
| 88 // Find the "Quit Chrome" menu item. | 165 hideDoppelganger_.reset([[DoppelgangerMenuItem alloc] |
| 89 chromeMenuQuitItem_.reset([GetItemByTag(IDC_CHROME_MENU, IDC_EXIT) retain]); | 166 initWithController:self |
| 90 DCHECK(chromeMenuQuitItem_); | 167 menuTag:IDC_CHROME_MENU |
| 168 itemTag:IDC_HIDE_APP | |
| 169 resourceId:IDS_HIDE_APP_MAC | |
| 170 action:@selector(hideCurrentPlatformApp) | |
| 171 keyEquivalent:@"h"]); | |
| 172 quitDoppelganger_.reset([[DoppelgangerMenuItem alloc] | |
| 173 initWithController:self | |
| 174 menuTag:IDC_CHROME_MENU | |
| 175 itemTag:IDC_EXIT | |
| 176 resourceId:IDS_EXIT_MAC | |
| 177 action:@selector(quitCurrentPlatformApp) | |
| 178 keyEquivalent:@"q"]); | |
| 91 | 179 |
| 92 // The app's menu. | 180 // The app's menu. |
| 93 appMenuItem_.reset([[NSMenuItem alloc] initWithTitle:@"" | 181 appMenuItem_.reset([[NSMenuItem alloc] initWithTitle:@"" |
| 94 action:nil | 182 action:nil |
| 95 keyEquivalent:@""]); | 183 keyEquivalent:@""]); |
| 96 base::scoped_nsobject<NSMenu> appMenu([[NSMenu alloc] initWithTitle:@""]); | 184 base::scoped_nsobject<NSMenu> appMenu([[NSMenu alloc] initWithTitle:@""]); |
| 185 [appMenuItem_ setSubmenu:appMenu]; | |
| 97 [appMenu setAutoenablesItems:NO]; | 186 [appMenu setAutoenablesItems:NO]; |
| 98 NSMenuItem* appMenuQuitItem = | 187 |
| 99 [appMenu addItemWithTitle:@"" | 188 [appMenu addItem:[hideDoppelganger_ menuItem]]; |
| 100 action:@selector(quitCurrentPlatformApp) | 189 [appMenu addItem:[NSMenuItem separatorItem]]; |
| 101 keyEquivalent:@"q"]; | 190 [appMenu addItem:[quitDoppelganger_ menuItem]]; |
| 102 [appMenuQuitItem setKeyEquivalentModifierMask: | |
| 103 [chromeMenuQuitItem_ keyEquivalentModifierMask]]; | |
| 104 [appMenuQuitItem setTarget:self]; | |
| 105 [appMenuItem_ setSubmenu:appMenu]; | |
| 106 | 191 |
| 107 // File menu. | 192 // File menu. |
| 108 fileMenuItem_.reset([NewTopLevelItemFrom(IDC_FILE_MENU) retain]); | 193 fileMenuItem_.reset([NewTopLevelItemFrom(IDC_FILE_MENU) retain]); |
| 109 AddDuplicateItem(fileMenuItem_, IDC_FILE_MENU, IDC_CLOSE_WINDOW); | 194 AddDuplicateItem(fileMenuItem_, IDC_FILE_MENU, IDC_CLOSE_WINDOW); |
| 110 | 195 |
| 111 // Edit menu. | 196 // Edit menu. |
| 112 editMenuItem_.reset([NewTopLevelItemFrom(IDC_EDIT_MENU) retain]); | 197 editMenuItem_.reset([NewTopLevelItemFrom(IDC_EDIT_MENU) retain]); |
| 113 AddDuplicateItem(editMenuItem_, IDC_EDIT_MENU, IDC_CONTENT_CONTEXT_UNDO); | 198 AddDuplicateItem(editMenuItem_, IDC_EDIT_MENU, IDC_CONTENT_CONTEXT_UNDO); |
| 114 AddDuplicateItem(editMenuItem_, IDC_EDIT_MENU, IDC_CONTENT_CONTEXT_REDO); | 199 AddDuplicateItem(editMenuItem_, IDC_EDIT_MENU, IDC_CONTENT_CONTEXT_REDO); |
| 115 [[editMenuItem_ submenu] addItem:[NSMenuItem separatorItem]]; | 200 [[editMenuItem_ submenu] addItem:[NSMenuItem separatorItem]]; |
| (...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 178 return; | 263 return; |
| 179 | 264 |
| 180 [self removeMenuItems:appId_]; | 265 [self removeMenuItems:appId_]; |
| 181 appId_.reset([appId copy]); | 266 appId_.reset([appId copy]); |
| 182 | 267 |
| 183 // Hide Chrome menu items. | 268 // Hide Chrome menu items. |
| 184 NSMenu* mainMenu = [NSApp mainMenu]; | 269 NSMenu* mainMenu = [NSApp mainMenu]; |
| 185 for (NSMenuItem* item in [mainMenu itemArray]) | 270 for (NSMenuItem* item in [mainMenu itemArray]) |
| 186 [item setHidden:YES]; | 271 [item setHidden:YES]; |
| 187 | 272 |
| 188 NSString* localizedQuitApp = | 273 [hideDoppelganger_ enableForApp:app]; |
| 189 l10n_util::GetNSStringF(IDS_EXIT_MAC, base::UTF8ToUTF16(app->name())); | 274 [quitDoppelganger_ enableForApp:app]; |
| 190 NSMenuItem* appMenuQuitItem = [[[appMenuItem_ submenu] itemArray] lastObject]; | |
| 191 [appMenuQuitItem setTitle:localizedQuitApp]; | |
| 192 | |
| 193 // It seems that two menu items that have the same key equivalent must also | |
| 194 // have the same action for the keyboard shortcut to work. (This refers to the | |
| 195 // original keyboard shortcut, regardless of any overrides set in OSX). | |
| 196 // In order to let the appMenuQuitItem have a different action, we remove the | |
| 197 // key equivalent from the chromeMenuQuitItem and restore it later. | |
| 198 [chromeMenuQuitItem_ setKeyEquivalent:@""]; | |
| 199 | 275 |
| 200 [appMenuItem_ setTitle:appId]; | 276 [appMenuItem_ setTitle:appId]; |
| 201 [[appMenuItem_ submenu] setTitle:title]; | 277 [[appMenuItem_ submenu] setTitle:title]; |
| 202 | 278 |
| 203 [mainMenu addItem:appMenuItem_]; | 279 [mainMenu addItem:appMenuItem_]; |
| 204 [mainMenu addItem:fileMenuItem_]; | 280 [mainMenu addItem:fileMenuItem_]; |
| 205 [mainMenu addItem:editMenuItem_]; | 281 [mainMenu addItem:editMenuItem_]; |
| 206 [mainMenu addItem:windowMenuItem_]; | 282 [mainMenu addItem:windowMenuItem_]; |
| 207 } | 283 } |
| 208 | 284 |
| 209 - (void)removeMenuItems:(NSString*)appId { | 285 - (void)removeMenuItems:(NSString*)appId { |
| 210 if (![appId_ isEqualToString:appId]) | 286 if (![appId_ isEqualToString:appId]) |
| 211 return; | 287 return; |
| 212 | 288 |
| 213 appId_.reset(); | 289 appId_.reset(); |
| 214 | 290 |
| 215 NSMenu* mainMenu = [NSApp mainMenu]; | 291 NSMenu* mainMenu = [NSApp mainMenu]; |
| 216 [mainMenu removeItem:appMenuItem_]; | 292 [mainMenu removeItem:appMenuItem_]; |
| 217 [mainMenu removeItem:fileMenuItem_]; | 293 [mainMenu removeItem:fileMenuItem_]; |
| 218 [mainMenu removeItem:editMenuItem_]; | 294 [mainMenu removeItem:editMenuItem_]; |
| 219 [mainMenu removeItem:windowMenuItem_]; | 295 [mainMenu removeItem:windowMenuItem_]; |
| 220 | 296 |
| 221 // Restore the Chrome main menu bar. | 297 // Restore the Chrome main menu bar. |
| 222 for (NSMenuItem* item in [mainMenu itemArray]) | 298 for (NSMenuItem* item in [mainMenu itemArray]) |
| 223 [item setHidden:NO]; | 299 [item setHidden:NO]; |
| 224 | 300 |
| 225 // Restore the keyboard shortcut to Chrome. This just needs to be set back to | 301 [hideDoppelganger_ disable]; |
| 226 // the original keyboard shortcut, regardless of any overrides in OSX. The | 302 [quitDoppelganger_ disable]; |
| 227 // overrides still work as they are based on the title of the menu item. | |
| 228 [chromeMenuQuitItem_ setKeyEquivalent:@"q"]; | |
| 229 } | 303 } |
| 230 | 304 |
| 231 - (void)quitCurrentPlatformApp { | 305 - (void)quitCurrentPlatformApp { |
| 232 apps::ShellWindow* shellWindow = | 306 apps::ShellWindow* shellWindow = |
| 233 apps::ShellWindowRegistry::GetShellWindowForNativeWindowAnyProfile( | 307 apps::ShellWindowRegistry::GetShellWindowForNativeWindowAnyProfile( |
| 234 [NSApp keyWindow]); | 308 [NSApp keyWindow]); |
| 235 if (shellWindow) | 309 if (shellWindow) |
| 236 apps::ExtensionAppShimHandler::QuitAppForWindow(shellWindow); | 310 apps::ExtensionAppShimHandler::QuitAppForWindow(shellWindow); |
| 237 } | 311 } |
| 238 | 312 |
| 313 - (void)hideCurrentPlatformApp { | |
| 314 apps::ShellWindow* shellWindow = | |
| 315 apps::ShellWindowRegistry::GetShellWindowForNativeWindowAnyProfile( | |
| 316 [NSApp keyWindow]); | |
| 317 if (shellWindow) | |
| 318 apps::ExtensionAppShimHandler::HideAppForWindow(shellWindow); | |
| 319 } | |
| 320 | |
| 239 @end | 321 @end |
| OLD | NEW |