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