Home | History | Annotate | Download | only in cocoa
      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 // Our patterns want to be drawn from the upper left hand corner of the view.
    149 // Cocoa wants to do it from the lower left of the window.
    150 //
    151 // Rephase our pattern to fit this view. Some other views (Tabs, Toolbar etc.)
    152 // will phase their patterns relative to this so all the views look right.
    153 //
    154 // To line up the background pattern with the pattern in the browser window
    155 // the background pattern for the tabs needs to be moved left by 5 pixels.
    156 const CGFloat kPatternHorizontalOffset = -5;
    157 // To match Windows and CrOS, have to offset vertically by 2 pixels.
    158 // Without tab strip, offset an extra pixel (determined by experimentation).
    159 const CGFloat kPatternVerticalOffset = 2;
    160 const CGFloat kPatternVerticalOffsetNoTabStrip = 3;
    161 
    162 
    163 + (NSPoint)themePatternPhaseFor:(NSView*)windowView
    164                    withTabStrip:(NSView*)tabStripView {
    165   // When we have a tab strip, line up with the top of the tab, otherwise,
    166   // line up with the top of the window.
    167   if (!tabStripView)
    168     return NSMakePoint(kPatternHorizontalOffset,
    169                        NSHeight([windowView bounds])
    170                        + kPatternVerticalOffsetNoTabStrip);
    171 
    172   NSRect tabStripViewWindowBounds = [tabStripView bounds];
    173   tabStripViewWindowBounds =
    174       [tabStripView convertRect:tabStripViewWindowBounds
    175                          toView:windowView];
    176   return NSMakePoint(NSMinX(tabStripViewWindowBounds)
    177                          + kPatternHorizontalOffset,
    178                      NSMinY(tabStripViewWindowBounds)
    179                          + [TabStripController defaultTabHeight]
    180                          + kPatternVerticalOffset);
    181 }
    182 
    183 + (void)activateWindowForController:(NSWindowController*)controller {
    184   // Per http://crbug.com/73779 and http://crbug.com/75223, we need this to
    185   // properly activate windows if Chrome is not the active application.
    186   [[controller window] makeKeyAndOrderFront:controller];
    187   ProcessSerialNumber psn;
    188   GetCurrentProcess(&psn);
    189   SetFrontProcessWithOptions(&psn, kSetFrontProcessFrontWindowOnly);
    190 }
    191 
    192 @end
    193