1 // Copyright (c) 2012 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.
5 #import "chrome/browser/ui/cocoa/chrome_event_processing_window.h"
7 #include "base/logging.h"
8 #include "chrome/browser/global_keyboard_shortcuts_mac.h"
9 #include "chrome/browser/ui/browser_commands.h"
10 #include "chrome/browser/ui/browser_finder.h"
11 #import "chrome/browser/ui/cocoa/browser_window_controller_private.h"
12 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
13 #import "content/public/browser/render_widget_host_view_mac_base.h"
17 // Type of functions listed in global_keyboard_shortcuts_mac.h.
18 typedef int (*KeyToCommandMapper)(bool, bool, bool, bool, int, unichar);
20 // If the event is for a Browser window, and the key combination has an
21 // associated command, execute the command.
22 bool HandleExtraKeyboardShortcut(
25 KeyToCommandMapper command_for_keyboard_shortcut) {
26 // Extract info from |event|.
27 NSUInteger modifers = [event modifierFlags];
28 const bool command = modifers & NSCommandKeyMask;
29 const bool shift = modifers & NSShiftKeyMask;
30 const bool control = modifers & NSControlKeyMask;
31 const bool option = modifers & NSAlternateKeyMask;
32 const int key_code = [event keyCode];
33 const unichar key_char = KeyCharacterForEvent(event);
35 int cmd = command_for_keyboard_shortcut(command, shift, control, option,
41 // Only handle event if this is a browser window.
42 Browser* browser = chrome::FindBrowserWithWindow(window);
46 chrome::ExecuteCommand(browser, cmd);
50 bool HandleExtraWindowKeyboardShortcut(NSEvent* event, NSWindow* window) {
51 return HandleExtraKeyboardShortcut(event, window,
52 CommandForWindowKeyboardShortcut);
55 bool HandleDelayedWindowKeyboardShortcut(NSEvent* event, NSWindow* window) {
56 return HandleExtraKeyboardShortcut(event, window,
57 CommandForDelayedWindowKeyboardShortcut);
60 bool HandleExtraBrowserKeyboardShortcut(NSEvent* event, NSWindow* window) {
61 return HandleExtraKeyboardShortcut(event, window,
62 CommandForBrowserKeyboardShortcut);
65 // Duplicate the given key event, but changing the associated window.
66 NSEvent* KeyEventForWindow(NSWindow* window, NSEvent* event) {
67 NSEventType event_type = [event type];
69 // Convert the event's location from the original window's coordinates into
71 NSPoint location = [event locationInWindow];
72 location = [[event window] convertBaseToScreen:location];
73 location = [window convertScreenToBase:location];
75 // Various things *only* apply to key down/up.
76 bool is_a_repeat = false;
77 NSString* characters = nil;
78 NSString* charactors_ignoring_modifiers = nil;
79 if (event_type == NSKeyDown || event_type == NSKeyUp) {
80 is_a_repeat = [event isARepeat];
81 characters = [event characters];
82 charactors_ignoring_modifiers = [event charactersIgnoringModifiers];
85 // This synthesis may be slightly imperfect: we provide nil for the context,
86 // since I (viettrungluu) am sceptical that putting in the original context
87 // (if one is given) is valid.
88 return [NSEvent keyEventWithType:event_type
90 modifierFlags:[event modifierFlags]
91 timestamp:[event timestamp]
92 windowNumber:[window windowNumber]
95 charactersIgnoringModifiers:charactors_ignoring_modifiers
97 keyCode:[event keyCode]];
102 @implementation ChromeEventProcessingWindow
104 - (BOOL)handleExtraKeyboardShortcut:(NSEvent*)event {
105 return HandleExtraBrowserKeyboardShortcut(event, self) ||
106 HandleExtraWindowKeyboardShortcut(event, self) ||
107 HandleDelayedWindowKeyboardShortcut(event, self);
110 - (BOOL)performKeyEquivalent:(NSEvent*)event {
111 // Some extension commands have higher priority than web content, and some
112 // have lower priority. Regardless of whether the event is being
113 // redispatched, let the extension system try to handle the event.
114 NSWindow* window = event.window;
116 BrowserWindowController* controller = [window windowController];
117 if ([controller respondsToSelector:@selector(handledByExtensionCommand:
119 ui::AcceleratorManager::HandlerPriority priority =
120 redispatchingEvent_ ? ui::AcceleratorManager::kNormalPriority
121 : ui::AcceleratorManager::kHighPriority;
122 if ([controller handledByExtensionCommand:event priority:priority])
127 if (redispatchingEvent_)
130 // Give the web site a chance to handle the event. If it doesn't want to
131 // handle it, it will call us back with one of the |handle*| methods above.
132 NSResponder* r = [self firstResponder];
133 if ([r conformsToProtocol:@protocol(RenderWidgetHostViewMacBase)])
134 return [r performKeyEquivalent:event];
136 // Handle per-window shortcuts like cmd-1, but do not handle browser-level
137 // shortcuts like cmd-left (else, cmd-left would do history navigation even
138 // if e.g. the Omnibox has focus).
139 if (HandleExtraWindowKeyboardShortcut(event, self))
142 if ([super performKeyEquivalent:event])
145 // Handle per-window shortcuts like Esc after giving everybody else a chance
147 return HandleDelayedWindowKeyboardShortcut(event, self);
150 - (BOOL)redispatchKeyEvent:(NSEvent*)event {
152 NSEventType eventType = [event type];
153 if (eventType != NSKeyDown &&
154 eventType != NSKeyUp &&
155 eventType != NSFlagsChanged) {
157 return YES; // Pretend it's been handled in an effort to limit damage.
160 // Ordinarily, the event's window should be this window. However, when
161 // switching between normal and fullscreen mode, we switch out the window, and
162 // the event's window might be the previous window (or even an earlier one if
163 // the renderer is running slowly and several mode switches occur). In this
164 // rare case, we synthesize a new key event so that its associate window
165 // (number) is our own.
166 if ([event window] != self)
167 event = KeyEventForWindow(self, event);
169 // Redispatch the event.
171 redispatchingEvent_ = YES;
172 [NSApp sendEvent:event];
173 redispatchingEvent_ = NO;
175 // If the event was not handled by [NSApp sendEvent:], the sendEvent:
176 // method below will be called, and because |redispatchingEvent_| is YES,
177 // |eventHandled_| will be set to NO.
178 return eventHandled_;
181 - (void)sendEvent:(NSEvent*)event {
182 if (!redispatchingEvent_)
183 [super sendEvent:event];
188 @end // ChromeEventProcessingWindow