• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/strings/string_util.h"
16 #include "base/strings/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/events/keycodes/keyboard_code_conversion.h"
22 #endif
23 
24 namespace ui {
25 
Accelerator()26 Accelerator::Accelerator()
27     : key_code_(ui::VKEY_UNKNOWN),
28       type_(ui::ET_KEY_PRESSED),
29       modifiers_(0) {
30 }
31 
Accelerator(KeyboardCode keycode,int modifiers)32 Accelerator::Accelerator(KeyboardCode keycode, int modifiers)
33     : key_code_(keycode),
34       type_(ui::ET_KEY_PRESSED),
35       modifiers_(modifiers) {
36 }
37 
Accelerator(const Accelerator & accelerator)38 Accelerator::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 
~Accelerator()46 Accelerator::~Accelerator() {
47 }
48 
operator =(const Accelerator & accelerator)49 Accelerator& 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 
operator <(const Accelerator & rhs) const62 bool 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 
operator ==(const Accelerator & rhs) const70 bool 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 
operator !=(const Accelerator & rhs) const81 bool Accelerator::operator !=(const Accelerator& rhs) const {
82   return !(*this == rhs);
83 }
84 
IsShiftDown() const85 bool Accelerator::IsShiftDown() const {
86   return (modifiers_ & EF_SHIFT_DOWN) != 0;
87 }
88 
IsCtrlDown() const89 bool Accelerator::IsCtrlDown() const {
90   return (modifiers_ & EF_CONTROL_DOWN) != 0;
91 }
92 
IsAltDown() const93 bool Accelerator::IsAltDown() const {
94   return (modifiers_ & EF_ALT_DOWN) != 0;
95 }
96 
IsCmdDown() const97 bool Accelerator::IsCmdDown() const {
98   return (modifiers_ & EF_COMMAND_DOWN) != 0;
99 }
100 
GetShortcutText() const101 base::string16 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     case ui::VKEY_MEDIA_NEXT_TRACK:
159       string_id = IDS_APP_MEDIA_NEXT_TRACK_KEY;
160       break;
161     case ui::VKEY_MEDIA_PLAY_PAUSE:
162       string_id = IDS_APP_MEDIA_PLAY_PAUSE_KEY;
163       break;
164     case ui::VKEY_MEDIA_PREV_TRACK:
165       string_id = IDS_APP_MEDIA_PREV_TRACK_KEY;
166       break;
167     case ui::VKEY_MEDIA_STOP:
168       string_id = IDS_APP_MEDIA_STOP_KEY;
169       break;
170     default:
171       break;
172   }
173 
174   base::string16 shortcut;
175   if (!string_id) {
176 #if defined(OS_WIN)
177     // Our fallback is to try translate the key code to a regular character
178     // unless it is one of digits (VK_0 to VK_9). Some keyboard
179     // layouts have characters other than digits assigned in
180     // an unshifted mode (e.g. French AZERY layout has 'a with grave
181     // accent' for '0'). For display in the menu (e.g. Ctrl-0 for the
182     // default zoom level), we leave VK_[0-9] alone without translation.
183     wchar_t key;
184     if (key_code_ >= '0' && key_code_ <= '9')
185       key = key_code_;
186     else
187       key = LOWORD(::MapVirtualKeyW(key_code_, MAPVK_VK_TO_CHAR));
188     shortcut += key;
189 #elif defined(USE_AURA) || defined(OS_MACOSX)
190     const uint16 c = GetCharacterFromKeyCode(key_code_, false);
191     if (c != 0)
192       shortcut +=
193           static_cast<base::string16::value_type>(base::ToUpperASCII(c));
194 #elif defined(TOOLKIT_GTK)
195     const gchar* name = NULL;
196     switch (key_code_) {
197       case ui::VKEY_OEM_2:
198         name = static_cast<const gchar*>("/");
199         break;
200       default:
201         name = gdk_keyval_name(gdk_keyval_to_lower(key_code_));
202         break;
203     }
204     if (name) {
205       if (name[0] != 0 && name[1] == 0)
206         shortcut +=
207             static_cast<base::string16::value_type>(g_ascii_toupper(name[0]));
208       else
209         shortcut += UTF8ToUTF16(name);
210     }
211 #endif
212   } else {
213     shortcut = l10n_util::GetStringUTF16(string_id);
214   }
215 
216   // Checking whether the character used for the accelerator is alphanumeric.
217   // If it is not, then we need to adjust the string later on if the locale is
218   // right-to-left. See below for more information of why such adjustment is
219   // required.
220   base::string16 shortcut_rtl;
221   bool adjust_shortcut_for_rtl = false;
222   if (base::i18n::IsRTL() && shortcut.length() == 1 &&
223       !IsAsciiAlpha(shortcut[0]) && !IsAsciiDigit(shortcut[0])) {
224     adjust_shortcut_for_rtl = true;
225     shortcut_rtl.assign(shortcut);
226   }
227 
228   if (IsShiftDown())
229     shortcut = l10n_util::GetStringFUTF16(IDS_APP_SHIFT_MODIFIER, shortcut);
230 
231   // Note that we use 'else-if' in order to avoid using Ctrl+Alt as a shortcut.
232   // See http://blogs.msdn.com/oldnewthing/archive/2004/03/29/101121.aspx for
233   // more information.
234   if (IsCtrlDown())
235     shortcut = l10n_util::GetStringFUTF16(IDS_APP_CONTROL_MODIFIER, shortcut);
236   else if (IsAltDown())
237     shortcut = l10n_util::GetStringFUTF16(IDS_APP_ALT_MODIFIER, shortcut);
238 
239   if (IsCmdDown())
240     shortcut = l10n_util::GetStringFUTF16(IDS_APP_COMMAND_MODIFIER, shortcut);
241 
242   // For some reason, menus in Windows ignore standard Unicode directionality
243   // marks (such as LRE, PDF, etc.). On RTL locales, we use RTL menus and
244   // therefore any text we draw for the menu items is drawn in an RTL context.
245   // Thus, the text "Ctrl++" (which we currently use for the Zoom In option)
246   // appears as "++Ctrl" in RTL because the Unicode BiDi algorithm puts
247   // punctuations on the left when the context is right-to-left. Shortcuts that
248   // do not end with a punctuation mark (such as "Ctrl+H" do not have this
249   // problem).
250   //
251   // The only way to solve this problem is to adjust the string if the locale
252   // is RTL so that it is drawn correctly in an RTL context. Instead of
253   // returning "Ctrl++" in the above example, we return "++Ctrl". This will
254   // cause the text to appear as "Ctrl++" when Windows draws the string in an
255   // RTL context because the punctuation no longer appears at the end of the
256   // string.
257   //
258   // TODO(idana) bug# 1232732: this hack can be avoided if instead of using
259   // views::Menu we use views::MenuItemView because the latter is a View
260   // subclass and therefore it supports marking text as RTL or LTR using
261   // standard Unicode directionality marks.
262   if (adjust_shortcut_for_rtl) {
263     int key_length = static_cast<int>(shortcut_rtl.length());
264     DCHECK_GT(key_length, 0);
265     shortcut_rtl.append(ASCIIToUTF16("+"));
266 
267     // Subtracting the size of the shortcut key and 1 for the '+' sign.
268     shortcut_rtl.append(shortcut, 0, shortcut.length() - key_length - 1);
269     shortcut.swap(shortcut_rtl);
270   }
271 
272   return shortcut;
273 }
274 
275 }  // namespace ui
276