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