Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ui / base / accelerators / accelerator.cc
bloba05e18534136e16094370224baf12cd7a8d8f6fc
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 = GetCharacterFromKeyCode(key_code_, false);
216 if (c != 0)
217 shortcut +=
218 static_cast<base::string16::value_type>(base::ToUpperASCII(c));
219 #endif
220 } else {
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
227 // required.
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);
236 if (IsShiftDown())
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
241 // more information.
242 if (IsCtrlDown())
243 shortcut = l10n_util::GetStringFUTF16(IDS_APP_CONTROL_MODIFIER, shortcut);
244 else if (IsAltDown())
245 shortcut = l10n_util::GetStringFUTF16(IDS_APP_ALT_MODIFIER, shortcut);
247 if (IsCmdDown()) {
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);
252 #else
253 NOTREACHED();
254 #endif
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
264 // problem).
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
271 // string.
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);
287 return shortcut;
290 } // namespace ui