- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / chrome_event_processing_window.mm
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.
4
5 #import "chrome/browser/ui/cocoa/chrome_event_processing_window.h"
6
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"
13
14 typedef 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   NSWindow* window = event.window;
68   if (window) {
69     BrowserWindowController* controller = [window windowController];
70     if ([controller respondsToSelector:@selector(handledByExtensionCommand:)]) {
71       if ([controller handledByExtensionCommand:event])
72         return YES;
73     }
74   }
75
76   // Give the web site a chance to handle the event. If it doesn't want to
77   // handle it, it will call us back with one of the |handle*| methods above.
78   NSResponder* r = [self firstResponder];
79   if ([r conformsToProtocol:@protocol(RenderWidgetHostViewMacBase)])
80     return [r performKeyEquivalent:event];
81
82   // If the delegate does not implement the BrowserCommandExecutor protocol,
83   // then we don't need to handle browser specific shortcut keys.
84   if (![[self delegate] conformsToProtocol:@protocol(BrowserCommandExecutor)])
85     return [super performKeyEquivalent:event];
86
87   // Handle per-window shortcuts like cmd-1, but do not handle browser-level
88   // shortcuts like cmd-left (else, cmd-left would do history navigation even
89   // if e.g. the Omnibox has focus).
90   if ([self handleExtraWindowKeyboardShortcut:event])
91     return YES;
92
93   if ([super performKeyEquivalent:event])
94     return YES;
95
96   // Handle per-window shortcuts like Esc after giving everybody else a chance
97   // to handle them
98   return [self handleDelayedWindowKeyboardShortcut:event];
99 }
100
101 - (BOOL)redispatchKeyEvent:(NSEvent*)event {
102   DCHECK(event);
103   NSEventType eventType = [event type];
104   if (eventType != NSKeyDown &&
105       eventType != NSKeyUp &&
106       eventType != NSFlagsChanged) {
107     NOTREACHED();
108     return YES;  // Pretend it's been handled in an effort to limit damage.
109   }
110
111   // Ordinarily, the event's window should be this window. However, when
112   // switching between normal and fullscreen mode, we switch out the window, and
113   // the event's window might be the previous window (or even an earlier one if
114   // the renderer is running slowly and several mode switches occur). In this
115   // rare case, we synthesize a new key event so that its associate window
116   // (number) is our own.
117   if ([event window] != self)
118     event = [self keyEventForWindow:self fromKeyEvent:event];
119
120   // Redispatch the event.
121   eventHandled_ = YES;
122   redispatchingEvent_ = YES;
123   [NSApp sendEvent:event];
124   redispatchingEvent_ = NO;
125
126   // If the event was not handled by [NSApp sendEvent:], the sendEvent:
127   // method below will be called, and because |redispatchingEvent_| is YES,
128   // |eventHandled_| will be set to NO.
129   return eventHandled_;
130 }
131
132 - (void)sendEvent:(NSEvent*)event {
133   if (!redispatchingEvent_)
134     [super sendEvent:event];
135   else
136     eventHandled_ = NO;
137 }
138
139 - (NSEvent*)keyEventForWindow:(NSWindow*)window fromKeyEvent:(NSEvent*)event {
140   NSEventType eventType = [event type];
141
142   // Convert the event's location from the original window's coordinates into
143   // our own.
144   NSPoint eventLoc = [event locationInWindow];
145   eventLoc = [[event window] convertBaseToScreen:eventLoc];
146   eventLoc = [self convertScreenToBase:eventLoc];
147
148   // Various things *only* apply to key down/up.
149   BOOL eventIsARepeat = NO;
150   NSString* eventCharacters = nil;
151   NSString* eventUnmodCharacters = nil;
152   if (eventType == NSKeyDown || eventType == NSKeyUp) {
153     eventIsARepeat = [event isARepeat];
154     eventCharacters = [event characters];
155     eventUnmodCharacters = [event charactersIgnoringModifiers];
156   }
157
158   // This synthesis may be slightly imperfect: we provide nil for the context,
159   // since I (viettrungluu) am sceptical that putting in the original context
160   // (if one is given) is valid.
161   return [NSEvent keyEventWithType:eventType
162                           location:eventLoc
163                      modifierFlags:[event modifierFlags]
164                          timestamp:[event timestamp]
165                       windowNumber:[window windowNumber]
166                            context:nil
167                         characters:eventCharacters
168        charactersIgnoringModifiers:eventUnmodCharacters
169                          isARepeat:eventIsARepeat
170                            keyCode:[event keyCode]];
171 }
172
173 @end  // ChromeEventProcessingWindow