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"
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"
27 const int kEventFlagsMask
= ui::EF_SHIFT_DOWN
| ui::EF_CONTROL_DOWN
|
28 ui::EF_ALT_DOWN
| ui::EF_COMMAND_DOWN
;
32 Accelerator::Accelerator()
33 : key_code_(ui::VKEY_UNKNOWN
),
34 type_(ui::ET_KEY_PRESSED
),
39 Accelerator::Accelerator(KeyboardCode keycode
, int modifiers
)
41 type_(ui::ET_KEY_PRESSED
),
42 modifiers_(modifiers
),
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();
74 platform_accelerator_
.reset();
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_
))
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 {
123 base::string16
Accelerator::GetShortcutText() const {
127 string_id
= IDS_APP_TAB_KEY
;
129 case ui::VKEY_RETURN
:
130 string_id
= IDS_APP_ENTER_KEY
;
132 case ui::VKEY_ESCAPE
:
133 string_id
= IDS_APP_ESC_KEY
;
136 string_id
= IDS_APP_SPACE_KEY
;
139 string_id
= IDS_APP_PAGEUP_KEY
;
142 string_id
= IDS_APP_PAGEDOWN_KEY
;
145 string_id
= IDS_APP_END_KEY
;
148 string_id
= IDS_APP_HOME_KEY
;
150 case ui::VKEY_INSERT
:
151 string_id
= IDS_APP_INSERT_KEY
;
153 case ui::VKEY_DELETE
:
154 string_id
= IDS_APP_DELETE_KEY
;
157 string_id
= IDS_APP_LEFT_ARROW_KEY
;
160 string_id
= IDS_APP_RIGHT_ARROW_KEY
;
163 string_id
= IDS_APP_UP_ARROW_KEY
;
166 string_id
= IDS_APP_DOWN_ARROW_KEY
;
169 string_id
= IDS_APP_BACKSPACE_KEY
;
172 string_id
= IDS_APP_F1_KEY
;
175 string_id
= IDS_APP_F11_KEY
;
177 case ui::VKEY_OEM_COMMA
:
178 string_id
= IDS_APP_COMMA_KEY
;
180 case ui::VKEY_OEM_PERIOD
:
181 string_id
= IDS_APP_PERIOD_KEY
;
183 case ui::VKEY_MEDIA_NEXT_TRACK
:
184 string_id
= IDS_APP_MEDIA_NEXT_TRACK_KEY
;
186 case ui::VKEY_MEDIA_PLAY_PAUSE
:
187 string_id
= IDS_APP_MEDIA_PLAY_PAUSE_KEY
;
189 case ui::VKEY_MEDIA_PREV_TRACK
:
190 string_id
= IDS_APP_MEDIA_PREV_TRACK_KEY
;
192 case ui::VKEY_MEDIA_STOP
:
193 string_id
= IDS_APP_MEDIA_STOP_KEY
;
199 base::string16 shortcut
;
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.
209 if (key_code_
>= '0' && key_code_
<= '9')
210 key
= static_cast<wchar_t>(key_code_
);
212 key
= LOWORD(::MapVirtualKeyW(key_code_
, MAPVK_VK_TO_CHAR
));
214 #elif defined(USE_AURA) || defined(OS_MACOSX)
215 const uint16 c
= GetCharacterFromKeyCode(key_code_
, false);
218 static_cast<base::string16::value_type
>(base::ToUpperASCII(c
));
221 shortcut
= l10n_util::GetStringUTF16(string_id
);
224 // Checking whether the character used for the accelerator is alphanumeric.
225 // If it is not, then we need to adjust the string later on if the locale is
226 // right-to-left. See below for more information of why such adjustment is
228 base::string16 shortcut_rtl
;
229 bool adjust_shortcut_for_rtl
= false;
230 if (base::i18n::IsRTL() && shortcut
.length() == 1 &&
231 !base::IsAsciiAlpha(shortcut
[0]) && !base::IsAsciiDigit(shortcut
[0])) {
232 adjust_shortcut_for_rtl
= true;
233 shortcut_rtl
.assign(shortcut
);
237 shortcut
= l10n_util::GetStringFUTF16(IDS_APP_SHIFT_MODIFIER
, shortcut
);
239 // Note that we use 'else-if' in order to avoid using Ctrl+Alt as a shortcut.
240 // See http://blogs.msdn.com/oldnewthing/archive/2004/03/29/101121.aspx for
243 shortcut
= l10n_util::GetStringFUTF16(IDS_APP_CONTROL_MODIFIER
, shortcut
);
244 else if (IsAltDown())
245 shortcut
= l10n_util::GetStringFUTF16(IDS_APP_ALT_MODIFIER
, shortcut
);
248 #if defined(OS_MACOSX)
249 shortcut
= l10n_util::GetStringFUTF16(IDS_APP_COMMAND_MODIFIER
, shortcut
);
250 #elif defined(OS_CHROMEOS)
251 shortcut
= l10n_util::GetStringFUTF16(IDS_APP_SEARCH_MODIFIER
, shortcut
);
257 // For some reason, menus in Windows ignore standard Unicode directionality
258 // marks (such as LRE, PDF, etc.). On RTL locales, we use RTL menus and
259 // therefore any text we draw for the menu items is drawn in an RTL context.
260 // Thus, the text "Ctrl++" (which we currently use for the Zoom In option)
261 // appears as "++Ctrl" in RTL because the Unicode BiDi algorithm puts
262 // punctuations on the left when the context is right-to-left. Shortcuts that
263 // do not end with a punctuation mark (such as "Ctrl+H" do not have this
266 // The only way to solve this problem is to adjust the string if the locale
267 // is RTL so that it is drawn correctly in an RTL context. Instead of
268 // returning "Ctrl++" in the above example, we return "++Ctrl". This will
269 // cause the text to appear as "Ctrl++" when Windows draws the string in an
270 // RTL context because the punctuation no longer appears at the end of the
273 // TODO(idana) bug# 1232732: this hack can be avoided if instead of using
274 // views::Menu we use views::MenuItemView because the latter is a View
275 // subclass and therefore it supports marking text as RTL or LTR using
276 // standard Unicode directionality marks.
277 if (adjust_shortcut_for_rtl
) {
278 int key_length
= static_cast<int>(shortcut_rtl
.length());
279 DCHECK_GT(key_length
, 0);
280 shortcut_rtl
.append(base::ASCIIToUTF16("+"));
282 // Subtracting the size of the shortcut key and 1 for the '+' sign.
283 shortcut_rtl
.append(shortcut
, 0, shortcut
.length() - key_length
- 1);
284 shortcut
.swap(shortcut_rtl
);