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