Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / ash / autoclick / autoclick_controller.cc
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.
4
5 #include "ash/autoclick/autoclick_controller.h"
6
7 #include "ash/shell.h"
8 #include "ash/wm/coordinate_conversion.h"
9 #include "base/timer/timer.h"
10 #include "ui/aura/env.h"
11 #include "ui/aura/root_window.h"
12 #include "ui/events/event.h"
13 #include "ui/events/event_constants.h"
14 #include "ui/events/event_handler.h"
15 #include "ui/gfx/point.h"
16 #include "ui/gfx/vector2d.h"
17
18 namespace ash {
19
20 namespace {
21
22 // The threshold of mouse movement measured in DIP that will
23 // initiate a new autoclick.
24 const int kMovementThreshold = 20;
25
26 bool IsModifierKey(ui::KeyboardCode key_code) {
27   return key_code == ui::VKEY_SHIFT ||
28       key_code == ui::VKEY_LSHIFT ||
29       key_code == ui::VKEY_CONTROL ||
30       key_code == ui::VKEY_LCONTROL ||
31       key_code == ui::VKEY_RCONTROL ||
32       key_code == ui::VKEY_MENU ||
33       key_code == ui::VKEY_LMENU ||
34       key_code == ui::VKEY_RMENU;
35 }
36
37 }  // namespace
38
39 // static.
40 const int AutoclickController::kDefaultAutoclickDelayMs = 400;
41
42 class AutoclickControllerImpl : public AutoclickController,
43                                 public ui::EventHandler {
44  public:
45   AutoclickControllerImpl();
46   virtual ~AutoclickControllerImpl();
47
48  private:
49   // AutoclickController overrides:
50   virtual void SetEnabled(bool enabled) OVERRIDE;
51   virtual bool IsEnabled() const OVERRIDE;
52   virtual void SetAutoclickDelay(int delay_ms) OVERRIDE;
53   virtual int GetAutoclickDelay() const OVERRIDE;
54
55   // ui::EventHandler overrides:
56   virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
57   virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
58   virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
59   virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
60   virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
61
62   void InitClickTimer();
63
64   void DoAutoclick();
65
66   bool enabled_;
67   int delay_ms_;
68   int mouse_event_flags_;
69   scoped_ptr<base::Timer> autoclick_timer_;
70   // The position in screen coordinates used to determine
71   // the distance the mouse has moved.
72   gfx::Point anchor_location_;
73
74   DISALLOW_COPY_AND_ASSIGN(AutoclickControllerImpl);
75 };
76
77
78 AutoclickControllerImpl::AutoclickControllerImpl()
79     : enabled_(false),
80       delay_ms_(kDefaultAutoclickDelayMs),
81       mouse_event_flags_(ui::EF_NONE),
82       anchor_location_(-kMovementThreshold, -kMovementThreshold) {
83   InitClickTimer();
84 }
85
86 AutoclickControllerImpl::~AutoclickControllerImpl() {
87 }
88
89 void AutoclickControllerImpl::SetEnabled(bool enabled) {
90   if (enabled_ == enabled)
91     return;
92   enabled_ = enabled;
93
94   if (enabled_) {
95     Shell::GetInstance()->AddPreTargetHandler(this);
96     autoclick_timer_->Stop();
97   } else {
98     Shell::GetInstance()->RemovePreTargetHandler(this);
99   }
100 }
101
102 bool AutoclickControllerImpl::IsEnabled() const {
103   return enabled_;
104 }
105
106 void AutoclickControllerImpl::SetAutoclickDelay(int delay_ms) {
107   delay_ms_ = delay_ms;
108   InitClickTimer();
109 }
110
111 int AutoclickControllerImpl::GetAutoclickDelay() const {
112   return delay_ms_;
113 }
114
115 void AutoclickControllerImpl::InitClickTimer() {
116   autoclick_timer_.reset(new base::Timer(
117       FROM_HERE,
118       base::TimeDelta::FromMilliseconds(delay_ms_),
119       base::Bind(&AutoclickControllerImpl::DoAutoclick,
120                  base::Unretained(this)),
121       false));
122 }
123
124 void AutoclickControllerImpl::OnMouseEvent(ui::MouseEvent* event) {
125   if (event->type() == ui::ET_MOUSE_MOVED &&
126       !(event->flags() & ui::EF_IS_SYNTHESIZED)) {
127     mouse_event_flags_ = event->flags();
128
129     gfx::Point mouse_location = event->root_location();
130     ash::wm::ConvertPointToScreen(
131         wm::GetRootWindowAt(mouse_location),
132         &mouse_location);
133
134     // The distance between the mouse location and the anchor location
135     // must exceed a certain threshold to initiate a new autoclick countdown.
136     // This ensures that mouse jitter caused by poor motor control does not
137     // 1. initiate an unwanted autoclick from rest
138     // 2. prevent the autoclick from ever occuring when the mouse
139     //    arrives at the target.
140     gfx::Vector2d delta = mouse_location - anchor_location_;
141     if (delta.LengthSquared() >= kMovementThreshold * kMovementThreshold) {
142       anchor_location_ = event->root_location();
143       autoclick_timer_->Reset();
144     }
145   } else if (event->type() == ui::ET_MOUSE_PRESSED) {
146     autoclick_timer_->Stop();
147   } else if (event->type() == ui::ET_MOUSEWHEEL &&
148              autoclick_timer_->IsRunning()) {
149     autoclick_timer_->Reset();
150   }
151 }
152
153 void AutoclickControllerImpl::OnKeyEvent(ui::KeyEvent* event) {
154   int modifier_mask =
155       ui::EF_SHIFT_DOWN |
156       ui::EF_CONTROL_DOWN |
157       ui::EF_ALT_DOWN |
158       ui::EF_COMMAND_DOWN |
159       ui::EF_EXTENDED;
160   int new_modifiers = event->flags() & modifier_mask;
161   mouse_event_flags_ = (mouse_event_flags_ & ~modifier_mask) | new_modifiers;
162
163   if (!IsModifierKey(event->key_code()))
164     autoclick_timer_->Stop();
165 }
166
167 void AutoclickControllerImpl::OnTouchEvent(ui::TouchEvent* event) {
168   autoclick_timer_->Stop();
169 }
170
171 void AutoclickControllerImpl::OnGestureEvent(ui::GestureEvent* event) {
172   autoclick_timer_->Stop();
173 }
174
175 void AutoclickControllerImpl::OnScrollEvent(ui::ScrollEvent* event) {
176   autoclick_timer_->Stop();
177 }
178
179 void AutoclickControllerImpl::DoAutoclick() {
180   gfx::Point screen_location =
181       aura::Env::GetInstance()->last_mouse_location();
182   aura::Window* root_window = wm::GetRootWindowAt(screen_location);
183   DCHECK(root_window) << "Root window not found while attempting autoclick.";
184
185   gfx::Point click_location(screen_location);
186   anchor_location_ = click_location;
187   wm::ConvertPointFromScreen(root_window, &click_location);
188
189   aura::WindowEventDispatcher* dispatcher = root_window->GetDispatcher();
190   dispatcher->host()->ConvertPointToHost(&click_location);
191
192   ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED,
193                              click_location,
194                              click_location,
195                              mouse_event_flags_ | ui::EF_LEFT_MOUSE_BUTTON,
196                              ui::EF_LEFT_MOUSE_BUTTON);
197   ui::MouseEvent release_event(ui::ET_MOUSE_RELEASED,
198                                click_location,
199                                click_location,
200                                mouse_event_flags_ | ui::EF_LEFT_MOUSE_BUTTON,
201                                ui::EF_LEFT_MOUSE_BUTTON);
202
203   ui::EventDispatchDetails details =
204       dispatcher->OnEventFromSource(&press_event);
205   if (!details.dispatcher_destroyed)
206     details = dispatcher->OnEventFromSource(&release_event);
207   if (details.dispatcher_destroyed)
208     return;
209 }
210
211 // static.
212 AutoclickController* AutoclickController::CreateInstance() {
213   return new AutoclickControllerImpl();
214 }
215
216 }  // namespace ash