2 Situare - A location system for Facebook
3 Copyright (C) 2010 Ixonos Plc. Authors:
5 Sami Rämö - sami.ramo@ixonos.com
6 Pekka Nissinen - pekka.nissinen@ixonos.com
8 Situare is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 version 2 as published by the Free Software Foundation.
12 Situare is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Situare; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
26 #include <QMouseEvent>
27 #include <QParallelAnimationGroup>
29 #include "coordinates/scenecoordinate.h"
30 #include "mapcommon.h"
31 #include "mapscroller.h"
35 const qreal MS_PER_S = 1000;
37 // const values for tuning the kinetic scroll effect
38 const int KINETIC_MIN_DRAG_LENGTH_VIEW_PIXELS = 30;
39 const int KINETIC_MAX_TIME_FROM_LAST_MOUSE_EVENT_MS = 100;
40 const qreal KINETIC_MAX_VIEW_DISTANCE_FACTOR = 0.8;
41 const int KINETIC_SCROLL_TIME_MS = 750;
42 const qreal KINETIC_SPEED_TO_DISTANCE_FACTOR = 0.15 * sqrt(KINETIC_SCROLL_TIME_MS / MS_PER_S);
44 const qreal ZOOM_TIME_MS = 350; ///< Length of the zoom effect (ms)
46 MapView::MapView(QWidget *parent)
47 : QGraphicsView(parent),
48 m_doubleTapZoomRunning(false)
50 qDebug() << __PRETTY_FUNCTION__;
52 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
53 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
55 m_zoomAnimation = new QPropertyAnimation(this, "viewScale", this);
56 connect(m_zoomAnimation, SIGNAL(finished()),
57 this, SIGNAL(viewZoomFinished()));
58 setOptimizationFlag(QGraphicsView::DontAdjustForAntialiasing);
60 m_scroller = &MapScroller::getInstance();
62 m_scrollAndZoomAnimation = new QParallelAnimationGroup();
63 m_scrollAndZoomAnimation->addAnimation(m_scroller);
64 m_scrollAndZoomAnimation->addAnimation(m_zoomAnimation);
65 connect(m_scrollAndZoomAnimation, SIGNAL(finished()),
66 this, SLOT(doubleTapZoomFinished()));
68 m_centerShiftAnimation = new QPropertyAnimation(this, "viewShift", this);
69 if (m_centerShiftAnimation) {
70 m_centerShiftAnimation->setStartValue(0.0);
71 m_centerShiftAnimation->setDuration(ZOOM_TIME_MS);
77 qDebug() << __PRETTY_FUNCTION__;
79 m_scrollAndZoomAnimation->removeAnimation(m_scroller);
80 delete m_scrollAndZoomAnimation;
83 void MapView::centerToSceneCoordinates(const SceneCoordinate &coordinate, bool isUserDragAction)
85 qDebug() << __PRETTY_FUNCTION__ << "coordinate" << coordinate;
87 QPointF target = coordinate.toPointF();
88 m_lastSetScenePosition = coordinate;
90 if (!isUserDragAction)
91 target += m_centerHorizontalShiftPoint;
96 void MapView::disableCenterShift()
98 qDebug() << __PRETTY_FUNCTION__;
100 if (m_centerShiftAnimation) {
101 m_centerShiftAnimation->setDirection(QAbstractAnimation::Backward);
102 m_centerShiftAnimation->start();
106 void MapView::doubleTapZoomFinished()
108 qDebug() << __PRETTY_FUNCTION__;
110 m_doubleTapZoomRunning = false;
114 void MapView::enableCenterShift()
116 qDebug() << __PRETTY_FUNCTION__;
118 if (m_centerShiftAnimation) {
119 m_centerShiftAnimation->setDirection(QAbstractAnimation::Forward);
120 m_centerShiftAnimation->start();
124 void MapView::mouseDoubleClickEvent(QMouseEvent *event)
126 qDebug() << __PRETTY_FUNCTION__;
128 if (m_zoomLevel + 1 <= OSM_MAX_ZOOM_LEVEL) {
129 QPoint pressPosition = mapToScene(event->pos()).toPoint();
130 QPoint viewCenterPosition = mapToScene(width() / 2 - 1, height() / 2 - 1).toPoint();
131 QPoint zoomPosition = viewCenterPosition - ((viewCenterPosition - pressPosition) / 2);
133 m_scrollAndZoomAnimation->stop();
134 m_doubleTapZoomRunning = true;
136 m_scroller->setEasingCurve(QEasingCurve::Linear);
137 m_scroller->setDuration(ZOOM_TIME_MS);
138 m_scroller->setStartValue(SceneCoordinate(m_internalScenePosition.x(),
139 m_internalScenePosition.y()));
140 m_scroller->setEndValue(SceneCoordinate(zoomPosition.x(), zoomPosition.y()));
142 m_zoomAnimation->setEasingCurve(QEasingCurve::InQuad);
143 m_zoomAnimation->setDuration(ZOOM_TIME_MS);
144 m_zoomAnimation->setStartValue(viewScale());
145 m_zoomAnimation->setEndValue(pow(2, m_zoomLevel+1 - OSM_MAX_ZOOM_LEVEL));
147 m_scrollAndZoomAnimation->start();
151 void MapView::mouseMoveEvent(QMouseEvent *event)
153 qDebug() << __PRETTY_FUNCTION__;
155 if (m_doubleTapZoomRunning)
158 m_internalScenePosition += m_lastMouseEventScenePosition - mapToScene(event->pos()).toPoint();
160 if (m_index >= VALUES)
163 m_dragMovement[m_index] = m_lastMouseEventViewPosition - event->pos();
164 m_dragTime[m_index] = m_time.elapsed();
168 emit viewScrolled(SceneCoordinate(m_internalScenePosition.x(), m_internalScenePosition.y()),
171 m_lastMouseEventScenePosition = mapToScene(event->pos()).toPoint();
172 m_lastMouseEventViewPosition = event->pos();
175 void MapView::mousePressEvent(QMouseEvent *event)
177 qDebug() << __PRETTY_FUNCTION__;
179 if (m_doubleTapZoomRunning)
186 QGraphicsView::mousePressEvent(event);
188 m_lastMouseEventScenePosition = mapToScene(event->pos()).toPoint();
189 m_lastMouseEventViewPosition = event->pos();
190 m_internalScenePosition = mapToScene(width() / 2 - 1, height() / 2 - 1).toPoint();
192 for (int i = 0; i < VALUES; i++) {
193 m_dragMovement[i] = QPoint();
199 void MapView::mouseReleaseEvent(QMouseEvent *event)
201 qDebug() << __PRETTY_FUNCTION__;
203 if (m_doubleTapZoomRunning)
206 int elapsed = m_time.elapsed();
208 QGraphicsView::mouseReleaseEvent(event);
210 // start kinetic scroll only if there isn't too much time elapsed from the last mouse move event
211 if (elapsed <= KINETIC_MAX_TIME_FROM_LAST_MOUSE_EVENT_MS) {
212 QPointF dragViewSpeed;
215 for (int i = 0; i < VALUES; i++) {
216 if (m_dragTime[i] > 0) {
217 dragViewSpeed += m_dragMovement[i] / (m_dragTime[i] / MS_PER_S);
218 dragLength += m_dragMovement[i].manhattanLength();
223 if (dragLength >= KINETIC_MIN_DRAG_LENGTH_VIEW_PIXELS) {
224 dragViewSpeed /= values;
225 QPointF effectViewDistance = dragViewSpeed * KINETIC_SPEED_TO_DISTANCE_FACTOR;
227 // limit the scroll distance in screen pixels
228 qreal biggerDistance = qMax(abs(effectViewDistance.x()), abs(effectViewDistance.y()));
229 if (biggerDistance > m_kineticMaxViewDistance)
230 effectViewDistance /= biggerDistance / m_kineticMaxViewDistance;
232 QPointF effectSceneDistance = effectViewDistance
233 * (1 << (OSM_MAX_ZOOM_LEVEL - m_zoomLevel));
235 m_scroller->setEasingCurve(QEasingCurve::OutCirc);
236 m_scroller->setDuration(KINETIC_SCROLL_TIME_MS);
237 m_scroller->setStartValue(SceneCoordinate(m_internalScenePosition.x(),
238 m_internalScenePosition.y()));
239 QPointF endValue = QPointF(m_internalScenePosition) + effectSceneDistance;
240 m_scroller->setEndValue(SceneCoordinate(endValue.x(), endValue.y()));
241 m_scroller->setKineticScrollFlag(true);
247 void MapView::resizeEvent(QResizeEvent *event)
249 qDebug() << __PRETTY_FUNCTION__ << "Resize:" << event->size();
251 m_kineticMaxViewDistance = qMax(width(), height()) * KINETIC_MAX_VIEW_DISTANCE_FACTOR;
253 emit viewResized(event->size());
255 if (m_centerShiftAnimation) {
256 m_centerShiftAnimation->setEndValue(event->size().width() / 4);
261 void MapView::setViewScale(qreal viewScale)
263 qDebug() << __PRETTY_FUNCTION__;
265 QTransform transform;
266 transform.scale(viewScale, viewScale);
267 setTransform(transform);
272 void MapView::setViewShift(qreal viewShift)
274 qDebug() << __PRETTY_FUNCTION__;
276 m_centerHorizontalShiftViewPixels = viewShift;
277 emit horizontalShiftingChanged(m_centerHorizontalShiftViewPixels);
282 void MapView::setZoomLevel(int zoomLevel)
284 qDebug() << __PRETTY_FUNCTION__;
286 m_zoomLevel = zoomLevel;
288 if (m_zoomAnimation) {
289 m_zoomAnimation->stop();
290 m_zoomAnimation->setEasingCurve(QEasingCurve::InQuad);
291 m_zoomAnimation->setDuration(ZOOM_TIME_MS);
292 m_zoomAnimation->setStartValue(viewScale());
293 m_zoomAnimation->setEndValue(pow(2, zoomLevel - OSM_MAX_ZOOM_LEVEL));
295 m_zoomAnimation->start();
299 void MapView::updateCenterShift()
301 qDebug() << __PRETTY_FUNCTION__;
303 m_centerHorizontalShiftPoint = QPointF(m_centerHorizontalShiftViewPixels * (1.0 / viewScale()),
306 centerToSceneCoordinates(m_lastSetScenePosition);
309 qreal MapView::viewScale() const
311 qDebug() << __PRETTY_FUNCTION__;
313 return transform().m11();
316 qreal MapView::viewShift() const
318 qDebug() << __PRETTY_FUNCTION__;
320 return m_centerHorizontalShiftViewPixels;