Initial import from qtquick2.
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsgpincharea.cpp
1 // Commit: 2ec2dc55ddf424f5a7acd0a4729ddd9af2d7c398
2 /****************************************************************************
3 **
4 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
5 ** All rights reserved.
6 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 **
8 ** This file is part of the QtSG module of the Qt Toolkit.
9 **
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** No Commercial Usage
12 ** This file contains pre-release code and may not be distributed.
13 ** You may use this file in accordance with the terms and conditions
14 ** contained in the Technology Preview License Agreement accompanying
15 ** this package.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 **
39 ** $QT_END_LICENSE$
40 **
41 ****************************************************************************/
42
43 #include "qsgpincharea_p_p.h"
44 #include "qsgcanvas.h"
45
46 #include <QtGui/qgraphicssceneevent.h>
47 #include <QtGui/qapplication.h>
48
49 #include <float.h>
50 #include <math.h>
51
52 QT_BEGIN_NAMESPACE
53
54 QSGPinch::QSGPinch()
55     : m_target(0), m_minScale(1.0), m_maxScale(1.0)
56     , m_minRotation(0.0), m_maxRotation(0.0)
57     , m_axis(NoDrag), m_xmin(-FLT_MAX), m_xmax(FLT_MAX)
58     , m_ymin(-FLT_MAX), m_ymax(FLT_MAX), m_active(false)
59 {
60 }
61
62 QSGPinchAreaPrivate::~QSGPinchAreaPrivate()
63 {
64     delete pinch;
65 }
66
67 QSGPinchArea::QSGPinchArea(QSGItem *parent)
68   : QSGItem(*(new QSGPinchAreaPrivate), parent)
69 {
70     Q_D(QSGPinchArea);
71     d->init();
72 }
73
74 QSGPinchArea::~QSGPinchArea()
75 {
76 }
77
78 bool QSGPinchArea::isEnabled() const
79 {
80     Q_D(const QSGPinchArea);
81     return d->absorb;
82 }
83
84 void QSGPinchArea::setEnabled(bool a)
85 {
86     Q_D(QSGPinchArea);
87     if (a != d->absorb) {
88         d->absorb = a;
89         emit enabledChanged();
90     }
91 }
92
93 void QSGPinchArea::touchEvent(QTouchEvent *event)
94 {
95     Q_D(QSGPinchArea);
96     if (!d->absorb || !isVisible()) {
97         QSGItem::event(event);
98         return;
99     }
100
101     switch (event->type()) {
102     case QEvent::TouchBegin:
103     case QEvent::TouchUpdate:
104         d->touchPoints.clear();
105         for (int i = 0; i < event->touchPoints().count(); ++i) {
106             if (!(event->touchPoints().at(i).state() & Qt::TouchPointReleased)) {
107                 d->touchPoints << event->touchPoints().at(i);
108             }
109         }
110         updatePinch();
111         break;
112     case QEvent::TouchEnd:
113         d->touchPoints.clear();
114         updatePinch();
115         break;
116     default:
117         QSGItem::event(event);
118     }
119 }
120
121 void QSGPinchArea::updatePinch()
122 {
123     Q_D(QSGPinchArea);
124     if (d->touchPoints.count() != 2) {
125         if (d->inPinch) {
126             d->stealMouse = false;
127             setKeepMouseGrab(false);
128             d->inPinch = false;
129             QPointF pinchCenter = mapFromScene(d->sceneLastCenter);
130             QSGPinchEvent pe(pinchCenter, d->pinchLastScale, d->pinchLastAngle, d->pinchRotation);
131             pe.setStartCenter(d->pinchStartCenter);
132             pe.setPreviousCenter(pinchCenter);
133             pe.setPreviousAngle(d->pinchLastAngle);
134             pe.setPreviousScale(d->pinchLastScale);
135             pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
136             pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
137             pe.setPoint1(d->lastPoint1);
138             pe.setPoint2(d->lastPoint2);
139             emit pinchFinished(&pe);
140             d->pinchStartDist = 0;
141             if (d->pinch && d->pinch->target())
142                 d->pinch->setActive(false);
143         }
144         return;
145     }
146     if (d->touchPoints.at(0).state() & Qt::TouchPointPressed
147         || d->touchPoints.at(1).state() & Qt::TouchPointPressed) {
148         d->sceneStartPoint1 = d->touchPoints.at(0).scenePos();
149         d->sceneStartPoint2 = d->touchPoints.at(1).scenePos();
150         d->inPinch = false;
151         d->pinchRejected = false;
152     } else if (!d->pinchRejected){
153         QSGItem *grabber = canvas() ? canvas()->mouseGrabberItem() : 0;
154         if (grabber == this || !grabber || !grabber->keepMouseGrab()) {
155             const int dragThreshold = QApplication::startDragDistance();
156             QPointF p1 = d->touchPoints.at(0).scenePos();
157             QPointF p2 = d->touchPoints.at(1).scenePos();
158             qreal dx = p1.x() - p2.x();
159             qreal dy = p1.y() - p2.y();
160             qreal dist = sqrt(dx*dx + dy*dy);
161             QPointF sceneCenter = (p1 + p2)/2;
162             qreal angle = QLineF(p1, p2).angle();
163             if (angle > 180)
164                 angle -= 360;
165             if (!d->inPinch) {
166                 if (qAbs(p1.x()-d->sceneStartPoint1.x()) > dragThreshold
167                         || qAbs(p1.y()-d->sceneStartPoint1.y()) > dragThreshold
168                         || qAbs(p2.x()-d->sceneStartPoint2.x()) > dragThreshold
169                         || qAbs(p2.y()-d->sceneStartPoint2.y()) > dragThreshold) {
170                     d->sceneStartCenter = sceneCenter;
171                     d->sceneLastCenter = sceneCenter;
172                     d->pinchStartCenter = mapFromScene(sceneCenter);
173                     d->pinchStartDist = dist;
174                     d->pinchStartAngle = angle;
175                     d->pinchLastScale = 1.0;
176                     d->pinchLastAngle = angle;
177                     d->pinchRotation = 0.0;
178                     d->lastPoint1 = d->touchPoints.at(0).pos();
179                     d->lastPoint2 = d->touchPoints.at(1).pos();
180                     QSGPinchEvent pe(d->pinchStartCenter, 1.0, angle, 0.0);
181                     pe.setStartCenter(d->pinchStartCenter);
182                     pe.setPreviousCenter(d->pinchStartCenter);
183                     pe.setPreviousAngle(d->pinchLastAngle);
184                     pe.setPreviousScale(d->pinchLastScale);
185                     pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
186                     pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
187                     pe.setPoint1(d->lastPoint1);
188                     pe.setPoint2(d->lastPoint2);
189                     emit pinchStarted(&pe);
190                     if (pe.accepted()) {
191                         d->inPinch = true;
192                         d->stealMouse = true;
193                         QSGCanvas *c = canvas();
194                         if (c && c->mouseGrabberItem() != this)
195                             grabMouse();
196                         setKeepMouseGrab(true);
197                         if (d->pinch && d->pinch->target()) {
198                             d->pinchStartPos = pinch()->target()->pos();
199                             d->pinchStartScale = d->pinch->target()->scale();
200                             d->pinchStartRotation = d->pinch->target()->rotation();
201                             d->pinch->setActive(true);
202                         }
203                     } else {
204                         d->pinchRejected = true;
205                     }
206                 }
207             } else if (d->pinchStartDist > 0) {
208                 qreal scale = dist / d->pinchStartDist;
209                 qreal da = d->pinchLastAngle - angle;
210                 if (da > 180)
211                     da -= 360;
212                 else if (da < -180)
213                     da += 360;
214                 d->pinchRotation += da;
215                 QPointF pinchCenter = mapFromScene(sceneCenter);
216                 QSGPinchEvent pe(pinchCenter, scale, angle, d->pinchRotation);
217                 pe.setStartCenter(d->pinchStartCenter);
218                 pe.setPreviousCenter(mapFromScene(d->sceneLastCenter));
219                 pe.setPreviousAngle(d->pinchLastAngle);
220                 pe.setPreviousScale(d->pinchLastScale);
221                 pe.setStartPoint1(mapFromScene(d->sceneStartPoint1));
222                 pe.setStartPoint2(mapFromScene(d->sceneStartPoint2));
223                 pe.setPoint1(d->touchPoints.at(0).pos());
224                 pe.setPoint2(d->touchPoints.at(1).pos());
225                 d->pinchLastScale = scale;
226                 d->sceneLastCenter = sceneCenter;
227                 d->pinchLastAngle = angle;
228                 d->lastPoint1 = d->touchPoints.at(0).pos();
229                 d->lastPoint2 = d->touchPoints.at(1).pos();
230                 emit pinchUpdated(&pe);
231                 if (d->pinch && d->pinch->target()) {
232                     qreal s = d->pinchStartScale * scale;
233                     s = qMin(qMax(pinch()->minimumScale(),s), pinch()->maximumScale());
234                     pinch()->target()->setScale(s);
235                     QPointF pos = sceneCenter - d->sceneStartCenter + d->pinchStartPos;
236                     if (pinch()->axis() & QSGPinch::XAxis) {
237                         qreal x = pos.x();
238                         if (x < pinch()->xmin())
239                             x = pinch()->xmin();
240                         else if (x > pinch()->xmax())
241                             x = pinch()->xmax();
242                         pinch()->target()->setX(x);
243                     }
244                     if (pinch()->axis() & QSGPinch::YAxis) {
245                         qreal y = pos.y();
246                         if (y < pinch()->ymin())
247                             y = pinch()->ymin();
248                         else if (y > pinch()->ymax())
249                             y = pinch()->ymax();
250                         pinch()->target()->setY(y);
251                     }
252                     if (d->pinchStartRotation >= pinch()->minimumRotation()
253                             && d->pinchStartRotation <= pinch()->maximumRotation()) {
254                         qreal r = d->pinchRotation + d->pinchStartRotation;
255                         r = qMin(qMax(pinch()->minimumRotation(),r), pinch()->maximumRotation());
256                         pinch()->target()->setRotation(r);
257                     }
258                 }
259             }
260         }
261     }
262 }
263
264 void QSGPinchArea::mousePressEvent(QGraphicsSceneMouseEvent *event)
265 {
266     Q_D(QSGPinchArea);
267     d->stealMouse = false;
268     if (!d->absorb)
269         QSGItem::mousePressEvent(event);
270     else {
271         setKeepMouseGrab(false);
272         event->setAccepted(true);
273     }
274 }
275
276 void QSGPinchArea::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
277 {
278     Q_D(QSGPinchArea);
279     if (!d->absorb) {
280         QSGItem::mouseMoveEvent(event);
281         return;
282     }
283 }
284
285 void QSGPinchArea::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
286 {
287     Q_D(QSGPinchArea);
288     d->stealMouse = false;
289     if (!d->absorb) {
290         QSGItem::mouseReleaseEvent(event);
291     } else {
292         QSGCanvas *c = canvas();
293         if (c && c->mouseGrabberItem() == this)
294             ungrabMouse();
295         setKeepMouseGrab(false);
296     }
297 }
298
299 void QSGPinchArea::mouseUngrabEvent()
300 {
301     setKeepMouseGrab(false);
302 }
303
304 bool QSGPinchArea::sendMouseEvent(QGraphicsSceneMouseEvent *event)
305 {
306     Q_D(QSGPinchArea);
307     QGraphicsSceneMouseEvent mouseEvent(event->type());
308     QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
309
310     QSGCanvas *c = canvas();
311     QSGItem *grabber = c ? c->mouseGrabberItem() : 0;
312     bool stealThisEvent = d->stealMouse;
313     if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
314         mouseEvent.setAccepted(false);
315         for (int i = 0x1; i <= 0x10; i <<= 1) {
316             if (event->buttons() & i) {
317                 Qt::MouseButton button = Qt::MouseButton(i);
318                 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
319             }
320         }
321         mouseEvent.setScenePos(event->scenePos());
322         mouseEvent.setLastScenePos(event->lastScenePos());
323         mouseEvent.setPos(mapFromScene(event->scenePos()));
324         mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
325
326         switch(mouseEvent.type()) {
327         case QEvent::GraphicsSceneMouseMove:
328             mouseMoveEvent(&mouseEvent);
329             break;
330         case QEvent::GraphicsSceneMousePress:
331             mousePressEvent(&mouseEvent);
332             break;
333         case QEvent::GraphicsSceneMouseRelease:
334             mouseReleaseEvent(&mouseEvent);
335             break;
336         default:
337             break;
338         }
339         grabber = c->mouseGrabberItem();
340         if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
341             grabMouse();
342
343         return stealThisEvent;
344     }
345     if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
346         d->stealMouse = false;
347         if (c && c->mouseGrabberItem() == this)
348             ungrabMouse();
349         setKeepMouseGrab(false);
350     }
351     return false;
352 }
353
354 bool QSGPinchArea::childMouseEventFilter(QSGItem *i, QEvent *e)
355 {
356     Q_D(QSGPinchArea);
357     if (!d->absorb || !isVisible())
358         return QSGItem::childMouseEventFilter(i, e);
359     switch (e->type()) {
360     case QEvent::GraphicsSceneMousePress:
361     case QEvent::GraphicsSceneMouseMove:
362     case QEvent::GraphicsSceneMouseRelease:
363         return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
364         break;
365     case QEvent::TouchBegin:
366     case QEvent::TouchUpdate: {
367             QTouchEvent *touch = static_cast<QTouchEvent*>(e);
368             d->touchPoints.clear();
369             for (int i = 0; i < touch->touchPoints().count(); ++i)
370                 if (!(touch->touchPoints().at(i).state() & Qt::TouchPointReleased))
371                     d->touchPoints << touch->touchPoints().at(i);
372             updatePinch();
373         }
374         return d->inPinch;
375     case QEvent::TouchEnd:
376         d->touchPoints.clear();
377         updatePinch();
378         break;
379     default:
380         break;
381     }
382
383     return QSGItem::childMouseEventFilter(i, e);
384 }
385
386 void QSGPinchArea::geometryChanged(const QRectF &newGeometry,
387                                             const QRectF &oldGeometry)
388 {
389     QSGItem::geometryChanged(newGeometry, oldGeometry);
390 }
391
392 void QSGPinchArea::itemChange(ItemChange change, const ItemChangeData &value)
393 {
394     QSGItem::itemChange(change, value);
395 }
396
397 QSGPinch *QSGPinchArea::pinch()
398 {
399     Q_D(QSGPinchArea);
400     if (!d->pinch)
401         d->pinch = new QSGPinch;
402     return d->pinch;
403 }
404
405
406 QT_END_NAMESPACE
407