Add ICU message format support
[chromium-blink-merge.git] / ash / sticky_keys / sticky_keys_controller.cc
blobc014d571a5ca9f0401c6bd526331caabd82c7c14
1 // Copyright 2013 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 "ash/sticky_keys/sticky_keys_controller.h"
7 #include "ash/sticky_keys/sticky_keys_overlay.h"
8 #include "base/basictypes.h"
9 #include "base/debug/stack_trace.h"
10 #include "ui/aura/window.h"
11 #include "ui/aura/window_tracker.h"
12 #include "ui/aura/window_tree_host.h"
13 #include "ui/events/event.h"
14 #include "ui/events/event_processor.h"
15 #include "ui/events/keycodes/keyboard_code_conversion.h"
17 namespace ash {
19 namespace {
21 // Returns true if the type of mouse event should be modified by sticky keys.
22 bool ShouldModifyMouseEvent(const ui::MouseEvent& event) {
23 ui::EventType type = event.type();
24 return type == ui::ET_MOUSE_PRESSED || type == ui::ET_MOUSE_RELEASED ||
25 type == ui::ET_MOUSEWHEEL;
28 // Handle the common tail of event rewriting.
29 ui::EventRewriteStatus RewriteUpdate(bool consumed,
30 bool released,
31 int mod_down_flags,
32 int* flags) {
33 int changed_down_flags = mod_down_flags & ~*flags;
34 *flags |= mod_down_flags;
35 if (consumed)
36 return ui::EVENT_REWRITE_DISCARD;
37 if (released)
38 return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
39 if (changed_down_flags)
40 return ui::EVENT_REWRITE_REWRITTEN;
41 return ui::EVENT_REWRITE_CONTINUE;
44 } // namespace
46 ///////////////////////////////////////////////////////////////////////////////
47 // StickyKeys
48 StickyKeysController::StickyKeysController()
49 : enabled_(false),
50 mod3_enabled_(false),
51 altgr_enabled_(false) {
54 StickyKeysController::~StickyKeysController() {
57 void StickyKeysController::Enable(bool enabled) {
58 if (enabled_ != enabled) {
59 enabled_ = enabled;
61 // Reset key handlers when activating sticky keys to ensure all
62 // the handlers' states are reset.
63 if (enabled_) {
64 shift_sticky_key_.reset(new StickyKeysHandler(ui::EF_SHIFT_DOWN));
65 alt_sticky_key_.reset(new StickyKeysHandler(ui::EF_ALT_DOWN));
66 altgr_sticky_key_.reset(new StickyKeysHandler(ui::EF_ALTGR_DOWN));
67 ctrl_sticky_key_.reset(new StickyKeysHandler(ui::EF_CONTROL_DOWN));
68 mod3_sticky_key_.reset(new StickyKeysHandler(ui::EF_MOD3_DOWN));
69 search_sticky_key_.reset(new StickyKeysHandler(ui::EF_COMMAND_DOWN));
71 overlay_.reset(new StickyKeysOverlay());
72 overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
73 overlay_->SetModifierVisible(ui::EF_MOD3_DOWN, mod3_enabled_);
74 } else if (overlay_) {
75 overlay_.reset();
80 void StickyKeysController::SetModifiersEnabled(bool mod3_enabled,
81 bool altgr_enabled) {
82 mod3_enabled_ = mod3_enabled;
83 altgr_enabled_ = altgr_enabled;
84 if (overlay_) {
85 overlay_->SetModifierVisible(ui::EF_ALTGR_DOWN, altgr_enabled_);
86 overlay_->SetModifierVisible(ui::EF_MOD3_DOWN, mod3_enabled_);
90 bool StickyKeysController::HandleKeyEvent(const ui::KeyEvent& event,
91 ui::KeyboardCode key_code,
92 int* mod_down_flags,
93 bool* released) {
94 return shift_sticky_key_->HandleKeyEvent(
95 event, key_code, mod_down_flags, released) ||
96 alt_sticky_key_->HandleKeyEvent(
97 event, key_code, mod_down_flags, released) ||
98 altgr_sticky_key_->HandleKeyEvent(
99 event, key_code, mod_down_flags, released) ||
100 ctrl_sticky_key_->HandleKeyEvent(
101 event, key_code, mod_down_flags, released) ||
102 mod3_sticky_key_->HandleKeyEvent(
103 event, key_code, mod_down_flags, released) ||
104 search_sticky_key_->HandleKeyEvent(
105 event, key_code, mod_down_flags, released);
108 bool StickyKeysController::HandleMouseEvent(const ui::MouseEvent& event,
109 int* mod_down_flags,
110 bool* released) {
111 return shift_sticky_key_->HandleMouseEvent(
112 event, mod_down_flags, released) ||
113 alt_sticky_key_->HandleMouseEvent(
114 event, mod_down_flags, released) ||
115 altgr_sticky_key_->HandleMouseEvent(
116 event, mod_down_flags, released) ||
117 ctrl_sticky_key_->HandleMouseEvent(
118 event, mod_down_flags, released) ||
119 mod3_sticky_key_->HandleMouseEvent(
120 event, mod_down_flags, released);
123 bool StickyKeysController::HandleScrollEvent(const ui::ScrollEvent& event,
124 int* mod_down_flags,
125 bool* released) {
126 return shift_sticky_key_->HandleScrollEvent(
127 event, mod_down_flags, released) ||
128 alt_sticky_key_->HandleScrollEvent(
129 event, mod_down_flags, released) ||
130 altgr_sticky_key_->HandleScrollEvent(
131 event, mod_down_flags, released) ||
132 ctrl_sticky_key_->HandleScrollEvent(
133 event, mod_down_flags, released) ||
134 mod3_sticky_key_->HandleScrollEvent(
135 event, mod_down_flags, released);
138 ui::EventRewriteStatus StickyKeysController::RewriteKeyEvent(
139 const ui::KeyEvent& event,
140 ui::KeyboardCode key_code,
141 int* flags) {
142 if (!enabled_)
143 return ui::EVENT_REWRITE_CONTINUE;
144 int mod_down_flags = 0;
145 bool released = false;
146 bool consumed = HandleKeyEvent(event, key_code, &mod_down_flags, &released);
147 UpdateOverlay();
148 return RewriteUpdate(consumed, released, mod_down_flags, flags);
151 ui::EventRewriteStatus StickyKeysController::RewriteMouseEvent(
152 const ui::MouseEvent& event,
153 int* flags) {
154 if (!enabled_)
155 return ui::EVENT_REWRITE_CONTINUE;
156 int mod_down_flags = 0;
157 bool released = false;
158 bool consumed = HandleMouseEvent(event, &mod_down_flags, &released);
159 UpdateOverlay();
160 return RewriteUpdate(consumed, released, mod_down_flags, flags);
163 ui::EventRewriteStatus StickyKeysController::RewriteScrollEvent(
164 const ui::ScrollEvent& event,
165 int* flags) {
166 if (!enabled_)
167 return ui::EVENT_REWRITE_CONTINUE;
168 int mod_down_flags = 0;
169 bool released = false;
170 bool consumed = HandleScrollEvent(event, &mod_down_flags, &released);
171 UpdateOverlay();
172 return RewriteUpdate(consumed, released, mod_down_flags, flags);
175 ui::EventRewriteStatus StickyKeysController::NextDispatchEvent(
176 scoped_ptr<ui::Event>* new_event) {
177 DCHECK(new_event);
178 new_event->reset();
179 int remaining = shift_sticky_key_->GetModifierUpEvent(new_event) +
180 alt_sticky_key_->GetModifierUpEvent(new_event) +
181 altgr_sticky_key_->GetModifierUpEvent(new_event) +
182 ctrl_sticky_key_->GetModifierUpEvent(new_event) +
183 mod3_sticky_key_->GetModifierUpEvent(new_event) +
184 search_sticky_key_->GetModifierUpEvent(new_event);
185 if (!new_event)
186 return ui::EVENT_REWRITE_CONTINUE;
187 if (remaining)
188 return ui::EVENT_REWRITE_DISPATCH_ANOTHER;
189 return ui::EVENT_REWRITE_REWRITTEN;
192 void StickyKeysController::UpdateOverlay() {
193 overlay_->SetModifierKeyState(
194 ui::EF_SHIFT_DOWN, shift_sticky_key_->current_state());
195 overlay_->SetModifierKeyState(
196 ui::EF_CONTROL_DOWN, ctrl_sticky_key_->current_state());
197 overlay_->SetModifierKeyState(
198 ui::EF_ALT_DOWN, alt_sticky_key_->current_state());
199 overlay_->SetModifierKeyState(
200 ui::EF_COMMAND_DOWN, search_sticky_key_->current_state());
201 overlay_->SetModifierKeyState(
202 ui::EF_ALTGR_DOWN, altgr_sticky_key_->current_state());
203 overlay_->SetModifierKeyState(
204 ui::EF_MOD3_DOWN, mod3_sticky_key_->current_state());
206 bool key_in_use =
207 shift_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
208 alt_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
209 altgr_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
210 ctrl_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
211 search_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED ||
212 mod3_sticky_key_->current_state() != STICKY_KEY_STATE_DISABLED;
214 overlay_->Show(enabled_ && key_in_use);
217 StickyKeysOverlay* StickyKeysController::GetOverlayForTest() {
218 return overlay_.get();
221 ///////////////////////////////////////////////////////////////////////////////
222 // StickyKeysHandler
223 StickyKeysHandler::StickyKeysHandler(ui::EventFlags modifier_flag)
224 : modifier_flag_(modifier_flag),
225 current_state_(STICKY_KEY_STATE_DISABLED),
226 preparing_to_enable_(false),
227 scroll_delta_(0) {
230 StickyKeysHandler::~StickyKeysHandler() {
233 bool StickyKeysHandler::HandleKeyEvent(const ui::KeyEvent& event,
234 ui::KeyboardCode key_code,
235 int* mod_down_flags,
236 bool* released) {
237 switch (current_state_) {
238 case STICKY_KEY_STATE_DISABLED:
239 return HandleDisabledState(event, key_code);
240 case STICKY_KEY_STATE_ENABLED:
241 return HandleEnabledState(event, key_code, mod_down_flags, released);
242 case STICKY_KEY_STATE_LOCKED:
243 return HandleLockedState(event, key_code, mod_down_flags, released);
245 NOTREACHED();
246 return false;
249 bool StickyKeysHandler::HandleMouseEvent(
250 const ui::MouseEvent& event,
251 int* mod_down_flags,
252 bool* released) {
253 if (ShouldModifyMouseEvent(event))
254 preparing_to_enable_ = false;
256 if (current_state_ == STICKY_KEY_STATE_DISABLED ||
257 !ShouldModifyMouseEvent(event)) {
258 return false;
260 DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
261 current_state_ == STICKY_KEY_STATE_LOCKED);
263 *mod_down_flags |= modifier_flag_;
264 // Only disable on the mouse released event in normal, non-locked mode.
265 if (current_state_ == STICKY_KEY_STATE_ENABLED &&
266 event.type() != ui::ET_MOUSE_PRESSED) {
267 current_state_ = STICKY_KEY_STATE_DISABLED;
268 *released = true;
269 return false;
272 return false;
275 bool StickyKeysHandler::HandleScrollEvent(
276 const ui::ScrollEvent& event,
277 int* mod_down_flags,
278 bool* released) {
279 preparing_to_enable_ = false;
280 if (current_state_ == STICKY_KEY_STATE_DISABLED)
281 return false;
282 DCHECK(current_state_ == STICKY_KEY_STATE_ENABLED ||
283 current_state_ == STICKY_KEY_STATE_LOCKED);
285 // We detect a direction change if the current |scroll_delta_| is assigned
286 // and the offset of the current scroll event has the opposing sign.
287 bool direction_changed = false;
288 if (current_state_ == STICKY_KEY_STATE_ENABLED &&
289 event.type() == ui::ET_SCROLL) {
290 int offset = event.y_offset();
291 if (scroll_delta_)
292 direction_changed = offset * scroll_delta_ <= 0;
293 scroll_delta_ = offset;
296 if (!direction_changed)
297 *mod_down_flags |= modifier_flag_;
299 // We want to modify all the scroll events in the scroll sequence, which ends
300 // with a fling start event. We also stop when the scroll sequence changes
301 // direction.
302 if (current_state_ == STICKY_KEY_STATE_ENABLED &&
303 (event.type() == ui::ET_SCROLL_FLING_START || direction_changed)) {
304 current_state_ = STICKY_KEY_STATE_DISABLED;
305 scroll_delta_ = 0;
306 *released = true;
307 return false;
310 return false;
313 int StickyKeysHandler::GetModifierUpEvent(scoped_ptr<ui::Event>* new_event) {
314 if (current_state_ != STICKY_KEY_STATE_DISABLED || !modifier_up_event_)
315 return 0;
316 DCHECK(new_event);
317 if (*new_event)
318 return 1;
319 new_event->reset(modifier_up_event_.release());
320 return 0;
323 StickyKeysHandler::KeyEventType StickyKeysHandler::TranslateKeyEvent(
324 ui::EventType type,
325 ui::KeyboardCode key_code) {
326 bool is_target_key = false;
327 if (key_code == ui::VKEY_SHIFT ||
328 key_code == ui::VKEY_LSHIFT ||
329 key_code == ui::VKEY_RSHIFT) {
330 is_target_key = (modifier_flag_ == ui::EF_SHIFT_DOWN);
331 } else if (key_code == ui::VKEY_CONTROL ||
332 key_code == ui::VKEY_LCONTROL ||
333 key_code == ui::VKEY_RCONTROL) {
334 is_target_key = (modifier_flag_ == ui::EF_CONTROL_DOWN);
335 } else if (key_code == ui::VKEY_MENU ||
336 key_code == ui::VKEY_LMENU ||
337 key_code == ui::VKEY_RMENU) {
338 is_target_key = (modifier_flag_ == ui::EF_ALT_DOWN);
339 } else if (key_code == ui::VKEY_ALTGR) {
340 is_target_key = (modifier_flag_ == ui::EF_ALTGR_DOWN);
341 } else if (key_code == ui::VKEY_OEM_8) {
342 is_target_key = (modifier_flag_ == ui::EF_MOD3_DOWN);
343 } else if (key_code == ui::VKEY_LWIN) {
344 is_target_key = (modifier_flag_ == ui::EF_COMMAND_DOWN);
345 } else {
346 return type == ui::ET_KEY_PRESSED ?
347 NORMAL_KEY_DOWN : NORMAL_KEY_UP;
350 if (is_target_key) {
351 return type == ui::ET_KEY_PRESSED ?
352 TARGET_MODIFIER_DOWN : TARGET_MODIFIER_UP;
354 return type == ui::ET_KEY_PRESSED ?
355 OTHER_MODIFIER_DOWN : OTHER_MODIFIER_UP;
358 bool StickyKeysHandler::HandleDisabledState(const ui::KeyEvent& event,
359 ui::KeyboardCode key_code) {
360 switch (TranslateKeyEvent(event.type(), key_code)) {
361 case TARGET_MODIFIER_UP:
362 if (preparing_to_enable_) {
363 preparing_to_enable_ = false;
364 scroll_delta_ = 0;
365 current_state_ = STICKY_KEY_STATE_ENABLED;
366 modifier_up_event_.reset(new ui::KeyEvent(event));
367 return true;
369 return false;
370 case TARGET_MODIFIER_DOWN:
371 preparing_to_enable_ = true;
372 return false;
373 case NORMAL_KEY_DOWN:
374 preparing_to_enable_ = false;
375 return false;
376 case NORMAL_KEY_UP:
377 case OTHER_MODIFIER_DOWN:
378 case OTHER_MODIFIER_UP:
379 return false;
381 NOTREACHED();
382 return false;
385 bool StickyKeysHandler::HandleEnabledState(const ui::KeyEvent& event,
386 ui::KeyboardCode key_code,
387 int* mod_down_flags,
388 bool* released) {
389 switch (TranslateKeyEvent(event.type(), key_code)) {
390 case NORMAL_KEY_UP:
391 case TARGET_MODIFIER_DOWN:
392 return false;
393 case TARGET_MODIFIER_UP:
394 current_state_ = STICKY_KEY_STATE_LOCKED;
395 modifier_up_event_.reset();
396 return true;
397 case NORMAL_KEY_DOWN: {
398 current_state_ = STICKY_KEY_STATE_DISABLED;
399 *mod_down_flags |= modifier_flag_;
400 *released = true;
401 return false;
403 case OTHER_MODIFIER_DOWN:
404 case OTHER_MODIFIER_UP:
405 return false;
407 NOTREACHED();
408 return false;
411 bool StickyKeysHandler::HandleLockedState(const ui::KeyEvent& event,
412 ui::KeyboardCode key_code,
413 int* mod_down_flags,
414 bool* released) {
415 switch (TranslateKeyEvent(event.type(), key_code)) {
416 case TARGET_MODIFIER_DOWN:
417 return true;
418 case TARGET_MODIFIER_UP:
419 current_state_ = STICKY_KEY_STATE_DISABLED;
420 return false;
421 case NORMAL_KEY_DOWN:
422 case NORMAL_KEY_UP:
423 *mod_down_flags |= modifier_flag_;
424 return false;
425 case OTHER_MODIFIER_DOWN:
426 case OTHER_MODIFIER_UP:
427 return false;
429 NOTREACHED();
430 return false;
433 } // namespace ash