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