accelerator.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
1// Copyright (c) 2012 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 "ui/base/accelerators/accelerator.h" 6 7#if defined(OS_WIN) 8#include <windows.h> 9#elif defined(TOOLKIT_GTK) 10#include <gdk/gdk.h> 11#endif 12 13#include "base/i18n/rtl.h" 14#include "base/logging.h" 15#include "base/string_util.h" 16#include "base/utf_string_conversions.h" 17#include "grit/ui_strings.h" 18#include "ui/base/l10n/l10n_util.h" 19 20#if !defined(OS_WIN) && (defined(USE_AURA) || defined(OS_MACOSX)) 21#include "ui/base/keycodes/keyboard_code_conversion.h" 22#endif 23 24namespace ui { 25 26Accelerator::Accelerator() 27 : key_code_(ui::VKEY_UNKNOWN), 28 type_(ui::ET_KEY_PRESSED), 29 modifiers_(0) { 30} 31 32Accelerator::Accelerator(KeyboardCode keycode, int modifiers) 33 : key_code_(keycode), 34 type_(ui::ET_KEY_PRESSED), 35 modifiers_(modifiers) { 36} 37 38Accelerator::Accelerator(const Accelerator& accelerator) { 39 key_code_ = accelerator.key_code_; 40 type_ = accelerator.type_; 41 modifiers_ = accelerator.modifiers_; 42} 43 44Accelerator::~Accelerator() { 45} 46 47Accelerator& Accelerator::operator=(const Accelerator& accelerator) { 48 if (this != &accelerator) { 49 key_code_ = accelerator.key_code_; 50 type_ = accelerator.type_; 51 modifiers_ = accelerator.modifiers_; 52 } 53 return *this; 54} 55 56bool Accelerator::operator <(const Accelerator& rhs) const { 57 if (key_code_ != rhs.key_code_) 58 return key_code_ < rhs.key_code_; 59 if (type_ != rhs.type_) 60 return type_ < rhs.type_; 61 return modifiers_ < rhs.modifiers_; 62} 63 64bool Accelerator::operator ==(const Accelerator& rhs) const { 65 return (key_code_ == rhs.key_code_) && (type_ == rhs.type_) && 66 (modifiers_ == rhs.modifiers_); 67} 68 69bool Accelerator::operator !=(const Accelerator& rhs) const { 70 return !(*this == rhs); 71} 72 73bool Accelerator::IsShiftDown() const { 74 return (modifiers_ & EF_SHIFT_DOWN) != 0; 75} 76 77bool Accelerator::IsCtrlDown() const { 78 return (modifiers_ & EF_CONTROL_DOWN) != 0; 79} 80 81bool Accelerator::IsAltDown() const { 82 return (modifiers_ & EF_ALT_DOWN) != 0; 83} 84 85bool Accelerator::IsCmdDown() const { 86 return (modifiers_ & EF_COMMAND_DOWN) != 0; 87} 88 89string16 Accelerator::GetShortcutText() const { 90 int string_id = 0; 91 switch(key_code_) { 92 case ui::VKEY_TAB: 93 string_id = IDS_APP_TAB_KEY; 94 break; 95 case ui::VKEY_RETURN: 96 string_id = IDS_APP_ENTER_KEY; 97 break; 98 case ui::VKEY_ESCAPE: 99 string_id = IDS_APP_ESC_KEY; 100 break; 101 case ui::VKEY_PRIOR: 102 string_id = IDS_APP_PAGEUP_KEY; 103 break; 104 case ui::VKEY_NEXT: 105 string_id = IDS_APP_PAGEDOWN_KEY; 106 break; 107 case ui::VKEY_END: 108 string_id = IDS_APP_END_KEY; 109 break; 110 case ui::VKEY_HOME: 111 string_id = IDS_APP_HOME_KEY; 112 break; 113 case ui::VKEY_INSERT: 114 string_id = IDS_APP_INSERT_KEY; 115 break; 116 case ui::VKEY_DELETE: 117 string_id = IDS_APP_DELETE_KEY; 118 break; 119 case ui::VKEY_LEFT: 120 string_id = IDS_APP_LEFT_ARROW_KEY; 121 break; 122 case ui::VKEY_RIGHT: 123 string_id = IDS_APP_RIGHT_ARROW_KEY; 124 break; 125 case ui::VKEY_BACK: 126 string_id = IDS_APP_BACKSPACE_KEY; 127 break; 128 case ui::VKEY_F1: 129 string_id = IDS_APP_F1_KEY; 130 break; 131 case ui::VKEY_F11: 132 string_id = IDS_APP_F11_KEY; 133 break; 134 default: 135 break; 136 } 137 138 string16 shortcut; 139 if (!string_id) { 140#if defined(OS_WIN) 141 // Our fallback is to try translate the key code to a regular character 142 // unless it is one of digits (VK_0 to VK_9). Some keyboard 143 // layouts have characters other than digits assigned in 144 // an unshifted mode (e.g. French AZERY layout has 'a with grave 145 // accent' for '0'). For display in the menu (e.g. Ctrl-0 for the 146 // default zoom level), we leave VK_[0-9] alone without translation. 147 wchar_t key; 148 if (key_code_ >= '0' && key_code_ <= '9') 149 key = key_code_; 150 else 151 key = LOWORD(::MapVirtualKeyW(key_code_, MAPVK_VK_TO_CHAR)); 152 shortcut += key; 153#elif defined(USE_AURA) || defined(OS_MACOSX) 154 const uint16 c = GetCharacterFromKeyCode(key_code_, false); 155 if (c != 0) 156 shortcut += static_cast<string16::value_type>(base::ToUpperASCII(c)); 157#elif defined(TOOLKIT_GTK) 158 const gchar* name = NULL; 159 switch (key_code_) { 160 case ui::VKEY_OEM_2: 161 name = static_cast<const gchar*>("/"); 162 break; 163 default: 164 name = gdk_keyval_name(gdk_keyval_to_lower(key_code_)); 165 break; 166 } 167 if (name) { 168 if (name[0] != 0 && name[1] == 0) 169 shortcut += static_cast<string16::value_type>(g_ascii_toupper(name[0])); 170 else 171 shortcut += UTF8ToUTF16(name); 172 } 173#endif 174 } else { 175 shortcut = l10n_util::GetStringUTF16(string_id); 176 } 177 178 // Checking whether the character used for the accelerator is alphanumeric. 179 // If it is not, then we need to adjust the string later on if the locale is 180 // right-to-left. See below for more information of why such adjustment is 181 // required. 182 string16 shortcut_rtl; 183 bool adjust_shortcut_for_rtl = false; 184 if (base::i18n::IsRTL() && shortcut.length() == 1 && 185 !IsAsciiAlpha(shortcut[0]) && !IsAsciiDigit(shortcut[0])) { 186 adjust_shortcut_for_rtl = true; 187 shortcut_rtl.assign(shortcut); 188 } 189 190 if (IsShiftDown()) 191 shortcut = l10n_util::GetStringFUTF16(IDS_APP_SHIFT_MODIFIER, shortcut); 192 193 // Note that we use 'else-if' in order to avoid using Ctrl+Alt as a shortcut. 194 // See http://blogs.msdn.com/oldnewthing/archive/2004/03/29/101121.aspx for 195 // more information. 196 if (IsCtrlDown()) 197 shortcut = l10n_util::GetStringFUTF16(IDS_APP_CONTROL_MODIFIER, shortcut); 198 else if (IsAltDown()) 199 shortcut = l10n_util::GetStringFUTF16(IDS_APP_ALT_MODIFIER, shortcut); 200 201 if (IsCmdDown()) 202 shortcut = l10n_util::GetStringFUTF16(IDS_APP_COMMAND_MODIFIER, shortcut); 203 204 // For some reason, menus in Windows ignore standard Unicode directionality 205 // marks (such as LRE, PDF, etc.). On RTL locales, we use RTL menus and 206 // therefore any text we draw for the menu items is drawn in an RTL context. 207 // Thus, the text "Ctrl++" (which we currently use for the Zoom In option) 208 // appears as "++Ctrl" in RTL because the Unicode BiDi algorithm puts 209 // punctuations on the left when the context is right-to-left. Shortcuts that 210 // do not end with a punctuation mark (such as "Ctrl+H" do not have this 211 // problem). 212 // 213 // The only way to solve this problem is to adjust the string if the locale 214 // is RTL so that it is drawn correctly in an RTL context. Instead of 215 // returning "Ctrl++" in the above example, we return "++Ctrl". This will 216 // cause the text to appear as "Ctrl++" when Windows draws the string in an 217 // RTL context because the punctuation no longer appears at the end of the 218 // string. 219 // 220 // TODO(idana) bug# 1232732: this hack can be avoided if instead of using 221 // views::Menu we use views::MenuItemView because the latter is a View 222 // subclass and therefore it supports marking text as RTL or LTR using 223 // standard Unicode directionality marks. 224 if (adjust_shortcut_for_rtl) { 225 int key_length = static_cast<int>(shortcut_rtl.length()); 226 DCHECK_GT(key_length, 0); 227 shortcut_rtl.append(ASCIIToUTF16("+")); 228 229 // Subtracting the size of the shortcut key and 1 for the '+' sign. 230 shortcut_rtl.append(shortcut, 0, shortcut.length() - key_length - 1); 231 shortcut.swap(shortcut_rtl); 232 } 233 234 return shortcut; 235} 236 237} // namespace ui 238