- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / browser_window_utils.mm
1 // Copyright (c) 2011 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/browser_window_utils.h"
6
7 #include <Carbon/Carbon.h>
8
9 #include "base/logging.h"
10 #include "chrome/app/chrome_command_ids.h"
11 #include "chrome/browser/global_keyboard_shortcuts_mac.h"
12 #include "chrome/browser/ui/browser.h"
13 #import "chrome/browser/ui/cocoa/chrome_event_processing_window.h"
14 #import "chrome/browser/ui/cocoa/nsmenuitem_additions.h"
15 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
16 #include "content/public/browser/native_web_keyboard_event.h"
17
18 using content::NativeWebKeyboardEvent;
19
20 @interface MenuWalker : NSObject
21 + (NSMenuItem*)itemForKeyEquivalent:(NSEvent*)key
22                                menu:(NSMenu*)menu;
23 @end
24
25 @implementation MenuWalker
26 + (NSMenuItem*)itemForKeyEquivalent:(NSEvent*)key
27                                menu:(NSMenu*)menu {
28   NSMenuItem* result = nil;
29
30   for (NSMenuItem* item in [menu itemArray]) {
31     NSMenu* submenu = [item submenu];
32     if (submenu) {
33       if (submenu != [NSApp servicesMenu])
34         result = [self itemForKeyEquivalent:key
35                                        menu:submenu];
36     } else if ([item cr_firesForKeyEventIfEnabled:key]) {
37       result = item;
38     }
39
40     if (result)
41       break;
42   }
43
44   return result;
45 }
46 @end
47
48 @implementation BrowserWindowUtils
49 + (BOOL)shouldHandleKeyboardEvent:(const NativeWebKeyboardEvent&)event {
50   if (event.skip_in_browser || event.type == NativeWebKeyboardEvent::Char)
51     return NO;
52   DCHECK(event.os_event != NULL);
53   return YES;
54 }
55
56 + (int)getCommandId:(const NativeWebKeyboardEvent&)event {
57   if ([event.os_event type] != NSKeyDown)
58     return -1;
59
60   // Look in menu.
61   NSMenuItem* item = [MenuWalker itemForKeyEquivalent:event.os_event
62                                                  menu:[NSApp mainMenu]];
63
64   if (item && [item action] == @selector(commandDispatch:) && [item tag] > 0)
65     return [item tag];
66
67   // "Close window" doesn't use the |commandDispatch:| mechanism. Menu items
68   // that do not correspond to IDC_ constants need no special treatment however,
69   // as they can't be blacklisted in
70   // |BrowserCommandController::IsReservedCommandOrKey()| anyhow.
71   if (item && [item action] == @selector(performClose:))
72     return IDC_CLOSE_WINDOW;
73
74   // "Exit" doesn't use the |commandDispatch:| mechanism either.
75   if (item && [item action] == @selector(terminate:))
76     return IDC_EXIT;
77
78   // Look in secondary keyboard shortcuts.
79   NSUInteger modifiers = [event.os_event modifierFlags];
80   const bool cmdKey = (modifiers & NSCommandKeyMask) != 0;
81   const bool shiftKey = (modifiers & NSShiftKeyMask) != 0;
82   const bool cntrlKey = (modifiers & NSControlKeyMask) != 0;
83   const bool optKey = (modifiers & NSAlternateKeyMask) != 0;
84   const int keyCode = [event.os_event keyCode];
85   const unichar keyChar = KeyCharacterForEvent(event.os_event);
86
87   int cmdNum = CommandForWindowKeyboardShortcut(
88       cmdKey, shiftKey, cntrlKey, optKey, keyCode, keyChar);
89   if (cmdNum != -1)
90     return cmdNum;
91
92   cmdNum = CommandForBrowserKeyboardShortcut(
93       cmdKey, shiftKey, cntrlKey, optKey, keyCode, keyChar);
94   if (cmdNum != -1)
95     return cmdNum;
96
97   return -1;
98 }
99
100 + (BOOL)handleKeyboardEvent:(NSEvent*)event
101                    inWindow:(NSWindow*)window {
102   ChromeEventProcessingWindow* event_window =
103       static_cast<ChromeEventProcessingWindow*>(window);
104   DCHECK([event_window isKindOfClass:[ChromeEventProcessingWindow class]]);
105
106   // Do not fire shortcuts on key up.
107   if ([event type] == NSKeyDown) {
108     // Send the event to the menu before sending it to the browser/window
109     // shortcut handling, so that if a user configures cmd-left to mean
110     // "previous tab", it takes precedence over the built-in "history back"
111     // binding. Other than that, the |-redispatchKeyEvent:| call would take care
112     // of invoking the original menu item shortcut as well.
113
114     if ([[NSApp mainMenu] performKeyEquivalent:event])
115       return true;
116
117     if ([event_window handleExtraBrowserKeyboardShortcut:event])
118       return true;
119
120     if ([event_window handleExtraWindowKeyboardShortcut:event])
121       return true;
122
123     if ([event_window handleDelayedWindowKeyboardShortcut:event])
124       return true;
125   }
126
127   return [event_window redispatchKeyEvent:event];
128 }
129
130 + (NSString*)scheduleReplaceOldTitle:(NSString*)oldTitle
131                         withNewTitle:(NSString*)newTitle
132                            forWindow:(NSWindow*)window {
133   if (oldTitle)
134     [[NSRunLoop currentRunLoop]
135         cancelPerformSelector:@selector(setTitle:)
136                        target:window
137                      argument:oldTitle];
138
139   [[NSRunLoop currentRunLoop]
140       performSelector:@selector(setTitle:)
141                target:window
142              argument:newTitle
143                 order:0
144                 modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
145   return [newTitle copy];
146 }
147
148 // The titlebar/tabstrip header on the mac is slightly smaller than on Windows.
149 // There is also no window frame to the left and right of the web contents on
150 // mac.
151 // To keep:
152 // - the window background pattern (IDR_THEME_FRAME.*) lined up vertically with
153 // the tab and toolbar patterns
154 // - the toolbar pattern lined up horizontally with the NTP background.
155 // we have to shift the pattern slightly, rather than drawing from the top left
156 // corner of the frame / tabstrip. The offsets below were empirically determined
157 // in order to line these patterns up.
158 //
159 // This will make the themes look slightly different than in Windows/Linux
160 // because of the differing heights between window top and tab top, but this has
161 // been approved by UI.
162 const CGFloat kPatternHorizontalOffset = -5;
163 // Without tab strip, offset an extra pixel (determined by experimentation).
164 const CGFloat kPatternVerticalOffset = 2;
165 const CGFloat kPatternVerticalOffsetNoTabStrip = 3;
166
167 + (NSPoint)themeImagePositionFor:(NSView*)windowView
168                     withTabStrip:(NSView*)tabStripView
169                        alignment:(ThemeImageAlignment)alignment {
170   if (!tabStripView) {
171     return NSMakePoint(kPatternHorizontalOffset,
172                        NSHeight([windowView bounds]) +
173                            kPatternVerticalOffsetNoTabStrip);
174   }
175
176   NSPoint position =
177       [BrowserWindowUtils themeImagePositionInTabStripCoords:tabStripView
178                                                    alignment:alignment];
179   return [tabStripView convertPoint:position toView:windowView];
180 }
181
182 + (NSPoint)themeImagePositionInTabStripCoords:(NSView*)tabStripView
183                                     alignment:(ThemeImageAlignment)alignment {
184   DCHECK(tabStripView);
185
186   if (alignment == THEME_IMAGE_ALIGN_WITH_TAB_STRIP) {
187     // The theme image is lined up with the top of the tab which is below the
188     // top of the tab strip.
189     return NSMakePoint(kPatternHorizontalOffset,
190                        [TabStripController defaultTabHeight] +
191                            kPatternVerticalOffset);
192   }
193   // The theme image is lined up with the top of the tab strip (as opposed to
194   // the top of the tab above). This is the same as lining up with the top of
195   // the window's root view when not in presentation mode.
196   return NSMakePoint(kPatternHorizontalOffset,
197                      NSHeight([tabStripView bounds]) +
198                          kPatternVerticalOffsetNoTabStrip);
199 }
200
201 + (void)activateWindowForController:(NSWindowController*)controller {
202   // Per http://crbug.com/73779 and http://crbug.com/75223, we need this to
203   // properly activate windows if Chrome is not the active application.
204   [[controller window] makeKeyAndOrderFront:controller];
205   ProcessSerialNumber psn;
206   GetCurrentProcess(&psn);
207   SetFrontProcessWithOptions(&psn, kSetFrontProcessFrontWindowOnly);
208 }
209
210 @end