Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(23)

Side by Side Diff: chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.mm

Issue 1259903002: DO NOT COMMIT: [Mac] Refactor keyboard shortcut handling so it can be reused in Views. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Sync to new CommandDispatcher design. Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #import "chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.h"
6
7 #include "base/logging.h"
8 #import "base/mac/foundation_util.h"
9 #include "chrome/app/chrome_command_ids.h"
10 #import "chrome/browser/app_controller_mac.h"
11 #include "chrome/browser/fullscreen.h"
12 #include "chrome/browser/global_keyboard_shortcuts_mac.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/ui/bookmarks/bookmark_utils.h"
15 #include "chrome/browser/ui/browser_commands.h"
16 #include "chrome/browser/ui/browser_finder.h"
17 #include "chrome/browser/ui/browser_window.h"
18 #import "chrome/browser/ui/cocoa/browser_window_controller_private.h"
19 #include "chrome/browser/ui/toolbar/encoding_menu_controller.h"
20 #include "content/public/browser/web_contents.h"
21 #import "ui/base/cocoa/cocoa_base_utils.h"
22
23 namespace {
24
25 // Type of functions listed in global_keyboard_shortcuts_mac.h.
26 typedef int (*KeyToCommandMapper)(bool, bool, bool, bool, int, unichar);
27
28 // If the event is for a Browser window, and the key combination has an
29 // associated command, execute the command.
30 bool HandleExtraKeyboardShortcut(
31 NSEvent* event,
32 NSWindow* window,
33 KeyToCommandMapper command_for_keyboard_shortcut) {
34 // Extract info from |event|.
35 NSUInteger modifers = [event modifierFlags];
36 const bool command = modifers & NSCommandKeyMask;
37 const bool shift = modifers & NSShiftKeyMask;
38 const bool control = modifers & NSControlKeyMask;
39 const bool option = modifers & NSAlternateKeyMask;
40 const int key_code = [event keyCode];
41 const unichar key_char = KeyCharacterForEvent(event);
42
43 int cmd = command_for_keyboard_shortcut(command, shift, control, option,
44 key_code, key_char);
45
46 if (cmd == -1)
47 return false;
48
49 // Only handle event if this is a browser window.
50 Browser* browser = chrome::FindBrowserWithWindow(window);
51 if (!browser)
52 return false;
53
54 chrome::ExecuteCommand(browser, cmd);
55 return true;
56 }
57
58 bool HandleExtraWindowKeyboardShortcut(NSEvent* event, NSWindow* window) {
59 return HandleExtraKeyboardShortcut(event, window,
60 CommandForWindowKeyboardShortcut);
61 }
62
63 bool HandleDelayedWindowKeyboardShortcut(NSEvent* event, NSWindow* window) {
64 return HandleExtraKeyboardShortcut(event, window,
65 CommandForDelayedWindowKeyboardShortcut);
66 }
67
68 bool HandleExtraBrowserKeyboardShortcut(NSEvent* event, NSWindow* window) {
69 return HandleExtraKeyboardShortcut(event, window,
70 CommandForBrowserKeyboardShortcut);
71 }
72
73 // Update a toggle state for an item if modified. The item may be an NSMenuItem
74 // or NSButton. Called by -validateUserInterfaceItem:.
75 void UpdateToggleStateWithTag(NSInteger tag, id item, NSWindow* window) {
76 if (![item respondsToSelector:@selector(state)] ||
77 ![item respondsToSelector:@selector(setState:)])
78 return;
79
80 Browser* browser = chrome::FindBrowserWithWindow(window);
81 DCHECK(browser);
82
83 // On Windows this logic happens in bookmark_bar_view.cc. This simply updates
84 // the menu item; it does not display the bookmark bar itself.
85 if (tag == IDC_SHOW_BOOKMARK_BAR) {
86 bool toggled = browser->window()->IsBookmarkBarVisible();
87 NSInteger oldState = [item state];
88 NSInteger newState = toggled ? NSOnState : NSOffState;
89 if (oldState != newState)
90 [item setState:newState];
91 return;
92 }
93
94 // Update the checked/unchecked state of items in the encoding menu.
95 // On Windows, this logic is part of |EncodingMenuModel| in
96 // browser/ui/views/toolbar_view.h.
97 EncodingMenuController encoding_controller;
98 if (!encoding_controller.DoesCommandBelongToEncodingMenu(tag))
99 return;
100
101 Profile* profile = browser->profile();
102 DCHECK(profile);
103 content::WebContents* current_tab =
104 browser->tab_strip_model()->GetActiveWebContents();
105 if (!current_tab)
106 return;
107
108 const std::string encoding = current_tab->GetEncoding();
109
110 bool toggled = encoding_controller.IsItemChecked(profile, encoding, tag);
111 NSInteger oldState = [item state];
112 NSInteger newState = toggled ? NSOnState : NSOffState;
113 if (oldState != newState)
114 [item setState:newState];
115 }
116
117 } // namespace
118
119 @implementation ChromeCommandDispatcherDelegate
120
121 - (BOOL)handleExtraKeyboardShortcut:(NSEvent*)event window:(NSWindow*)window {
122 return HandleExtraBrowserKeyboardShortcut(event, window) ||
123 HandleExtraWindowKeyboardShortcut(event, window) ||
124 HandleDelayedWindowKeyboardShortcut(event, window);
125 }
126
127 - (BOOL)eventHandledByExtensionCommand:(NSEvent*)event
128 isRedispatch:(BOOL)isRedispatch {
129 // Some extension commands have higher priority than web content, and some
130 // have lower priority. Regardless of whether the event is being redispatched,
131 // let the extension system try to handle the event. In case this is a
132 // redispatched event, [event window] gives the correct window.
133 if ([event window]) {
134 BrowserWindowController* controller = [[event window] windowController];
135 if ([controller respondsToSelector:@selector(handledByExtensionCommand:
136 priority:)]) {
137 ui::AcceleratorManager::HandlerPriority priority =
138 isRedispatch ? ui::AcceleratorManager::kNormalPriority
139 : ui::AcceleratorManager::kHighPriority;
140 if ([controller handledByExtensionCommand:event priority:priority])
141 return YES;
142 }
143 }
144 return NO;
145 }
146
147 - (BOOL)prePerformKeyEquivalent:(NSEvent*)event window:(NSWindow*)window {
148 // Handle per-window shortcuts like cmd-1, but do not handle browser-level
149 // shortcuts like cmd-left (else, cmd-left would do history navigation even
150 // if e.g. the Omnibox has focus).
151 return HandleExtraWindowKeyboardShortcut(event, window);
152 }
153
154 - (BOOL)postPerformKeyEquivalent:(NSEvent*)event window:(NSWindow*)window {
155 // Handle per-window shortcuts like Esc after giving everybody else a chance
156 // to handle them
157 return HandleDelayedWindowKeyboardShortcut(event, window);
158 }
159
160 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item
161 window:(NSWindow*)window {
162 SEL action = [item action];
163 if (action != @selector(commandDispatch:) &&
164 action != @selector(commandDispatchUsingKeyModifiers:)) {
165 // By default, interface items are enabled if the object in the responder
166 // chain that implements the action does not implement
167 // -validateUserInterfaceItem. Since we only care about -commandDispatch,
168 // return YES for all other actions
169 return YES;
170 }
171
172 Browser* browser = chrome::FindBrowserWithWindow(window);
173 DCHECK(browser);
174 NSInteger tag = [item tag];
175 if (!chrome::SupportsCommand(browser, tag))
176 return NO;
177
178 // Generate return value (enabled state).
179 BOOL enable = chrome::IsCommandEnabled(browser, tag);
180 switch (tag) {
181 case IDC_CLOSE_TAB:
182 // Disable "close tab" if the receiving window is not tabbed.
183 // We simply check whether the item has a keyboard shortcut set here;
184 // app_controller_mac.mm actually determines whether the item should
185 // be enabled.
186 if (NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item))
187 enable &= !![[menuItem keyEquivalent] length];
188 break;
189 case IDC_FULLSCREEN: {
190 if (NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item)) {
191 BrowserWindowController* controller =
192 base::mac::ObjCCast<BrowserWindowController>(
193 [window windowController]);
194 if (chrome::mac::SupportsSystemFullscreen()) {
195 [menuItem setTitle:[controller titleForFullscreenMenuItem]];
196 } else {
197 [menuItem setHidden:YES];
198 }
199 }
200 break;
201 }
202 case IDC_PRESENTATION_MODE: {
203 if (NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item)) {
204 BrowserWindowController* controller =
205 base::mac::ObjCCast<BrowserWindowController>(
206 [window windowController]);
207 [menuItem setTitle:[controller titleForFullscreenMenuItem]];
208
209 if (chrome::mac::SupportsSystemFullscreen())
210 [menuItem setAlternate:YES];
211 }
212 break;
213 }
214 case IDC_SHOW_SIGNIN: {
215 Profile* original_profile = browser->profile()->GetOriginalProfile();
216 [AppController updateSigninItem:item
217 shouldShow:enable
218 currentProfile:original_profile];
219 break;
220 }
221 case IDC_BOOKMARK_PAGE: {
222 // Extensions have the ability to hide the bookmark page menu item.
223 // This only affects the bookmark page menu item under the main menu.
224 // The bookmark page menu item under the wrench menu has its
225 // visibility controlled by WrenchMenuModel.
226 bool shouldHide =
227 chrome::ShouldRemoveBookmarkThisPageUI(browser->profile());
228 NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item);
229 [menuItem setHidden:shouldHide];
230 break;
231 }
232 case IDC_BOOKMARK_ALL_TABS: {
233 // Extensions have the ability to hide the bookmark all tabs menu
234 // item. This only affects the bookmark page menu item under the main
235 // menu. The bookmark page menu item under the wrench menu has its
236 // visibility controlled by WrenchMenuModel.
237 bool shouldHide =
238 chrome::ShouldRemoveBookmarkOpenPagesUI(browser->profile());
239 NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item);
240 [menuItem setHidden:shouldHide];
241 break;
242 }
243 default:
244 // Special handling for the contents of the Text Encoding submenu. On
245 // Mac OS, instead of enabling/disabling the top-level menu item, we
246 // enable/disable the submenu's contents (per Apple's HIG).
247 EncodingMenuController encoding_controller;
248 if (encoding_controller.DoesCommandBelongToEncodingMenu(tag)) {
249 enable &=
250 chrome::IsCommandEnabled(browser, IDC_ENCODING_MENU) ? YES : NO;
251 }
252 }
253
254 // If the item is toggleable, find its toggle state and
255 // try to update it. This is a little awkward, but the alternative is
256 // to check after a commandDispatch, which seems worse.
257 UpdateToggleStateWithTag(tag, item, window);
258
259 return enable;
260 }
261
262 - (void)commandDispatch:(id)sender window:(NSWindow*)window {
263 DCHECK(sender);
264 // Identify the actual BWC to which the command should be dispatched. It might
265 // belong to a background window, yet this controller gets it because it is
266 // the foreground window's controller and thus in the responder chain. Some
267 // senders don't have this problem (for example, menus only operate on the
268 // foreground window), so this is only an issue for senders that are part of
269 // windows.
270 NSWindow* targetWindow = window;
271 if ([sender respondsToSelector:@selector(window)])
272 targetWindow = [sender window];
273 Browser* browser = chrome::FindBrowserWithWindow(targetWindow);
274 DCHECK(browser);
275
276 // When system fullscreen is available, it supercedes presentation mode.
277 int tag = [sender tag];
278 if (tag == IDC_PRESENTATION_MODE && chrome::mac::SupportsSystemFullscreen())
279 tag = IDC_FULLSCREEN;
280
281 chrome::ExecuteCommand(browser, tag);
282 }
283
284 - (void)commandDispatchUsingKeyModifiers:(id)sender window:(NSWindow*)window {
285 DCHECK(sender);
286
287 if (![sender isEnabled]) {
288 // This code is reachable e.g. if the user mashes the back button, queuing
289 // up a bunch of events before the button's enabled state is updated:
290 // http://crbug.com/63254
291 return;
292 }
293
294 // See comment above for why we do this.
295 NSWindow* targetWindow = window;
296 if ([sender respondsToSelector:@selector(window)])
297 targetWindow = [sender window];
298 Browser* browser = chrome::FindBrowserWithWindow(targetWindow);
299 DCHECK(browser);
300
301 NSInteger command = [sender tag];
302 NSUInteger modifierFlags = [[NSApp currentEvent] modifierFlags];
303 if ((command == IDC_RELOAD) &&
304 (modifierFlags & (NSShiftKeyMask | NSControlKeyMask))) {
305 command = IDC_RELOAD_IGNORING_CACHE;
306 // Mask off Shift and Control so they don't affect the disposition below.
307 modifierFlags &= ~(NSShiftKeyMask | NSControlKeyMask);
308 }
309 if (![[sender window] isMainWindow]) {
310 // Remove the command key from the flags, it means "keep the window in
311 // the background" in this case.
312 modifierFlags &= ~NSCommandKeyMask;
313 }
314 chrome::ExecuteCommandWithDisposition(
315 browser, command, ui::WindowOpenDispositionFromNSEventWithFlags(
316 [NSApp currentEvent], modifierFlags));
317 }
318
319 @end // ChromeCommandDispatchDelegate
OLDNEW
« no previous file with comments | « chrome/browser/ui/cocoa/chrome_command_dispatcher_delegate.h ('k') | chrome/browser/ui/cocoa/chrome_event_processing_window.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698