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 #import "chrome/browser/ui/cocoa/browser_command_executor.h"
9 #import "chrome/browser/ui/cocoa/browser_window_controller_private.h"
10 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
11 #include "chrome/browser/global_keyboard_shortcuts_mac.h"
12 #import "content/public/browser/render_widget_host_view_mac_base.h"
14 typedef int (*KeyToCommandMapper)(bool, bool, bool, bool, int, unichar);
16 @interface ChromeEventProcessingWindow ()
17 // Duplicate the given key event, but changing the associated window.
18 - (NSEvent*)keyEventForWindow:(NSWindow*)window fromKeyEvent:(NSEvent*)event;
21 @implementation ChromeEventProcessingWindow
23 - (BOOL)handleExtraKeyboardShortcut:(NSEvent*)event fromTable:
24 (KeyToCommandMapper)commandForKeyboardShortcut {
25 // Extract info from |event|.
26 NSUInteger modifers = [event modifierFlags];
27 const bool cmdKey = modifers & NSCommandKeyMask;
28 const bool shiftKey = modifers & NSShiftKeyMask;
29 const bool cntrlKey = modifers & NSControlKeyMask;
30 const bool optKey = modifers & NSAlternateKeyMask;
31 const unichar keyCode = [event keyCode];
32 const unichar keyChar = KeyCharacterForEvent(event);
34 int cmdNum = commandForKeyboardShortcut(cmdKey, shiftKey, cntrlKey, optKey,
38 id executor = [self delegate];
40 DCHECK([executor conformsToProtocol:@protocol(BrowserCommandExecutor)]);
41 DCHECK([executor respondsToSelector:@selector(executeCommand:)]);
42 [executor executeCommand:cmdNum];
48 - (BOOL)handleExtraWindowKeyboardShortcut:(NSEvent*)event {
49 return [self handleExtraKeyboardShortcut:event
50 fromTable:CommandForWindowKeyboardShortcut];
53 - (BOOL)handleDelayedWindowKeyboardShortcut:(NSEvent*)event {
54 return [self handleExtraKeyboardShortcut:event
55 fromTable:CommandForDelayedWindowKeyboardShortcut];
58 - (BOOL)handleExtraBrowserKeyboardShortcut:(NSEvent*)event {
59 return [self handleExtraKeyboardShortcut:event
60 fromTable:CommandForBrowserKeyboardShortcut];
63 - (BOOL)performKeyEquivalent:(NSEvent*)event {
64 // Some extension commands have higher priority than web content, and some
65 // have lower priority. Regardless of whether the event is being
66 // redispatched, let the extension system try to handle the event.
67 NSWindow* window = event.window;
69 BrowserWindowController* controller = [window windowController];
70 if ([controller respondsToSelector:@selector(handledByExtensionCommand:
72 ui::AcceleratorManager::HandlerPriority priority =
73 redispatchingEvent_ ? ui::AcceleratorManager::kNormalPriority
74 : ui::AcceleratorManager::kHighPriority;
75 if ([controller handledByExtensionCommand:event priority:priority])
80 if (redispatchingEvent_)
83 // Give the web site a chance to handle the event. If it doesn't want to
84 // handle it, it will call us back with one of the |handle*| methods above.
85 NSResponder* r = [self firstResponder];
86 if ([r conformsToProtocol:@protocol(RenderWidgetHostViewMacBase)])
87 return [r performKeyEquivalent:event];
89 // If the delegate does not implement the BrowserCommandExecutor protocol,
90 // then we don't need to handle browser specific shortcut keys.
91 if (![[self delegate] conformsToProtocol:@protocol(BrowserCommandExecutor)])
92 return [super performKeyEquivalent:event];
94 // Handle per-window shortcuts like cmd-1, but do not handle browser-level
95 // shortcuts like cmd-left (else, cmd-left would do history navigation even
96 // if e.g. the Omnibox has focus).
97 if ([self handleExtraWindowKeyboardShortcut:event])
100 if ([super performKeyEquivalent:event])
103 // Handle per-window shortcuts like Esc after giving everybody else a chance
105 return [self handleDelayedWindowKeyboardShortcut:event];
108 - (BOOL)redispatchKeyEvent:(NSEvent*)event {
110 NSEventType eventType = [event type];
111 if (eventType != NSKeyDown &&
112 eventType != NSKeyUp &&
113 eventType != NSFlagsChanged) {
115 return YES; // Pretend it's been handled in an effort to limit damage.
118 // Ordinarily, the event's window should be this window. However, when
119 // switching between normal and fullscreen mode, we switch out the window, and
120 // the event's window might be the previous window (or even an earlier one if
121 // the renderer is running slowly and several mode switches occur). In this
122 // rare case, we synthesize a new key event so that its associate window
123 // (number) is our own.
124 if ([event window] != self)
125 event = [self keyEventForWindow:self fromKeyEvent:event];
127 // Redispatch the event.
129 redispatchingEvent_ = YES;
130 [NSApp sendEvent:event];
131 redispatchingEvent_ = NO;
133 // If the event was not handled by [NSApp sendEvent:], the sendEvent:
134 // method below will be called, and because |redispatchingEvent_| is YES,
135 // |eventHandled_| will be set to NO.
136 return eventHandled_;
139 - (void)sendEvent:(NSEvent*)event {
140 if (!redispatchingEvent_)
141 [super sendEvent:event];
146 - (NSEvent*)keyEventForWindow:(NSWindow*)window fromKeyEvent:(NSEvent*)event {
147 NSEventType eventType = [event type];
149 // Convert the event's location from the original window's coordinates into
151 NSPoint eventLoc = [event locationInWindow];
152 eventLoc = [[event window] convertBaseToScreen:eventLoc];
153 eventLoc = [self convertScreenToBase:eventLoc];
155 // Various things *only* apply to key down/up.
156 BOOL eventIsARepeat = NO;
157 NSString* eventCharacters = nil;
158 NSString* eventUnmodCharacters = nil;
159 if (eventType == NSKeyDown || eventType == NSKeyUp) {
160 eventIsARepeat = [event isARepeat];
161 eventCharacters = [event characters];
162 eventUnmodCharacters = [event charactersIgnoringModifiers];
165 // This synthesis may be slightly imperfect: we provide nil for the context,
166 // since I (viettrungluu) am sceptical that putting in the original context
167 // (if one is given) is valid.
168 return [NSEvent keyEventWithType:eventType
170 modifierFlags:[event modifierFlags]
171 timestamp:[event timestamp]
172 windowNumber:[window windowNumber]
174 characters:eventCharacters
175 charactersIgnoringModifiers:eventUnmodCharacters
176 isARepeat:eventIsARepeat
177 keyCode:[event keyCode]];
180 @end // ChromeEventProcessingWindow