• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2010 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_event_processing_window.h"
6
7#include "base/logging.h"
8#import "chrome/browser/renderer_host/render_widget_host_view_mac.h"
9#import "chrome/browser/ui/cocoa/browser_command_executor.h"
10#import "chrome/browser/ui/cocoa/browser_frame_view.h"
11#import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
12#include "chrome/browser/global_keyboard_shortcuts_mac.h"
13
14typedef int (*KeyToCommandMapper)(bool, bool, bool, bool, int, unichar);
15
16@interface ChromeEventProcessingWindow ()
17// Duplicate the given key event, but changing the associated window.
18- (NSEvent*)keyEventForWindow:(NSWindow*)window fromKeyEvent:(NSEvent*)event;
19@end
20
21@implementation ChromeEventProcessingWindow
22
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);
33
34  int cmdNum = commandForKeyboardShortcut(cmdKey, shiftKey, cntrlKey, optKey,
35      keyCode, keyChar);
36
37  if (cmdNum != -1) {
38    id executor = [self delegate];
39    // A bit of sanity.
40    DCHECK([executor conformsToProtocol:@protocol(BrowserCommandExecutor)]);
41    DCHECK([executor respondsToSelector:@selector(executeCommand:)]);
42    [executor executeCommand:cmdNum];
43    return YES;
44  }
45  return NO;
46}
47
48- (BOOL)handleExtraWindowKeyboardShortcut:(NSEvent*)event {
49  return [self handleExtraKeyboardShortcut:event
50                                 fromTable:CommandForWindowKeyboardShortcut];
51}
52
53- (BOOL)handleDelayedWindowKeyboardShortcut:(NSEvent*)event {
54  return [self handleExtraKeyboardShortcut:event
55                         fromTable:CommandForDelayedWindowKeyboardShortcut];
56}
57
58- (BOOL)handleExtraBrowserKeyboardShortcut:(NSEvent*)event {
59  return [self handleExtraKeyboardShortcut:event
60                                 fromTable:CommandForBrowserKeyboardShortcut];
61}
62
63- (BOOL)performKeyEquivalent:(NSEvent*)event {
64  if (redispatchingEvent_)
65    return NO;
66
67  // Give the web site a chance to handle the event. If it doesn't want to
68  // handle it, it will call us back with one of the |handle*| methods above.
69  NSResponder* r = [self firstResponder];
70  if ([r isKindOfClass:[RenderWidgetHostViewCocoa class]])
71    return [r performKeyEquivalent:event];
72
73  // If the delegate does not implement the BrowserCommandExecutor protocol,
74  // then we don't need to handle browser specific shortcut keys.
75  if (![[self delegate] conformsToProtocol:@protocol(BrowserCommandExecutor)])
76    return [super performKeyEquivalent:event];
77
78  // Handle per-window shortcuts like cmd-1, but do not handle browser-level
79  // shortcuts like cmd-left (else, cmd-left would do history navigation even
80  // if e.g. the Omnibox has focus).
81  if ([self handleExtraWindowKeyboardShortcut:event])
82    return YES;
83
84  if ([super performKeyEquivalent:event])
85    return YES;
86
87  // Handle per-window shortcuts like Esc after giving everybody else a chance
88  // to handle them
89  return [self handleDelayedWindowKeyboardShortcut:event];
90}
91
92- (BOOL)redispatchKeyEvent:(NSEvent*)event {
93  DCHECK(event);
94  NSEventType eventType = [event type];
95  if (eventType != NSKeyDown &&
96      eventType != NSKeyUp &&
97      eventType != NSFlagsChanged) {
98    NOTREACHED();
99    return YES;  // Pretend it's been handled in an effort to limit damage.
100  }
101
102  // Ordinarily, the event's window should be this window. However, when
103  // switching between normal and fullscreen mode, we switch out the window, and
104  // the event's window might be the previous window (or even an earlier one if
105  // the renderer is running slowly and several mode switches occur). In this
106  // rare case, we synthesize a new key event so that its associate window
107  // (number) is our own.
108  if ([event window] != self)
109    event = [self keyEventForWindow:self fromKeyEvent:event];
110
111  // Redispatch the event.
112  eventHandled_ = YES;
113  redispatchingEvent_ = YES;
114  [NSApp sendEvent:event];
115  redispatchingEvent_ = NO;
116
117  // If the event was not handled by [NSApp sendEvent:], the sendEvent:
118  // method below will be called, and because |redispatchingEvent_| is YES,
119  // |eventHandled_| will be set to NO.
120  return eventHandled_;
121}
122
123- (void)sendEvent:(NSEvent*)event {
124  if (!redispatchingEvent_)
125    [super sendEvent:event];
126  else
127    eventHandled_ = NO;
128}
129
130- (NSEvent*)keyEventForWindow:(NSWindow*)window fromKeyEvent:(NSEvent*)event {
131  NSEventType eventType = [event type];
132
133  // Convert the event's location from the original window's coordinates into
134  // our own.
135  NSPoint eventLoc = [event locationInWindow];
136  eventLoc = [[event window] convertBaseToScreen:eventLoc];
137  eventLoc = [self convertScreenToBase:eventLoc];
138
139  // Various things *only* apply to key down/up.
140  BOOL eventIsARepeat = NO;
141  NSString* eventCharacters = nil;
142  NSString* eventUnmodCharacters = nil;
143  if (eventType == NSKeyDown || eventType == NSKeyUp) {
144    eventIsARepeat = [event isARepeat];
145    eventCharacters = [event characters];
146    eventUnmodCharacters = [event charactersIgnoringModifiers];
147  }
148
149  // This synthesis may be slightly imperfect: we provide nil for the context,
150  // since I (viettrungluu) am sceptical that putting in the original context
151  // (if one is given) is valid.
152  return [NSEvent keyEventWithType:eventType
153                          location:eventLoc
154                     modifierFlags:[event modifierFlags]
155                         timestamp:[event timestamp]
156                      windowNumber:[window windowNumber]
157                           context:nil
158                        characters:eventCharacters
159       charactersIgnoringModifiers:eventUnmodCharacters
160                         isARepeat:eventIsARepeat
161                           keyCode:[event keyCode]];
162}
163
164@end  // ChromeEventProcessingWindow
165