- add sources.
[platform/framework/web/crosswalk.git] / src / ui / views / mouse_watcher.cc
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/views/mouse_watcher.h"
6
7 #include "base/bind.h"
8 #include "base/compiler_specific.h"
9 #include "base/event_types.h"
10 #include "base/memory/weak_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "ui/events/event_constants.h"
13 #include "ui/events/event_utils.h"
14 #include "ui/gfx/screen.h"
15
16 namespace views {
17
18 // Amount of time between when the mouse moves outside the Host's zone and when
19 // the listener is notified.
20 const int kNotifyListenerTimeMs = 300;
21
22 class MouseWatcher::Observer : public base::MessageLoopForUI::Observer {
23  public:
24   explicit Observer(MouseWatcher* mouse_watcher)
25       : mouse_watcher_(mouse_watcher),
26         notify_listener_factory_(this) {
27     base::MessageLoopForUI::current()->AddObserver(this);
28   }
29
30   virtual ~Observer() {
31     base::MessageLoopForUI::current()->RemoveObserver(this);
32   }
33
34   // MessageLoop::Observer implementation:
35 #if defined(OS_WIN)
36   virtual base::EventStatus WillProcessEvent(
37       const base::NativeEvent& event) OVERRIDE {
38     return base::EVENT_CONTINUE;
39   }
40
41   virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE {
42     // We spy on three different Windows messages here to see if the mouse has
43     // moved out of the bounds of the current view. The messages are:
44     //
45     // WM_MOUSEMOVE:
46     //   For when the mouse moves from the view into the rest of the browser UI,
47     //   i.e. within the bounds of the same windows HWND.
48     // WM_MOUSELEAVE:
49     //   For when the mouse moves out of the bounds of the view's HWND.
50     // WM_NCMOUSELEAVE:
51     //   For notification when the mouse leaves the _non-client_ area.
52     //
53     switch (event.message) {
54       case WM_MOUSEMOVE:
55         HandleGlobalMouseMoveEvent(MouseWatcherHost::MOUSE_MOVE);
56         break;
57       case WM_MOUSELEAVE:
58       case WM_NCMOUSELEAVE:
59         HandleGlobalMouseMoveEvent(MouseWatcherHost::MOUSE_EXIT);
60         break;
61     }
62   }
63 #elif defined(USE_AURA)
64   virtual base::EventStatus WillProcessEvent(
65       const base::NativeEvent& event) OVERRIDE {
66     return base::EVENT_CONTINUE;
67   }
68   virtual void DidProcessEvent(const base::NativeEvent& event) OVERRIDE {
69     switch (ui::EventTypeFromNative(event)) {
70       case ui::ET_MOUSE_MOVED:
71       case ui::ET_MOUSE_DRAGGED:
72         // DRAGGED is a special case of MOVED. See events_win.cc/events_x.cc.
73         HandleGlobalMouseMoveEvent(MouseWatcherHost::MOUSE_MOVE);
74         break;
75       case ui::ET_MOUSE_EXITED:
76         HandleGlobalMouseMoveEvent(MouseWatcherHost::MOUSE_EXIT);
77         break;
78       default:
79         break;
80     }
81   }
82 #endif
83
84  private:
85   MouseWatcherHost* host() const { return mouse_watcher_->host_.get(); }
86
87   // Called from the message loop observer when a mouse movement has occurred.
88   void HandleGlobalMouseMoveEvent(MouseWatcherHost::MouseEventType event_type) {
89     bool contained = host()->Contains(
90         // TODO(scottmg): Native is wrong http://crbug.com/133312
91         gfx::Screen::GetNativeScreen()->GetCursorScreenPoint(),
92         event_type);
93     if (!contained) {
94       // Mouse moved outside the host's zone, start a timer to notify the
95       // listener.
96       if (!notify_listener_factory_.HasWeakPtrs()) {
97         base::MessageLoop::current()->PostDelayedTask(
98             FROM_HERE,
99             base::Bind(&Observer::NotifyListener,
100                        notify_listener_factory_.GetWeakPtr()),
101             event_type == MouseWatcherHost::MOUSE_MOVE
102                 ? base::TimeDelta::FromMilliseconds(kNotifyListenerTimeMs)
103                 : mouse_watcher_->notify_on_exit_time_);
104       }
105     } else {
106       // Mouse moved quickly out of the host and then into it again, so cancel
107       // the timer.
108       notify_listener_factory_.InvalidateWeakPtrs();
109     }
110   }
111
112   void NotifyListener() {
113     mouse_watcher_->NotifyListener();
114     // WARNING: we've been deleted.
115   }
116
117  private:
118   MouseWatcher* mouse_watcher_;
119
120   // A factory that is used to construct a delayed callback to the listener.
121   base::WeakPtrFactory<Observer> notify_listener_factory_;
122
123   DISALLOW_COPY_AND_ASSIGN(Observer);
124 };
125
126 MouseWatcherListener::~MouseWatcherListener() {
127 }
128
129 MouseWatcherHost::~MouseWatcherHost() {
130 }
131
132 MouseWatcher::MouseWatcher(MouseWatcherHost* host,
133                            MouseWatcherListener* listener)
134     : host_(host),
135       listener_(listener),
136       notify_on_exit_time_(base::TimeDelta::FromMilliseconds(
137           kNotifyListenerTimeMs)) {
138 }
139
140 MouseWatcher::~MouseWatcher() {
141 }
142
143 void MouseWatcher::Start() {
144   if (!is_observing())
145     observer_.reset(new Observer(this));
146 }
147
148 void MouseWatcher::Stop() {
149   observer_.reset(NULL);
150 }
151
152 void MouseWatcher::NotifyListener() {
153   observer_.reset(NULL);
154   listener_->MouseMovedOutOfHost();
155 }
156
157 }  // namespace views