util/src/gui/kernel/qstandardgestures.cpp
changeset 7 f7bc934e204c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/util/src/gui/kernel/qstandardgestures.cpp	Wed Mar 31 11:06:36 2010 +0300
@@ -0,0 +1,566 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtGui module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qstandardgestures_p.h"
+#include "qgesture.h"
+#include "qgesture_p.h"
+#include "qevent.h"
+#include "qwidget.h"
+#include "qabstractscrollarea.h"
+#include "qdebug.h"
+
+QT_BEGIN_NAMESPACE
+
+QPanGestureRecognizer::QPanGestureRecognizer()
+{
+}
+
+QGesture *QPanGestureRecognizer::create(QObject *target)
+{
+    if (target && target->isWidgetType()) {
+#if defined(Q_OS_WIN) && !defined(QT_NO_NATIVE_GESTURES)
+        // for scroll areas on Windows we want to use native gestures instead
+        if (!qobject_cast<QAbstractScrollArea *>(target->parent()))
+            static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents);
+#else
+        static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents);
+#endif
+    }
+    return new QPanGesture;
+}
+
+QGestureRecognizer::Result QPanGestureRecognizer::recognize(QGesture *state,
+                                                            QObject *,
+                                                            QEvent *event)
+{
+    QPanGesture *q = static_cast<QPanGesture *>(state);
+    QPanGesturePrivate *d = q->d_func();
+
+    const QTouchEvent *ev = static_cast<const QTouchEvent *>(event);
+
+    QGestureRecognizer::Result result;
+    switch (event->type()) {
+    case QEvent::TouchBegin: {
+        result = QGestureRecognizer::MayBeGesture;
+        QTouchEvent::TouchPoint p = ev->touchPoints().at(0);
+        d->lastOffset = d->offset = QPointF();
+        break;
+    }
+    case QEvent::TouchEnd: {
+        if (q->state() != Qt::NoGesture) {
+            if (ev->touchPoints().size() == 2) {
+                QTouchEvent::TouchPoint p1 = ev->touchPoints().at(0);
+                QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1);
+                d->lastOffset = d->offset;
+                d->offset =
+                        QPointF(p1.pos().x() - p1.startPos().x() + p2.pos().x() - p2.startPos().x(),
+                              p1.pos().y() - p1.startPos().y() + p2.pos().y() - p2.startPos().y()) / 2;
+            }
+            result = QGestureRecognizer::FinishGesture;
+        } else {
+            result = QGestureRecognizer::CancelGesture;
+        }
+        break;
+    }
+    case QEvent::TouchUpdate: {
+        if (ev->touchPoints().size() >= 2) {
+            QTouchEvent::TouchPoint p1 = ev->touchPoints().at(0);
+            QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1);
+            d->lastOffset = d->offset;
+            d->offset =
+                    QPointF(p1.pos().x() - p1.startPos().x() + p2.pos().x() - p2.startPos().x(),
+                          p1.pos().y() - p1.startPos().y() + p2.pos().y() - p2.startPos().y()) / 2;
+            if (d->offset.x() > 10  || d->offset.y() > 10 ||
+                d->offset.x() < -10 || d->offset.y() < -10) {
+                result = QGestureRecognizer::TriggerGesture;
+            } else {
+                result = QGestureRecognizer::MayBeGesture;
+            }
+        }
+        break;
+    }
+    case QEvent::MouseButtonPress:
+    case QEvent::MouseMove:
+    case QEvent::MouseButtonRelease:
+        result = QGestureRecognizer::Ignore;
+        break;
+    default:
+        result = QGestureRecognizer::Ignore;
+        break;
+    }
+    return result;
+}
+
+void QPanGestureRecognizer::reset(QGesture *state)
+{
+    QPanGesture *pan = static_cast<QPanGesture*>(state);
+    QPanGesturePrivate *d = pan->d_func();
+
+    d->lastOffset = d->offset = QPointF();
+    d->acceleration = 0;
+
+    QGestureRecognizer::reset(state);
+}
+
+
+//
+// QPinchGestureRecognizer
+//
+
+QPinchGestureRecognizer::QPinchGestureRecognizer()
+{
+}
+
+QGesture *QPinchGestureRecognizer::create(QObject *target)
+{
+    if (target && target->isWidgetType()) {
+        static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents);
+    }
+    return new QPinchGesture;
+}
+
+QGestureRecognizer::Result QPinchGestureRecognizer::recognize(QGesture *state,
+                                                              QObject *,
+                                                              QEvent *event)
+{
+    QPinchGesture *q = static_cast<QPinchGesture *>(state);
+    QPinchGesturePrivate *d = q->d_func();
+
+    const QTouchEvent *ev = static_cast<const QTouchEvent *>(event);
+
+    QGestureRecognizer::Result result;
+
+    switch (event->type()) {
+    case QEvent::TouchBegin: {
+        result = QGestureRecognizer::MayBeGesture;
+        break;
+    }
+    case QEvent::TouchEnd: {
+        if (q->state() != Qt::NoGesture) {
+            result = QGestureRecognizer::FinishGesture;
+        } else {
+            result = QGestureRecognizer::CancelGesture;
+        }
+        break;
+    }
+    case QEvent::TouchUpdate: {
+        d->changeFlags = 0;
+        if (ev->touchPoints().size() == 2) {
+            QTouchEvent::TouchPoint p1 = ev->touchPoints().at(0);
+            QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1);
+
+            d->hotSpot = p1.screenPos();
+            d->isHotSpotSet = true;
+
+            if (d->isNewSequence) {
+                d->startPosition[0] = p1.screenPos();
+                d->startPosition[1] = p2.screenPos();
+            }
+            QLineF line(p1.screenPos(), p2.screenPos());
+            QLineF tmp(line);
+            tmp.setLength(line.length() / 2.);
+            QPointF centerPoint = tmp.p2();
+
+            d->lastCenterPoint = d->centerPoint;
+            d->centerPoint = centerPoint;
+            d->changeFlags |= QPinchGesture::CenterPointChanged;
+
+            const qreal scaleFactor =
+                    QLineF(p1.screenPos(), p2.screenPos()).length()
+                    / QLineF(d->startPosition[0],  d->startPosition[1]).length();
+            if (d->isNewSequence) {
+                d->lastScaleFactor = scaleFactor;
+            } else {
+                d->lastScaleFactor = d->scaleFactor;
+            }
+            d->scaleFactor = scaleFactor;
+            d->totalScaleFactor += d->scaleFactor - d->lastScaleFactor;
+            d->changeFlags |= QPinchGesture::ScaleFactorChanged;
+
+            qreal angle = QLineF(p1.screenPos(), p2.screenPos()).angle();
+            if (angle > 180)
+                angle -= 360;
+            qreal startAngle = QLineF(p1.startScreenPos(), p2.startScreenPos()).angle();
+            if (startAngle > 180)
+                startAngle -= 360;
+            const qreal rotationAngle = startAngle - angle;
+            if (d->isNewSequence)
+                d->lastRotationAngle = rotationAngle;
+            else
+                d->lastRotationAngle = d->rotationAngle;
+            d->rotationAngle = rotationAngle;
+            d->totalRotationAngle += d->rotationAngle - d->lastRotationAngle;
+            d->changeFlags |= QPinchGesture::RotationAngleChanged;
+
+            d->totalChangeFlags |= d->changeFlags;
+            d->isNewSequence = false;
+            result = QGestureRecognizer::TriggerGesture;
+        } else {
+            d->isNewSequence = true;
+            if (q->state() == Qt::NoGesture)
+                result = QGestureRecognizer::Ignore;
+            else
+                result = QGestureRecognizer::FinishGesture;
+        }
+        break;
+    }
+    case QEvent::MouseButtonPress:
+    case QEvent::MouseMove:
+    case QEvent::MouseButtonRelease:
+        result = QGestureRecognizer::Ignore;
+        break;
+    default:
+        result = QGestureRecognizer::Ignore;
+        break;
+    }
+    return result;
+}
+
+void QPinchGestureRecognizer::reset(QGesture *state)
+{
+    QPinchGesture *pinch = static_cast<QPinchGesture *>(state);
+    QPinchGesturePrivate *d = pinch->d_func();
+
+    d->totalChangeFlags = d->changeFlags = 0;
+
+    d->startCenterPoint = d->lastCenterPoint = d->centerPoint = QPointF();
+    d->totalScaleFactor = d->lastScaleFactor = d->scaleFactor = 0;
+    d->totalRotationAngle = d->lastRotationAngle = d->rotationAngle = 0;
+
+    d->isNewSequence = true;
+    d->startPosition[0] = d->startPosition[1] = QPointF();
+
+    QGestureRecognizer::reset(state);
+}
+
+//
+// QSwipeGestureRecognizer
+//
+
+QSwipeGestureRecognizer::QSwipeGestureRecognizer()
+{
+}
+
+QGesture *QSwipeGestureRecognizer::create(QObject *target)
+{
+    if (target && target->isWidgetType()) {
+        static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents);
+    }
+    return new QSwipeGesture;
+}
+
+QGestureRecognizer::Result QSwipeGestureRecognizer::recognize(QGesture *state,
+                                                              QObject *,
+                                                              QEvent *event)
+{
+    QSwipeGesture *q = static_cast<QSwipeGesture *>(state);
+    QSwipeGesturePrivate *d = q->d_func();
+
+    const QTouchEvent *ev = static_cast<const QTouchEvent *>(event);
+
+    QGestureRecognizer::Result result;
+
+    switch (event->type()) {
+    case QEvent::TouchBegin: {
+        d->speed = 1;
+        d->time = QTime::currentTime();
+        d->started = true;
+        result = QGestureRecognizer::MayBeGesture;
+        break;
+    }
+    case QEvent::TouchEnd: {
+        if (q->state() != Qt::NoGesture) {
+            result = QGestureRecognizer::FinishGesture;
+        } else {
+            result = QGestureRecognizer::CancelGesture;
+        }
+        break;
+    }
+    case QEvent::TouchUpdate: {
+        if (!d->started)
+            result = QGestureRecognizer::CancelGesture;
+        else if (ev->touchPoints().size() == 3) {
+            QTouchEvent::TouchPoint p1 = ev->touchPoints().at(0);
+            QTouchEvent::TouchPoint p2 = ev->touchPoints().at(1);
+            QTouchEvent::TouchPoint p3 = ev->touchPoints().at(2);
+
+            if (d->lastPositions[0].isNull()) {
+                d->lastPositions[0] = p1.startScreenPos().toPoint();
+                d->lastPositions[1] = p2.startScreenPos().toPoint();
+                d->lastPositions[2] = p3.startScreenPos().toPoint();
+            }
+            d->hotSpot = p1.screenPos();
+            d->isHotSpotSet = true;
+
+            int xDistance = (p1.screenPos().x() - d->lastPositions[0].x() +
+                             p2.screenPos().x() - d->lastPositions[1].x() +
+                             p3.screenPos().x() - d->lastPositions[2].x()) / 3;
+            int yDistance = (p1.screenPos().y() - d->lastPositions[0].y() +
+                             p2.screenPos().y() - d->lastPositions[1].y() +
+                             p3.screenPos().y() - d->lastPositions[2].y()) / 3;
+
+            const int distance = xDistance >= yDistance ? xDistance : yDistance;
+            int elapsedTime = d->time.msecsTo(QTime::currentTime());
+            if (!elapsedTime)
+                elapsedTime = 1;
+            d->speed = 0.9 * d->speed + distance / elapsedTime;
+            d->time = QTime::currentTime();
+            d->swipeAngle = QLineF(p1.startScreenPos(), p1.screenPos()).angle();
+
+            static const int MoveThreshold = 50;
+            if (xDistance > MoveThreshold || yDistance > MoveThreshold) {
+                // measure the distance to check if the direction changed
+                d->lastPositions[0] = p1.screenPos().toPoint();
+                d->lastPositions[1] = p2.screenPos().toPoint();
+                d->lastPositions[2] = p3.screenPos().toPoint();
+                QSwipeGesture::SwipeDirection horizontal =
+                        xDistance > 0 ? QSwipeGesture::Right : QSwipeGesture::Left;
+                QSwipeGesture::SwipeDirection vertical =
+                        yDistance > 0 ? QSwipeGesture::Down : QSwipeGesture::Up;
+                if (d->verticalDirection == QSwipeGesture::NoDirection)
+                    d->verticalDirection = vertical;
+                if (d->horizontalDirection == QSwipeGesture::NoDirection)
+                    d->horizontalDirection = horizontal;
+                if (d->verticalDirection != vertical || d->horizontalDirection != horizontal) {
+                    // the user has changed the direction!
+                    result = QGestureRecognizer::CancelGesture;
+                }
+                result = QGestureRecognizer::TriggerGesture;
+            } else {
+                if (q->state() != Qt::NoGesture)
+                    result = QGestureRecognizer::TriggerGesture;
+                else
+                    result = QGestureRecognizer::MayBeGesture;
+            }
+        } else if (ev->touchPoints().size() > 3) {
+            result = QGestureRecognizer::CancelGesture;
+        } else { // less than 3 touch points
+            if (d->started && (ev->touchPointStates() & Qt::TouchPointPressed))
+                result = QGestureRecognizer::CancelGesture;
+            else if (d->started)
+                result = QGestureRecognizer::Ignore;
+            else
+                result = QGestureRecognizer::MayBeGesture;
+        }
+        break;
+    }
+    case QEvent::MouseButtonPress:
+    case QEvent::MouseMove:
+    case QEvent::MouseButtonRelease:
+        result = QGestureRecognizer::Ignore;
+        break;
+    default:
+        result = QGestureRecognizer::Ignore;
+        break;
+    }
+    return result;
+}
+
+void QSwipeGestureRecognizer::reset(QGesture *state)
+{
+    QSwipeGesture *q = static_cast<QSwipeGesture *>(state);
+    QSwipeGesturePrivate *d = q->d_func();
+
+    d->verticalDirection = d->horizontalDirection = QSwipeGesture::NoDirection;
+    d->swipeAngle = 0;
+
+    d->lastPositions[0] = d->lastPositions[1] = d->lastPositions[2] = QPoint();
+    d->started = false;
+    d->speed = 0;
+    d->time = QTime();
+
+    QGestureRecognizer::reset(state);
+}
+
+//
+// QTapGestureRecognizer
+//
+
+QTapGestureRecognizer::QTapGestureRecognizer()
+{
+}
+
+QGesture *QTapGestureRecognizer::create(QObject *target)
+{
+    if (target && target->isWidgetType()) {
+        static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents);
+    }
+    return new QTapGesture;
+}
+
+QGestureRecognizer::Result QTapGestureRecognizer::recognize(QGesture *state,
+                                                            QObject *,
+                                                            QEvent *event)
+{
+    QTapGesture *q = static_cast<QTapGesture *>(state);
+    QTapGesturePrivate *d = q->d_func();
+
+    const QTouchEvent *ev = static_cast<const QTouchEvent *>(event);
+
+    QGestureRecognizer::Result result = QGestureRecognizer::CancelGesture;
+
+    switch (event->type()) {
+    case QEvent::TouchBegin: {
+        d->position = ev->touchPoints().at(0).pos();
+        result = QGestureRecognizer::TriggerGesture;
+        break;
+    }
+    case QEvent::TouchUpdate:
+    case QEvent::TouchEnd: {
+        if (q->state() != Qt::NoGesture && ev->touchPoints().size() == 1) {
+            QTouchEvent::TouchPoint p = ev->touchPoints().at(0);
+            QPoint delta = p.pos().toPoint() - p.startPos().toPoint();
+            enum { TapRadius = 40 };
+            if (delta.manhattanLength() <= TapRadius) {
+                if (event->type() == QEvent::TouchEnd)
+                    result = QGestureRecognizer::FinishGesture;
+                else
+                    result = QGestureRecognizer::TriggerGesture;
+            }
+        }
+        break;
+    }
+    case QEvent::MouseButtonPress:
+    case QEvent::MouseMove:
+    case QEvent::MouseButtonRelease:
+        result = QGestureRecognizer::Ignore;
+        break;
+    default:
+        result = QGestureRecognizer::Ignore;
+        break;
+    }
+    return result;
+}
+
+void QTapGestureRecognizer::reset(QGesture *state)
+{
+    QTapGesture *q = static_cast<QTapGesture *>(state);
+    QTapGesturePrivate *d = q->d_func();
+
+    d->position = QPointF();
+
+    QGestureRecognizer::reset(state);
+}
+
+//
+// QTapAndHoldGestureRecognizer
+//
+
+QTapAndHoldGestureRecognizer::QTapAndHoldGestureRecognizer()
+{
+}
+
+QGesture *QTapAndHoldGestureRecognizer::create(QObject *target)
+{
+    if (target && target->isWidgetType()) {
+        static_cast<QWidget *>(target)->setAttribute(Qt::WA_AcceptTouchEvents);
+    }
+    return new QTapAndHoldGesture;
+}
+
+QGestureRecognizer::Result
+QTapAndHoldGestureRecognizer::recognize(QGesture *state, QObject *object,
+                                        QEvent *event)
+{
+    QTapAndHoldGesture *q = static_cast<QTapAndHoldGesture *>(state);
+    QTapAndHoldGesturePrivate *d = q->d_func();
+
+    if (object == state && event->type() == QEvent::Timer) {
+        q->killTimer(d->timerId);
+        d->timerId = 0;
+        return QGestureRecognizer::Ignore | QGestureRecognizer::ConsumeEventHint;
+    }
+
+    const QTouchEvent *ev = static_cast<const QTouchEvent *>(event);
+
+    QGestureRecognizer::Result result = QGestureRecognizer::CancelGesture;
+
+    enum { TimerInterval = 2000 };
+    enum { TapRadius = 40 };
+
+    switch (event->type()) {
+    case QEvent::TouchBegin:
+        d->position = ev->touchPoints().at(0).pos();
+        if (d->timerId)
+            q->killTimer(d->timerId);
+        d->timerId = q->startTimer(TimerInterval);
+        result = QGestureRecognizer::TriggerGesture;
+        break;
+    case QEvent::TouchEnd:
+        if (d->timerId)
+            result = QGestureRecognizer::CancelGesture;
+        else
+            result = QGestureRecognizer::FinishGesture;
+        break;
+    case QEvent::TouchUpdate:
+        if (q->state() != Qt::NoGesture && ev->touchPoints().size() == 1) {
+            QTouchEvent::TouchPoint p = ev->touchPoints().at(0);
+            QPoint delta = p.pos().toPoint() - p.startPos().toPoint();
+            if (delta.manhattanLength() <= TapRadius)
+                result = QGestureRecognizer::TriggerGesture;
+        }
+        break;
+    case QEvent::MouseButtonPress:
+    case QEvent::MouseMove:
+    case QEvent::MouseButtonRelease:
+        result = QGestureRecognizer::Ignore;
+        break;
+    default:
+        result = QGestureRecognizer::Ignore;
+        break;
+    }
+    return result;
+}
+
+void QTapAndHoldGestureRecognizer::reset(QGesture *state)
+{
+    QTapAndHoldGesture *q = static_cast<QTapAndHoldGesture *>(state);
+    QTapAndHoldGesturePrivate *d = q->d_func();
+
+    d->position = QPointF();
+    if (d->timerId)
+        q->killTimer(d->timerId);
+    d->timerId = 0;
+
+    QGestureRecognizer::reset(state);
+}
+
+QT_END_NAMESPACE