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