summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa/chrome_event_processing_window.mm
blob: 7426a948121849e3867e225cd592e1efd88d1316 (plain)
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
// Copyright (c) 2010 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/cocoa/chrome_event_processing_window.h"

#include "base/logging.h"
#import "chrome/browser/cocoa/browser_command_executor.h"
#import "chrome/browser/cocoa/browser_frame_view.h"
#import "chrome/browser/cocoa/tab_strip_controller.h"
#import "chrome/browser/renderer_host/render_widget_host_view_mac.h"
#include "chrome/browser/global_keyboard_shortcuts_mac.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 {
  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 isKindOfClass:[RenderWidgetHostViewCocoa class]])
    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