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