summaryrefslogtreecommitdiff
path: root/src/imports/location/qdeclarativegeomapflickable.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/imports/location/qdeclarativegeomapflickable.cpp')
-rw-r--r--src/imports/location/qdeclarativegeomapflickable.cpp371
1 files changed, 371 insertions, 0 deletions
diff --git a/src/imports/location/qdeclarativegeomapflickable.cpp b/src/imports/location/qdeclarativegeomapflickable.cpp
new file mode 100644
index 00000000..c565801f
--- /dev/null
+++ b/src/imports/location/qdeclarativegeomapflickable.cpp
@@ -0,0 +1,371 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $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 <QtGui/QApplication>
+#include <QGraphicsSceneMouseEvent>
+#include <QPropertyAnimation>
+#include <QEasingCurve>
+#include "qdeclarativegeomapflickable_p.h"
+#include <QTimer>
+#include "map.h"
+#include "math.h"
+
+// Constant values from QSGFlickable
+// The default maximum velocity of a flick.
+#ifndef QML_FLICK_DEFAULTMAXVELOCITY
+#define QML_FLICK_DEFAULTMAXVELOCITY 2500
+#endif
+// The default deceleration of a flick.
+#ifndef QML_FLICK_DEFAULTDECELERATION
+#define QML_FLICK_DEFAULTDECELERATION 1500
+#endif
+// Minimum
+#ifndef QML_FLICK_MINIMUMECELERATION
+#define QML_FLICK_MINIMUMDECELERATION 10
+#endif
+
+
+// The number of samples to discard when calculating the flick velocity.
+// Touch panels often produce inaccurate results as the finger is lifted.
+#ifndef QML_FLICK_DISCARDSAMPLES
+#define QML_FLICK_DISCARDSAMPLES 1
+#endif
+
+// FlickThreshold determines how far the "mouse" must have moved
+// before we perform a flick.
+static const int FlickThreshold = 20;
+// RetainGrabVelocity is the maxmimum instantaneous velocity that
+// will ensure the Flickable retains the grab on consecutive flicks.
+static const int RetainGrabVelocity = 15;
+// Really slow flicks can be annoying.
+const qreal MinimumFlickVelocity = 75.0;
+
+QT_BEGIN_NAMESPACE
+
+QDeclarativeGeoMapFlickable::QDeclarativeGeoMapFlickable(Map* map, QObject *parent)
+ : QObject(parent),
+ pressed_(false),
+ maxVelocity_(QML_FLICK_DEFAULTMAXVELOCITY),
+ deceleration_(QML_FLICK_DEFAULTDECELERATION),
+ flicking_(false),
+ map_(map),
+ enabled_(false),
+ moving_(false)
+{
+ Q_ASSERT(map_);
+ pressTime_.invalidate();
+ lastPosTime_.invalidate();
+ velocityTime_.invalidate();
+ //animation_ = new QPropertyAnimation(map_->mapCamera(), "cameraData", this);
+ //animation_ = new QPropertyAnimation(map_->mapCamera(), "center", this);
+ animation_ = new QPropertyAnimation(map_, "camera", this);
+ animation_->setEasingCurve(QEasingCurve(QEasingCurve::OutQuad));
+ connect(animation_, SIGNAL(finished()), this, SLOT(flickAnimationFinished()));
+ //connect(animation_, SIGNAL(valueChanged(const QVariant&)), this, SLOT(flickAnimationValueChanged(const QVariant&)));
+}
+
+QDeclarativeGeoMapFlickable::~QDeclarativeGeoMapFlickable()
+{
+}
+
+qreal QDeclarativeGeoMapFlickable::deceleration() const
+{
+ return deceleration_;
+}
+
+void QDeclarativeGeoMapFlickable::setDeceleration(qreal deceleration)
+{
+ if (deceleration < QML_FLICK_MINIMUMDECELERATION)
+ deceleration = QML_FLICK_MINIMUMDECELERATION;
+ if (deceleration == deceleration_)
+ return;
+
+ deceleration_ = deceleration;
+ emit decelerationChanged();
+}
+
+void QDeclarativeGeoMapFlickable::mousePressEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (!enabled_)
+ return;
+ //Q_ASSERT(!pressed_);
+
+ stop();
+ pressed_ = true;
+ lastPos_ = QPointF();
+ pressPos_ = event->pos();
+ lastPosTime_.start();
+ pressTime_.start();
+ velocityTime_.start();
+}
+
+void QDeclarativeGeoMapFlickable::stop()
+{
+ velocityBufferX_.clear();
+ velocityBufferY_.clear();
+ velocityX_ = 0.0;
+ velocityY_ = 0.0;
+ if (moving_) {
+ moving_ = false;
+ emit movementEnded();
+ }
+ if (flicking_) {
+ flicking_ = false;
+ if (animation_->state() == QPropertyAnimation::Running)
+ animation_->stop();
+ emit flickEnded();
+ }
+ lastPosTime_.invalidate();
+ pressTime_.invalidate();
+ velocityTime_.invalidate();
+}
+
+void QDeclarativeGeoMapFlickable::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (!enabled_) {
+ return;
+ }
+ if (!pressed_) {
+ return;
+ }
+ if (!lastPosTime_.isValid()) {
+ return;
+ }
+
+ // Check if thresholds for normal panning are met.
+ // (normal panning vs flicking: flicking will start from mouse release event).
+ int dyFromPress = int(event->pos().y() - pressPos_.y());
+ int dxFromPress = int(event->pos().x() - pressPos_.x());
+ int dyFromLastPos;
+ int dxFromLastPos;
+ if (!lastPos_.isNull()) {
+ dyFromLastPos = event->pos().y() - lastPos_.y();
+ dxFromLastPos = event->pos().x() - lastPos_.x();
+ }
+
+ // Simple pan (drag) while being pressed
+ if ((qAbs(dyFromPress) > QApplication::startDragDistance()
+ || qAbs(dxFromPress) > QApplication::startDragDistance()
+ || pressTime_.elapsed() > 200) && !lastPos_.isNull()) {
+
+ if (!moving_) {
+ emit movementStarted();
+ moving_ = true;
+ }
+ updateCamera(dxFromLastPos, dyFromLastPos, 0);
+ }
+ // Take velocity samples, used later to determine the flick
+ // duration and speed (when mouse is released).
+ if (!lastPos_.isNull()) {
+ qreal elapsed = qreal(lastPosTime_.elapsed()) / 1000.;
+ if (elapsed <= 0)
+ return;
+ lastPosTime_.restart();
+ addVelocitySample(velocityBufferY_, double(dyFromLastPos)/elapsed);
+ addVelocitySample(velocityBufferX_, double(dxFromLastPos)/elapsed);
+ }
+ lastPos_ = event->pos();
+}
+
+// FIXME coordinate pan with a sleeve-constant (zoom level is not considered appropriately)
+void QDeclarativeGeoMapFlickable::updateCamera(int dx, int dy, int timeMs)
+{
+ if (timeMs < 0)
+ return;
+ CameraData cameraStart = map_->cameraData();
+ QGeoCoordinate coordinate = cameraStart.center();
+ if (timeMs == 0) {
+ // No animation, just set new values.
+ coordinate.setLatitude(coordinate.latitude() + (dy / pow(2.0, cameraStart.zoomFactor())));
+ coordinate.setLongitude(coordinate.longitude() - (dx / pow(2.0, cameraStart.zoomFactor())));
+ cameraStart.setCenter(coordinate);
+ map_->setCameraData(cameraStart);
+ } else {
+ qDebug() << "Will do flick animation dx (pix), dy (pix), time (ms): " << dx << dy << timeMs;
+ if (animation_->state() == QPropertyAnimation::Running)
+ animation_->stop();
+ CameraData cameraEnd = cameraStart;
+ animation_->setDuration(timeMs);
+ coordinate.setLongitude(coordinate.longitude() - (dx / pow(2.0, cameraStart.zoomFactor())));
+ coordinate.setLatitude(coordinate.latitude() + (dy / pow(2.0, cameraStart.zoomFactor())));
+ cameraEnd.setCenter(coordinate);
+
+ animation_->setStartValue(QVariant::fromValue(cameraStart));
+ animation_->setEndValue(QVariant::fromValue(cameraEnd));
+
+ //animation_->setStartValue(QVariant::fromValue(cameraStart.center()));
+ //animation_->setEndValue(QVariant::fromValue(cameraEnd.center()));
+
+ qDebug() << "The latitude will go from:" << cameraStart.center().latitude() << "to:" << cameraEnd.center().latitude();
+ qDebug() << "The longitude will go from:" << cameraStart.center().longitude() << "to:" << cameraEnd.center().longitude();
+ QTimer::singleShot(0, animation_, SLOT(start()));
+ flicking_ = true;
+ emit flickStarted();
+ }
+}
+
+// Adds velocity sample to sample buffer. Data is later used to calculate
+// flick speed. By default 3 latest samples are considered.
+void QDeclarativeGeoMapFlickable::addVelocitySample(QVector<qreal>& buffer, qreal sample)
+{
+ if (sample > maxVelocity_)
+ sample = maxVelocity_;
+ else if (sample < -maxVelocity_)
+ sample = -maxVelocity_;
+ buffer.append(sample);
+ if (buffer.count() > QML_FLICK_SAMPLEBUFFER)
+ buffer.remove(0);
+}
+
+void QDeclarativeGeoMapFlickable::updateVelocity(QVector<qreal>& buffer, qreal& velocity)
+{
+ if (buffer.count() > QML_FLICK_DISCARDSAMPLES) {
+ velocity = 0;
+ int count = buffer.count() - QML_FLICK_DISCARDSAMPLES;
+ for (int i = 0; i < count; ++i) {
+ qreal v = buffer.at(i);
+ velocity += v;
+ }
+ velocity /= count;
+ }
+}
+
+
+void QDeclarativeGeoMapFlickable::setEnabled(bool enabled)
+{
+ if (enabled_ == enabled)
+ return;
+ enabled_ = enabled;
+ if (!enabled_)
+ stop();
+ emit enabledChanged();
+}
+
+bool QDeclarativeGeoMapFlickable::enabled() const
+{
+ return enabled_;
+}
+
+void QDeclarativeGeoMapFlickable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
+{
+ if (!pressed_ || !enabled_)
+ return;
+ pressed_ = false;
+
+ // if we drag then pause before release we should not cause a flick.
+ if (lastPosTime_.elapsed() < 100) {
+ updateVelocity(velocityBufferY_, velocityY_);
+ updateVelocity(velocityBufferX_, velocityX_);
+ } else {
+ velocityX_ = 0.0;
+ velocityY_ = 0.0;
+ }
+ int flickTimeY = 0;
+ int flickTimeX = 0;
+ int flickPixelsX = 0;
+ int flickPixelsY = 0;
+ if (qAbs(velocityY_) > MinimumFlickVelocity && qAbs(event->pos().y() - pressPos_.y()) > FlickThreshold) {
+ // calculate Y flick animation values
+ qreal acceleration = deceleration_;
+ if ((velocityY_ > 0.0f) == (deceleration_ > 0.0f))
+ acceleration = acceleration * -1.0f;
+ flickTimeY = static_cast<int>(-1000 * velocityY_ / acceleration);
+ flickPixelsY = (flickTimeY * velocityY_) / (1000.0 * 2);
+ //qDebug() << "---=== would flick Y, velocity (pix/sec), flick duration (msec): ===---" << velocityY_ << flickTimeY;
+ } else {
+ // reset
+ //qDebug() << "---=== would NOT flick Y, velocity (pix/sec): ===---" << velocityY_;
+ }
+ if (qAbs(velocityX_) > MinimumFlickVelocity && qAbs(event->pos().x() - pressPos_.x()) > FlickThreshold) {
+ // calculate X flick animation values
+ qreal acceleration = deceleration_;
+ if ((velocityX_ > 0.0f) == (deceleration_ > 0.0f))
+ acceleration = acceleration * -1.0f;
+ flickTimeX = static_cast<int>(-1000 * velocityX_ / acceleration);
+ flickPixelsX = (flickTimeX * velocityX_) / (1000.0 * 2);
+ //qDebug() << "---=== would flick X, velocity (pix/sec), flick duration (msec), pixels: ===---" << velocityX_ << flickTimeX << "pixels: " << flickPixelsX;
+ } else {
+ // reset
+ //qDebug() << "---=== would NOT flick X, velocity (pix/sec) ===---" << velocityX_;
+ }
+ int flickTime = qMax(flickTimeY, flickTimeX);
+ updateCamera(flickPixelsX, flickPixelsY, flickTime);
+
+ if (flickTime == 0 && moving_) {
+ emit movementEnded();
+ moving_ = false;
+ }
+ velocityBufferX_.clear();
+ velocityBufferY_.clear();
+ velocityX_ = 0.0;
+ velocityY_ = 0.0;
+ lastPosTime_.invalidate();
+ pressTime_.invalidate();
+ velocityTime_.invalidate();
+}
+
+void QDeclarativeGeoMapFlickable::flickAnimationFinished()
+{
+ //qDebug() << metaObject()->className() << __FUNCTION__;
+ //Q_ASSERT(flicking_);
+ //Q_ASSERT(moving_);
+ flicking_ = false;
+ moving_ = false;
+ emit flickEnded();
+ emit movementEnded();
+}
+
+/*
+void QDeclarativeGeoMapFlickable::flickAnimationValueChanged(const QVariant& value)
+{
+ qDebug() << metaObject()->className() << __FUNCTION__;
+}
+*/
+
+void QDeclarativeGeoMapFlickable::timerEvent(QTimerEvent *event)
+{
+ Q_UNUSED(event); // TODO press delay handling
+ //qDebug() << metaObject()->className() << __FUNCTION__ ;
+}
+
+#include "moc_qdeclarativegeomapflickable_p.cpp"
+
+QT_END_NAMESPACE