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.
5 #import "chrome/browser/ui/cocoa/browser_window_utils.h"
7 #include <Carbon/Carbon.h>
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"
18 using content::NativeWebKeyboardEvent;
20 @interface MenuWalker : NSObject
21 + (NSMenuItem*)itemForKeyEquivalent:(NSEvent*)key
25 @implementation MenuWalker
26 + (NSMenuItem*)itemForKeyEquivalent:(NSEvent*)key
28 NSMenuItem* result = nil;
30 for (NSMenuItem* item in [menu itemArray]) {
31 NSMenu* submenu = [item submenu];
33 if (submenu != [NSApp servicesMenu])
34 result = [self itemForKeyEquivalent:key
36 } else if ([item cr_firesForKeyEventIfEnabled:key]) {
48 @implementation BrowserWindowUtils
49 + (BOOL)shouldHandleKeyboardEvent:(const NativeWebKeyboardEvent&)event {
50 if (event.skip_in_browser || event.type == NativeWebKeyboardEvent::Char)
52 DCHECK(event.os_event != NULL);
56 + (int)getCommandId:(const NativeWebKeyboardEvent&)event {
57 if ([event.os_event type] != NSKeyDown)
61 NSMenuItem* item = [MenuWalker itemForKeyEquivalent:event.os_event
62 menu:[NSApp mainMenu]];
64 if (item && [item action] == @selector(commandDispatch:) && [item tag] > 0)
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;
74 // "Exit" doesn't use the |commandDispatch:| mechanism either.
75 if (item && [item action] == @selector(terminate:))
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);
87 int cmdNum = CommandForWindowKeyboardShortcut(
88 cmdKey, shiftKey, cntrlKey, optKey, keyCode, keyChar);
92 cmdNum = CommandForBrowserKeyboardShortcut(
93 cmdKey, shiftKey, cntrlKey, optKey, keyCode, keyChar);
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]]);
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.
114 if ([[NSApp mainMenu] performKeyEquivalent:event])
117 if ([event_window handleExtraBrowserKeyboardShortcut:event])
120 if ([event_window handleExtraWindowKeyboardShortcut:event])
123 if ([event_window handleDelayedWindowKeyboardShortcut:event])
127 return [event_window redispatchKeyEvent:event];
130 + (NSString*)scheduleReplaceOldTitle:(NSString*)oldTitle
131 withNewTitle:(NSString*)newTitle
132 forWindow:(NSWindow*)window {
134 [[NSRunLoop currentRunLoop]
135 cancelPerformSelector:@selector(setTitle:)
139 [[NSRunLoop currentRunLoop]
140 performSelector:@selector(setTitle:)
144 modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
145 return [newTitle copy];
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
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.
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;
167 + (NSPoint)themeImagePositionFor:(NSView*)windowView
168 withTabStrip:(NSView*)tabStripView
169 alignment:(ThemeImageAlignment)alignment {
171 return NSMakePoint(kPatternHorizontalOffset,
172 NSHeight([windowView bounds]) +
173 kPatternVerticalOffsetNoTabStrip);
177 [BrowserWindowUtils themeImagePositionInTabStripCoords:tabStripView
178 alignment:alignment];
179 return [tabStripView convertPoint:position toView:windowView];
182 + (NSPoint)themeImagePositionInTabStripCoords:(NSView*)tabStripView
183 alignment:(ThemeImageAlignment)alignment {
184 DCHECK(tabStripView);
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);
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);
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);