1/*
2    Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies)
3    Copyright (C) 2006 Zack Rusin <zack@kde.org>
4    Copyright (C) 2011 Research In Motion Limited.
5
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public
8    License as published by the Free Software Foundation; either
9    version 2 of the License, or (at your option) any later version.
10
11    This library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15
16    You should have received a copy of the GNU Library General Public License
17    along with this library; see the file COPYING.LIB.  If not, write to
18    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19    Boston, MA 02110-1301, USA.
20*/
21
22#include "config.h"
23#include "WebEventConversion.h"
24
25#include "PlatformGestureEvent.h"
26#include "PlatformMouseEvent.h"
27#include "PlatformTouchEvent.h"
28#include "PlatformTouchPoint.h"
29#include "PlatformWheelEvent.h"
30#include <QTouchEvent>
31#include <QWheelEvent>
32#include <wtf/CurrentTime.h>
33
34namespace WebCore {
35
36static void mouseEventModifiersFromQtKeyboardModifiers(Qt::KeyboardModifiers keyboardModifiers, unsigned& modifiers)
37{
38    modifiers = 0;
39    if (keyboardModifiers & Qt::ShiftModifier)
40        modifiers |= PlatformEvent::ShiftKey;
41    if (keyboardModifiers & Qt::ControlModifier)
42        modifiers |= PlatformEvent::CtrlKey;
43    if (keyboardModifiers & Qt::AltModifier)
44        modifiers |= PlatformEvent::AltKey;
45    if (keyboardModifiers & Qt::MetaModifier)
46        modifiers |= PlatformEvent::MetaKey;
47}
48
49static void mouseEventTypeAndMouseButtonFromQEvent(const QEvent* event, PlatformEvent::Type& mouseEventType, MouseButton& mouseButton)
50{
51    switch (event->type()) {
52    case QEvent::MouseButtonDblClick:
53    case QEvent::MouseButtonPress:
54        mouseEventType = PlatformEvent::MousePressed;
55        break;
56    case QEvent::MouseButtonRelease:
57        mouseEventType = PlatformEvent::MouseReleased;
58        break;
59    case QEvent::MouseMove:
60        mouseEventType = PlatformEvent::MouseMoved;
61        break;
62    default:
63        ASSERT_NOT_REACHED();
64        mouseEventType = PlatformEvent::MouseMoved;
65        break;
66    }
67
68    Qt::MouseButtons mouseButtons;
69
70    const QMouseEvent* mouseEvent = static_cast<const QMouseEvent*>(event);
71    mouseButtons = mouseEventType == PlatformEvent::MouseMoved ? mouseEvent->buttons() : mouseEvent->button();
72
73
74    if (mouseButtons & Qt::LeftButton)
75        mouseButton = LeftButton;
76    else if (mouseButtons & Qt::RightButton)
77        mouseButton = RightButton;
78    else if (mouseButtons & Qt::MidButton)
79        mouseButton = MiddleButton;
80    else
81        mouseButton = NoButton;
82}
83
84class WebKitPlatformMouseEvent : public PlatformMouseEvent {
85public:
86    WebKitPlatformMouseEvent(QInputEvent*, int clickCount);
87};
88
89WebKitPlatformMouseEvent::WebKitPlatformMouseEvent(QInputEvent* event, int clickCount)
90{
91    m_timestamp = WTF::currentTime();
92
93    bool isContextMenuEvent = false;
94#ifndef QT_NO_CONTEXTMENU
95    if (event->type() == QEvent::ContextMenu) {
96        isContextMenuEvent = true;
97        m_type = PlatformEvent::MousePressed;
98        QContextMenuEvent* ce = static_cast<QContextMenuEvent*>(event);
99        m_position = IntPoint(ce->pos());
100        m_globalPosition = IntPoint(ce->globalPos());
101        m_button = RightButton;
102    }
103#endif
104    if (!isContextMenuEvent) {
105        PlatformEvent::Type type;
106        mouseEventTypeAndMouseButtonFromQEvent(event, type, m_button);
107        QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
108
109        m_type = type;
110        m_position = IntPoint(mouseEvent->pos());
111        m_globalPosition = IntPoint(mouseEvent->globalPos());
112    }
113
114    m_clickCount = clickCount;
115    mouseEventModifiersFromQtKeyboardModifiers(event->modifiers(), m_modifiers);
116}
117
118PlatformMouseEvent convertMouseEvent(QInputEvent* event, int clickCount)
119{
120    return WebKitPlatformMouseEvent(event, clickCount);
121}
122
123class WebKitPlatformWheelEvent : public PlatformWheelEvent {
124public:
125    WebKitPlatformWheelEvent(QWheelEvent*, int wheelScrollLines);
126
127private:
128    void applyDelta(int delta, Qt::Orientation, int wheelScrollLines);
129};
130
131void WebKitPlatformWheelEvent::applyDelta(int delta, Qt::Orientation orientation, int wheelScrollLines)
132{
133    if (orientation == Qt::Horizontal) {
134        m_deltaX = delta;
135        m_deltaY = 0;
136    } else {
137        m_deltaX = 0;
138        m_deltaY = delta;
139    }
140    m_wheelTicksX = m_deltaX / 120.0f;
141    m_wheelTicksY = m_deltaY / 120.0f;
142
143    // Since we request the scroll delta by the pixel, convert the wheel delta to pixel delta using the standard scroll step.
144    // Use the same single scroll step as QTextEdit (in QTextEditPrivate::init [h,v]bar->setSingleStep)
145    static const float cDefaultQtScrollStep = 20.f;
146    m_deltaX = m_wheelTicksX * wheelScrollLines * cDefaultQtScrollStep;
147    m_deltaY = m_wheelTicksY * wheelScrollLines * cDefaultQtScrollStep;
148}
149
150WebKitPlatformWheelEvent::WebKitPlatformWheelEvent(QWheelEvent* e, int wheelScrollLines)
151{
152    m_timestamp = WTF::currentTime();
153    mouseEventModifiersFromQtKeyboardModifiers(e->modifiers(), m_modifiers);
154    m_position = e->pos();
155    m_globalPosition = e->globalPos();
156    m_granularity = ScrollByPixelWheelEvent;
157    m_directionInvertedFromDevice = false;
158    applyDelta(e->delta(), e->orientation(), wheelScrollLines);
159}
160
161#if ENABLE(TOUCH_EVENTS)
162class WebKitPlatformTouchEvent : public PlatformTouchEvent {
163public:
164    WebKitPlatformTouchEvent(QTouchEvent*);
165};
166
167class WebKitPlatformTouchPoint : public PlatformTouchPoint {
168public:
169    WebKitPlatformTouchPoint(const QTouchEvent::TouchPoint&, State);
170};
171
172WebKitPlatformTouchEvent::WebKitPlatformTouchEvent(QTouchEvent* event)
173{
174    switch (event->type()) {
175    case QEvent::TouchBegin:
176        m_type = PlatformEvent::TouchStart;
177        break;
178    case QEvent::TouchUpdate:
179        m_type = PlatformEvent::TouchMove;
180        break;
181    case QEvent::TouchEnd:
182        m_type = PlatformEvent::TouchEnd;
183        break;
184    case QEvent::TouchCancel:
185        m_type = PlatformEvent::TouchCancel;
186        break;
187    }
188
189    const QList<QTouchEvent::TouchPoint>& points = event->touchPoints();
190    for (int i = 0; i < points.count(); ++i) {
191        PlatformTouchPoint::State state = PlatformTouchPoint::TouchStateEnd;
192
193        switch (points.at(i).state()) {
194        case Qt::TouchPointReleased:
195            state = PlatformTouchPoint::TouchReleased;
196            break;
197        case Qt::TouchPointMoved:
198            state = PlatformTouchPoint::TouchMoved;
199            break;
200        case Qt::TouchPointPressed:
201            state = PlatformTouchPoint::TouchPressed;
202            break;
203        case Qt::TouchPointStationary:
204            state = PlatformTouchPoint::TouchStationary;
205            break;
206        }
207
208        // Qt does not have a Qt::TouchPointCancelled point state, so if we receive a touch cancel event,
209        // simply cancel all touch points here.
210        if (m_type == PlatformEvent::TouchCancel)
211            state = PlatformTouchPoint::TouchCancelled;
212
213        m_touchPoints.append(WebKitPlatformTouchPoint(points.at(i), state));
214    }
215
216    mouseEventModifiersFromQtKeyboardModifiers(event->modifiers(), m_modifiers);
217
218    m_timestamp = WTF::currentTime();
219}
220
221WebKitPlatformTouchPoint::WebKitPlatformTouchPoint(const QTouchEvent::TouchPoint& point, State state)
222{
223    // The QTouchEvent::TouchPoint API states that ids will be >= 0.
224    m_id = point.id();
225    m_state = state;
226    m_screenPos = point.screenPos().toPoint();
227    m_pos = point.pos().toPoint();
228    // Qt reports touch point size as rectangles, but we will pretend it is an oval.
229    QRect touchRect = point.rect().toAlignedRect();
230    if (touchRect.isValid()) {
231        m_radiusX = point.rect().width() / 2;
232        m_radiusY = point.rect().height() / 2;
233    } else {
234        // http://www.w3.org/TR/2011/WD-touch-events-20110505: 1 if no value is known.
235        m_radiusX = 1;
236        m_radiusY = 1;
237    }
238    m_force = point.pressure();
239    // FIXME: Support m_rotationAngle if QTouchEvent at some point supports it.
240}
241#endif
242
243#if ENABLE(GESTURE_EVENTS)
244class WebKitPlatformGestureEvent : public PlatformGestureEvent {
245public:
246    WebKitPlatformGestureEvent(QGestureEventFacade*);
247};
248
249static inline PlatformEvent::Type toPlatformEventType(Qt::GestureType type)
250{
251    switch (type) {
252    case Qt::TapGesture:
253        return PlatformEvent::GestureTap;
254    case Qt::TapAndHoldGesture:
255        return PlatformEvent::GestureLongPress;
256    default:
257        ASSERT_NOT_REACHED();
258        return PlatformEvent::NoType;
259    }
260}
261
262WebKitPlatformGestureEvent::WebKitPlatformGestureEvent(QGestureEventFacade* event)
263{
264    m_type = toPlatformEventType(event->type);
265    m_globalPosition = event->globalPos;
266    m_position = event->pos;
267    m_timestamp = WTF::currentTime();
268}
269
270#endif
271
272PlatformWheelEvent convertWheelEvent(QWheelEvent* event, int wheelScrollLines)
273{
274    return WebKitPlatformWheelEvent(event, wheelScrollLines);
275}
276
277#if ENABLE(TOUCH_EVENTS)
278PlatformTouchEvent convertTouchEvent(QTouchEvent* event)
279{
280    return WebKitPlatformTouchEvent(event);
281}
282#endif
283
284#if ENABLE(GESTURE_EVENTS)
285PlatformGestureEvent convertGesture(QGestureEventFacade* event)
286{
287    return WebKitPlatformGestureEvent(event);
288}
289#endif
290}
291