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