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#include "chrome/browser/ui/cocoa/accelerators_cocoa.h"
6
7#import <Cocoa/Cocoa.h>
8
9#include "base/logging.h"
10#include "base/memory/singleton.h"
11#include "chrome/app/chrome_command_ids.h"
12#import "ui/base/accelerators/platform_accelerator_cocoa.h"
13#import "ui/events/cocoa/cocoa_event_utils.h"
14#import "ui/events/keycodes/keyboard_code_conversion_mac.h"
15
16namespace {
17
18// These accelerators are not associated with a command_id.
19const struct AcceleratorListing {
20  NSUInteger modifiers;  // The Cocoa modifiers.
21  ui::KeyboardCode key_code;  // The key used for cross-platform compatibility.
22} kAcceleratorList [] = {
23  {NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_H},
24  {NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_W},
25  {NSCommandKeyMask | NSAlternateKeyMask | NSShiftKeyMask, ui::VKEY_V},
26  {NSCommandKeyMask, ui::VKEY_E},
27  {NSCommandKeyMask, ui::VKEY_J},
28  {NSCommandKeyMask, ui::VKEY_OEM_1},
29  {NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_OEM_1},
30  {NSCommandKeyMask, ui::VKEY_OEM_COMMA},
31  {NSCommandKeyMask | NSControlKeyMask, ui::VKEY_SPACE},
32};
33
34const struct AcceleratorMapping {
35  int command_id;
36  NSUInteger modifiers;  // The Cocoa modifiers.
37  ui::KeyboardCode key_code;  // The key used for cross-platform compatibility.
38} kAcceleratorMap[] = {
39  // Accelerators used in the toolbar menu.
40  {IDC_CLEAR_BROWSING_DATA, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_BACK},
41  {IDC_COPY, NSCommandKeyMask, ui::VKEY_C},
42  {IDC_CUT, NSCommandKeyMask, ui::VKEY_X},
43  {IDC_DEV_TOOLS, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_I},
44  {IDC_DEV_TOOLS_CONSOLE, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_J},
45  {IDC_FIND, NSCommandKeyMask, ui::VKEY_F},
46  {IDC_FULLSCREEN, NSCommandKeyMask | NSControlKeyMask, ui::VKEY_F},
47  {IDC_NEW_INCOGNITO_WINDOW, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_N},
48  {IDC_NEW_TAB, NSCommandKeyMask, ui::VKEY_T},
49  {IDC_NEW_WINDOW, NSCommandKeyMask, ui::VKEY_N},
50  {IDC_PASTE, NSCommandKeyMask, ui::VKEY_V},
51  {IDC_PRINT, NSCommandKeyMask, ui::VKEY_P},
52  {IDC_RESTORE_TAB, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_T},
53  {IDC_SAVE_PAGE, NSCommandKeyMask, ui::VKEY_S},
54  {IDC_SHOW_BOOKMARK_BAR, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_B},
55  {IDC_SHOW_BOOKMARK_MANAGER, NSCommandKeyMask | NSAlternateKeyMask,
56   ui::VKEY_B},
57  {IDC_BOOKMARK_PAGE, NSCommandKeyMask, ui::VKEY_D},
58  {IDC_SHOW_DOWNLOADS, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_J},
59  {IDC_SHOW_HISTORY, NSCommandKeyMask, ui::VKEY_Y},
60  {IDC_VIEW_SOURCE, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_U},
61  {IDC_ZOOM_MINUS, NSCommandKeyMask, ui::VKEY_OEM_MINUS},
62  {IDC_ZOOM_PLUS, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_OEM_PLUS},
63
64  // Accelerators used in MainMenu.xib, but not the toolbar menu.
65  {IDC_HIDE_APP, NSCommandKeyMask, ui::VKEY_H},
66  {IDC_EXIT, NSCommandKeyMask, ui::VKEY_Q},
67  {IDC_OPEN_FILE, NSCommandKeyMask, ui::VKEY_O},
68  {IDC_FOCUS_LOCATION, NSCommandKeyMask, ui::VKEY_L},
69  {IDC_CLOSE_WINDOW, NSCommandKeyMask, ui::VKEY_W},
70  {IDC_EMAIL_PAGE_LOCATION, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_I},
71#if !defined(DISABLE_BASIC_PRINTING)
72  {IDC_BASIC_PRINT, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_P},
73#endif  // !DISABLE_BASIC_PRINTING
74  {IDC_CONTENT_CONTEXT_UNDO, NSCommandKeyMask, ui::VKEY_Z},
75  {IDC_CONTENT_CONTEXT_REDO, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_Z},
76  {IDC_CONTENT_CONTEXT_CUT, NSCommandKeyMask, ui::VKEY_X},
77  {IDC_CONTENT_CONTEXT_COPY, NSCommandKeyMask, ui::VKEY_C},
78  {IDC_CONTENT_CONTEXT_PASTE, NSCommandKeyMask, ui::VKEY_V},
79  {IDC_CONTENT_CONTEXT_PASTE_AND_MATCH_STYLE,
80   NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_V},
81  {IDC_CONTENT_CONTEXT_SELECTALL, NSCommandKeyMask, ui::VKEY_A},
82  {IDC_FOCUS_SEARCH, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_F},
83  {IDC_FIND_NEXT, NSCommandKeyMask, ui::VKEY_G},
84  {IDC_FIND_PREVIOUS, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_G},
85  {IDC_ZOOM_PLUS, NSCommandKeyMask, ui::VKEY_OEM_PLUS},
86  {IDC_ZOOM_MINUS, NSCommandKeyMask, ui::VKEY_OEM_MINUS},
87  {IDC_STOP, NSCommandKeyMask, ui::VKEY_OEM_PERIOD},
88  {IDC_RELOAD, NSCommandKeyMask, ui::VKEY_R},
89  {IDC_RELOAD_IGNORING_CACHE, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_R},
90  {IDC_PRESENTATION_MODE, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_F},
91  {IDC_ZOOM_NORMAL, NSCommandKeyMask, ui::VKEY_0},
92  {IDC_HOME, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_H},
93  {IDC_BACK, NSCommandKeyMask, ui::VKEY_OEM_4},
94  {IDC_FORWARD, NSCommandKeyMask, ui::VKEY_OEM_6},
95  {IDC_BOOKMARK_ALL_TABS, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_D},
96  {IDC_MINIMIZE_WINDOW, NSCommandKeyMask, ui::VKEY_M},
97  {IDC_SELECT_NEXT_TAB, NSCommandKeyMask | NSAlternateKeyMask, ui::VKEY_RIGHT},
98  {IDC_SELECT_PREVIOUS_TAB, NSCommandKeyMask | NSAlternateKeyMask,
99   ui::VKEY_LEFT},
100  {IDC_HELP_PAGE_VIA_MENU, NSCommandKeyMask | NSShiftKeyMask, ui::VKEY_OEM_2},
101};
102
103// Create a Cocoa platform accelerator given a cross platform |key_code| and
104// the |cocoa_modifiers|.
105scoped_ptr<ui::PlatformAccelerator> PlatformAcceleratorFromKeyCode(
106    ui::KeyboardCode key_code,
107    NSUInteger cocoa_modifiers) {
108  unichar character;
109  unichar char_no_modifiers;
110  int result = ui::MacKeyCodeForWindowsKeyCode(
111      key_code, cocoa_modifiers, &character, &char_no_modifiers);
112  DCHECK(result != -1);
113  NSString* key_equivalent = [NSString stringWithFormat:@"%C", character];
114
115  return scoped_ptr<ui::PlatformAccelerator>(
116      new ui::PlatformAcceleratorCocoa(key_equivalent, cocoa_modifiers));
117}
118
119// Create a cross platform accelerator given a cross platform |key_code| and
120// the |cocoa_modifiers|.
121ui::Accelerator AcceleratorFromKeyCode(ui::KeyboardCode key_code,
122                                       NSUInteger cocoa_modifiers) {
123  int cross_platform_modifiers = ui::EventFlagsFromModifiers(cocoa_modifiers);
124  ui::Accelerator accelerator(key_code, cross_platform_modifiers);
125
126  scoped_ptr<ui::PlatformAccelerator> platform_accelerator =
127      PlatformAcceleratorFromKeyCode(key_code, cocoa_modifiers);
128  accelerator.set_platform_accelerator(platform_accelerator.Pass());
129  return accelerator;
130}
131
132}  // namespace
133
134AcceleratorsCocoa::AcceleratorsCocoa() {
135  for (size_t i = 0; i < arraysize(kAcceleratorMap); ++i) {
136    const AcceleratorMapping& entry = kAcceleratorMap[i];
137    ui::Accelerator accelerator =
138        AcceleratorFromKeyCode(entry.key_code, entry.modifiers);
139    accelerators_.insert(std::make_pair(entry.command_id, accelerator));
140  }
141
142  for (size_t i = 0; i < arraysize(kAcceleratorList); ++i) {
143    const AcceleratorListing& entry = kAcceleratorList[i];
144    ui::Accelerator accelerator =
145        AcceleratorFromKeyCode(entry.key_code, entry.modifiers);
146    accelerator_vector_.push_back(accelerator);
147  }
148}
149
150AcceleratorsCocoa::~AcceleratorsCocoa() {}
151
152// static
153AcceleratorsCocoa* AcceleratorsCocoa::GetInstance() {
154  return Singleton<AcceleratorsCocoa>::get();
155}
156
157const ui::Accelerator* AcceleratorsCocoa::GetAcceleratorForCommand(
158    int command_id) {
159  AcceleratorMap::iterator it = accelerators_.find(command_id);
160  if (it == accelerators_.end())
161    return NULL;
162  return &it->second;
163}
164
165const ui::Accelerator* AcceleratorsCocoa::GetAcceleratorForHotKey(
166    NSString* key_equivalent, NSUInteger modifiers) const {
167  for (AcceleratorVector::const_iterator it = accelerator_vector_.begin();
168       it != accelerator_vector_.end();
169       ++it) {
170    const ui::Accelerator& accelerator = *it;
171    const ui::PlatformAcceleratorCocoa* platform_accelerator =
172        static_cast<const ui::PlatformAcceleratorCocoa*>(
173            accelerator.platform_accelerator());
174    unichar character;
175    unichar char_no_modifiers;
176    int result =
177        ui::MacKeyCodeForWindowsKeyCode(accelerator.key_code(),
178                                        platform_accelerator->modifier_mask(),
179                                        &character,
180                                        &char_no_modifiers);
181    if (result == -1)
182      return NULL;
183
184    // Check for a match in the modifiers and key_equivalent.
185    NSUInteger mask = platform_accelerator->modifier_mask();
186    BOOL maskEqual =
187        (mask == modifiers) || ((mask & (~NSShiftKeyMask)) == modifiers);
188    NSString* string = [NSString stringWithFormat:@"%C", character];
189    if ([string isEqual:key_equivalent] && maskEqual)
190      return &*it;
191  }
192
193  return NULL;
194}
195