| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <AppKit/NSEvent.h> |
| 5 #include <Carbon/Carbon.h> | 6 #include <Carbon/Carbon.h> |
| 6 | 7 |
| 7 #include "chrome/browser/global_keyboard_shortcuts_mac.h" | 8 #include "chrome/browser/global_keyboard_shortcuts_mac.h" |
| 8 | 9 |
| 9 #include "base/basictypes.h" | 10 #include "base/basictypes.h" |
| 11 #include "base/logging.h" |
| 10 #include "chrome/app/chrome_dll_resource.h" | 12 #include "chrome/app/chrome_dll_resource.h" |
| 11 | 13 |
| 12 // Basically, there are two kinds of keyboard shortcuts: Ones that should work | 14 // Basically, there are two kinds of keyboard shortcuts: Ones that should work |
| 13 // only if the tab contents is focused (BrowserKeyboardShortcut), and ones that | 15 // only if the tab contents is focused (BrowserKeyboardShortcut), and ones that |
| 14 // should work in all other cases (WindowKeyboardShortcut). In the latter case, | 16 // should work in all other cases (WindowKeyboardShortcut). In the latter case, |
| 15 // we differentiate between shortcuts that are checked before any other view | 17 // we differentiate between shortcuts that are checked before any other view |
| 16 // gets the chance to handle them (WindowKeyboardShortcut) or after all views | 18 // gets the chance to handle them (WindowKeyboardShortcut) or after all views |
| 17 // had a chance but did not handle the keypress event | 19 // had a chance but did not handle the keypress event |
| 18 // (DelayedWindowKeyboardShortcut) | 20 // (DelayedWindowKeyboardShortcut) |
| 19 | 21 |
| 20 const KeyboardShortcutData* GetWindowKeyboardShortcutTable | 22 const KeyboardShortcutData* GetWindowKeyboardShortcutTable |
| 21 (size_t* num_entries) { | 23 (size_t* num_entries) { |
| 22 static const KeyboardShortcutData keyboard_shortcuts[] = { | 24 static const KeyboardShortcutData keyboard_shortcuts[] = { |
| 23 {true, true, false, false, kVK_ANSI_RightBracket, IDC_SELECT_NEXT_TAB}, | 25 // '{' / '}' characters should be matched earlier than virtual key code |
| 24 {false, false, true, false, kVK_PageDown, IDC_SELECT_NEXT_TAB}, | 26 // (therefore we can match alt-8 as '{' on german keyboards). |
| 25 {false, false, true, false, kVK_Tab, IDC_SELECT_NEXT_TAB}, | 27 {true, false, false, false, 0, '}', IDC_SELECT_NEXT_TAB}, |
| 26 {true, true, false, false, kVK_ANSI_LeftBracket, IDC_SELECT_PREVIOUS_TAB}, | 28 {true, false, false, false, 0, '{', IDC_SELECT_PREVIOUS_TAB}, |
| 27 {false, false, true, false, kVK_PageUp, IDC_SELECT_PREVIOUS_TAB}, | 29 {false, false, true, false, kVK_PageDown, 0, IDC_SELECT_NEXT_TAB}, |
| 28 {false, true, true, false, kVK_Tab, IDC_SELECT_PREVIOUS_TAB}, | 30 {false, false, true, false, kVK_Tab, 0, IDC_SELECT_NEXT_TAB}, |
| 31 {false, false, true, false, kVK_PageUp, 0, IDC_SELECT_PREVIOUS_TAB}, |
| 32 {false, true, true, false, kVK_Tab, 0, IDC_SELECT_PREVIOUS_TAB}, |
| 29 // Cmd-0..8 select the Nth tab, with cmd-9 being "last tab". | 33 // Cmd-0..8 select the Nth tab, with cmd-9 being "last tab". |
| 30 {true, false, false, false, kVK_ANSI_1, IDC_SELECT_TAB_0}, | 34 {true, false, false, false, kVK_ANSI_1, 0, IDC_SELECT_TAB_0}, |
| 31 {true, false, false, false, kVK_ANSI_2, IDC_SELECT_TAB_1}, | 35 {true, false, false, false, kVK_ANSI_2, 0, IDC_SELECT_TAB_1}, |
| 32 {true, false, false, false, kVK_ANSI_3, IDC_SELECT_TAB_2}, | 36 {true, false, false, false, kVK_ANSI_3, 0, IDC_SELECT_TAB_2}, |
| 33 {true, false, false, false, kVK_ANSI_4, IDC_SELECT_TAB_3}, | 37 {true, false, false, false, kVK_ANSI_4, 0, IDC_SELECT_TAB_3}, |
| 34 {true, false, false, false, kVK_ANSI_5, IDC_SELECT_TAB_4}, | 38 {true, false, false, false, kVK_ANSI_5, 0, IDC_SELECT_TAB_4}, |
| 35 {true, false, false, false, kVK_ANSI_6, IDC_SELECT_TAB_5}, | 39 {true, false, false, false, kVK_ANSI_6, 0, IDC_SELECT_TAB_5}, |
| 36 {true, false, false, false, kVK_ANSI_7, IDC_SELECT_TAB_6}, | 40 {true, false, false, false, kVK_ANSI_7, 0, IDC_SELECT_TAB_6}, |
| 37 {true, false, false, false, kVK_ANSI_8, IDC_SELECT_TAB_7}, | 41 {true, false, false, false, kVK_ANSI_8, 0, IDC_SELECT_TAB_7}, |
| 38 {true, false, false, false, kVK_ANSI_9, IDC_SELECT_LAST_TAB}, | 42 {true, false, false, false, kVK_ANSI_9, 0, IDC_SELECT_LAST_TAB}, |
| 39 }; | 43 }; |
| 40 | 44 |
| 41 *num_entries = arraysize(keyboard_shortcuts); | 45 *num_entries = arraysize(keyboard_shortcuts); |
| 42 | 46 |
| 43 return keyboard_shortcuts; | 47 return keyboard_shortcuts; |
| 44 } | 48 } |
| 45 | 49 |
| 46 const KeyboardShortcutData* GetDelayedWindowKeyboardShortcutTable | 50 const KeyboardShortcutData* GetDelayedWindowKeyboardShortcutTable |
| 47 (size_t* num_entries) { | 51 (size_t* num_entries) { |
| 48 static const KeyboardShortcutData keyboard_shortcuts[] = { | 52 static const KeyboardShortcutData keyboard_shortcuts[] = { |
| 49 {false, false, false, false, kVK_Escape, IDC_STOP}, | 53 {false, false, false, false, kVK_Escape, 0, IDC_STOP}, |
| 50 }; | 54 }; |
| 51 | 55 |
| 52 *num_entries = arraysize(keyboard_shortcuts); | 56 *num_entries = arraysize(keyboard_shortcuts); |
| 53 | 57 |
| 54 return keyboard_shortcuts; | 58 return keyboard_shortcuts; |
| 55 } | 59 } |
| 56 | 60 |
| 57 const KeyboardShortcutData* GetBrowserKeyboardShortcutTable | 61 const KeyboardShortcutData* GetBrowserKeyboardShortcutTable |
| 58 (size_t* num_entries) { | 62 (size_t* num_entries) { |
| 59 static const KeyboardShortcutData keyboard_shortcuts[] = { | 63 static const KeyboardShortcutData keyboard_shortcuts[] = { |
| 60 {true, false, false, false, kVK_LeftArrow, IDC_BACK}, | 64 {true, false, false, false, kVK_LeftArrow, 0, IDC_BACK}, |
| 61 {true, false, false, false, kVK_RightArrow, IDC_FORWARD}, | 65 {true, false, false, false, kVK_RightArrow, 0, IDC_FORWARD}, |
| 62 {false, false, false, false, kVK_Delete, IDC_BACK}, | 66 {false, false, false, false, kVK_Delete, 0, IDC_BACK}, |
| 63 {false, true, false, false, kVK_Delete, IDC_FORWARD}, | 67 {false, true, false, false, kVK_Delete, 0, IDC_FORWARD}, |
| 64 }; | 68 }; |
| 65 | 69 |
| 66 *num_entries = arraysize(keyboard_shortcuts); | 70 *num_entries = arraysize(keyboard_shortcuts); |
| 67 | 71 |
| 68 return keyboard_shortcuts; | 72 return keyboard_shortcuts; |
| 69 } | 73 } |
| 70 | 74 |
| 75 static bool MatchesEventForKeyboardShortcut( |
| 76 const KeyboardShortcutData& shortcut, |
| 77 bool command_key, bool shift_key, bool cntrl_key, bool opt_key, |
| 78 int vkey_code, unichar key_char) { |
| 79 // Expects that one of |key_char| or |vkey_code| is 0. |
| 80 DCHECK((shortcut.key_char == 0) ^ (shortcut.vkey_code == 0)); |
| 81 if (shortcut.key_char) { |
| 82 // The given shortcut key is to be matched by a keyboard character. |
| 83 // In this case we ignore shift and opt (alt) key modifiers, because |
| 84 // the character may be generated by a combination with those keys. |
| 85 if (shortcut.command_key == command_key && |
| 86 shortcut.cntrl_key == cntrl_key && |
| 87 shortcut.key_char == key_char) |
| 88 return true; |
| 89 } else if (shortcut.vkey_code) { |
| 90 // The given shortcut key is to be matched by a virtual key code. |
| 91 if (shortcut.command_key == command_key && |
| 92 shortcut.shift_key == shift_key && |
| 93 shortcut.cntrl_key == cntrl_key && |
| 94 shortcut.opt_key == opt_key && |
| 95 shortcut.vkey_code == vkey_code) |
| 96 return true; |
| 97 } else { |
| 98 NOTREACHED(); // Shouldn't happen. |
| 99 } |
| 100 return false; |
| 101 } |
| 102 |
| 71 static int CommandForKeyboardShortcut( | 103 static int CommandForKeyboardShortcut( |
| 72 const KeyboardShortcutData* (*get_keyboard_shortcut_table)(size_t*), | 104 const KeyboardShortcutData* (*get_keyboard_shortcut_table)(size_t*), |
| 73 bool command_key, bool shift_key, bool cntrl_key, bool opt_key, | 105 bool command_key, bool shift_key, bool cntrl_key, bool opt_key, |
| 74 int vkey_code) { | 106 int vkey_code, unichar key_char) { |
| 75 | 107 |
| 76 // Scan through keycodes and see if it corresponds to one of the global | 108 // Scan through keycodes and see if it corresponds to one of the global |
| 77 // shortcuts on file. | 109 // shortcuts on file. |
| 78 // | 110 // |
| 79 // TODO(jeremy): Change this into a hash table once we get enough | 111 // TODO(jeremy): Change this into a hash table once we get enough |
| 80 // entries in the array to make a difference. | 112 // entries in the array to make a difference. |
| 113 // (When turning this into a hash table, note that the current behavior |
| 114 // relies on the order of the table (see the comment for '{' / '}' above). |
| 81 size_t num_shortcuts = 0; | 115 size_t num_shortcuts = 0; |
| 82 const KeyboardShortcutData *it = get_keyboard_shortcut_table(&num_shortcuts); | 116 const KeyboardShortcutData *it = get_keyboard_shortcut_table(&num_shortcuts); |
| 83 for (size_t i = 0; i < num_shortcuts; ++i, ++it) { | 117 for (size_t i = 0; i < num_shortcuts; ++i, ++it) { |
| 84 if (it->command_key == command_key && | 118 if (MatchesEventForKeyboardShortcut(*it, command_key, shift_key, cntrl_key, |
| 85 it->shift_key == shift_key && | 119 opt_key, vkey_code, key_char)) |
| 86 it->cntrl_key == cntrl_key && | |
| 87 it->opt_key == opt_key && | |
| 88 it->vkey_code == vkey_code) { | |
| 89 return it->chrome_command; | 120 return it->chrome_command; |
| 90 } | |
| 91 } | 121 } |
| 92 | 122 |
| 93 return -1; | 123 return -1; |
| 94 } | 124 } |
| 95 | 125 |
| 96 int CommandForWindowKeyboardShortcut( | 126 int CommandForWindowKeyboardShortcut( |
| 97 bool command_key, bool shift_key, bool cntrl_key, bool opt_key, | 127 bool command_key, bool shift_key, bool cntrl_key, bool opt_key, |
| 98 int vkey_code) { | 128 int vkey_code, unichar key_char) { |
| 99 return CommandForKeyboardShortcut(GetWindowKeyboardShortcutTable, | 129 return CommandForKeyboardShortcut(GetWindowKeyboardShortcutTable, |
| 100 command_key, shift_key, | 130 command_key, shift_key, |
| 101 cntrl_key, opt_key, vkey_code); | 131 cntrl_key, opt_key, vkey_code, |
| 132 key_char); |
| 102 } | 133 } |
| 103 | 134 |
| 104 int CommandForDelayedWindowKeyboardShortcut( | 135 int CommandForDelayedWindowKeyboardShortcut( |
| 105 bool command_key, bool shift_key, bool cntrl_key, bool opt_key, | 136 bool command_key, bool shift_key, bool cntrl_key, bool opt_key, |
| 106 int vkey_code) { | 137 int vkey_code, unichar key_char) { |
| 107 return CommandForKeyboardShortcut(GetDelayedWindowKeyboardShortcutTable, | 138 return CommandForKeyboardShortcut(GetDelayedWindowKeyboardShortcutTable, |
| 108 command_key, shift_key, | 139 command_key, shift_key, |
| 109 cntrl_key, opt_key, vkey_code); | 140 cntrl_key, opt_key, vkey_code, |
| 141 key_char); |
| 110 } | 142 } |
| 111 | 143 |
| 112 int CommandForBrowserKeyboardShortcut( | 144 int CommandForBrowserKeyboardShortcut( |
| 113 bool command_key, bool shift_key, bool cntrl_key, bool opt_key, | 145 bool command_key, bool shift_key, bool cntrl_key, bool opt_key, |
| 114 int vkey_code) { | 146 int vkey_code, unichar key_char) { |
| 115 return CommandForKeyboardShortcut(GetBrowserKeyboardShortcutTable, | 147 return CommandForKeyboardShortcut(GetBrowserKeyboardShortcutTable, |
| 116 command_key, shift_key, | 148 command_key, shift_key, |
| 117 cntrl_key, opt_key, vkey_code); | 149 cntrl_key, opt_key, vkey_code, |
| 150 key_char); |
| 118 } | 151 } |
| 152 |
| 153 unichar KeyCharacterForEvent(NSEvent* event) { |
| 154 const NSString* eventString = [event charactersIgnoringModifiers]; |
| 155 const NSString* characters = [event characters]; |
| 156 |
| 157 if ([eventString length] != 1) |
| 158 return 0; |
| 159 |
| 160 if ([characters length] != 1) |
| 161 return [eventString characterAtIndex:0]; |
| 162 |
| 163 // When both |characters| and |charactersIgnoringModifiers| are ascii, |
| 164 // return the first character of |characters|, if... |
| 165 if (isascii([eventString characterAtIndex:0]) && |
| 166 isascii([characters characterAtIndex:0])) { |
| 167 // |characters| is an alphabet (mainly for dvorak-qwerty layout), or |
| 168 if (isalpha([characters characterAtIndex:0])) |
| 169 return [characters characterAtIndex:0]; |
| 170 // opt/alt modifier is set (e.g. on german layout we want '{' for opt-8). |
| 171 if ([event modifierFlags] & NSAlternateKeyMask) |
| 172 return [characters characterAtIndex:0]; |
| 173 } |
| 174 |
| 175 return [eventString characterAtIndex:0]; |
| 176 } |
| OLD | NEW |