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 |