Update licenseheader text in source files for qtdeclarative Qt module
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsgflickable.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 QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgflickable_p.h"
43 #include "qsgflickable_p_p.h"
44 #include "qsgcanvas.h"
45 #include "qsgcanvas_p.h"
46
47 #include <QtDeclarative/qdeclarativeinfo.h>
48 #include <QtGui/qgraphicssceneevent.h>
49 #include <QtGui/qapplication.h>
50 #include "qplatformdefs.h"
51
52 QT_BEGIN_NAMESPACE
53
54 // The maximum number of pixels a flick can overshoot
55 #ifndef QML_FLICK_OVERSHOOT
56 #define QML_FLICK_OVERSHOOT 200
57 #endif
58
59 // The number of samples to use in calculating the velocity of a flick
60 #ifndef QML_FLICK_SAMPLEBUFFER
61 #define QML_FLICK_SAMPLEBUFFER 3
62 #endif
63
64 // The number of samples to discard when calculating the flick velocity.
65 // Touch panels often produce inaccurate results as the finger is lifted.
66 #ifndef QML_FLICK_DISCARDSAMPLES
67 #define QML_FLICK_DISCARDSAMPLES 1
68 #endif
69
70 // The default maximum velocity of a flick.
71 #ifndef QML_FLICK_DEFAULTMAXVELOCITY
72 #define QML_FLICK_DEFAULTMAXVELOCITY 2500
73 #endif
74
75 // The default deceleration of a flick.
76 #ifndef QML_FLICK_DEFAULTDECELERATION
77 #define QML_FLICK_DEFAULTDECELERATION 1500
78 #endif
79
80 // How much faster to decelerate when overshooting
81 #ifndef QML_FLICK_OVERSHOOTFRICTION
82 #define QML_FLICK_OVERSHOOTFRICTION 8
83 #endif
84
85 // FlickThreshold determines how far the "mouse" must have moved
86 // before we perform a flick.
87 static const int FlickThreshold = 20;
88
89 // RetainGrabVelocity is the maxmimum instantaneous velocity that
90 // will ensure the Flickable retains the grab on consecutive flicks.
91 static const int RetainGrabVelocity = 15;
92
93 QSGFlickableVisibleArea::QSGFlickableVisibleArea(QSGFlickable *parent)
94     : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
95     , m_yPosition(0.), m_heightRatio(0.)
96 {
97 }
98
99 qreal QSGFlickableVisibleArea::widthRatio() const
100 {
101     return m_widthRatio;
102 }
103
104 qreal QSGFlickableVisibleArea::xPosition() const
105 {
106     return m_xPosition;
107 }
108
109 qreal QSGFlickableVisibleArea::heightRatio() const
110 {
111     return m_heightRatio;
112 }
113
114 qreal QSGFlickableVisibleArea::yPosition() const
115 {
116     return m_yPosition;
117 }
118
119 void QSGFlickableVisibleArea::updateVisible()
120 {
121     QSGFlickablePrivate *p = QSGFlickablePrivate::get(flickable);
122
123     bool changeX = false;
124     bool changeY = false;
125     bool changeWidth = false;
126     bool changeHeight = false;
127
128     // Vertical
129     const qreal viewheight = flickable->height();
130     const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent();
131     qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight);
132     qreal pageSize = viewheight / (maxyextent + viewheight);
133
134     if (pageSize != m_heightRatio) {
135         m_heightRatio = pageSize;
136         changeHeight = true;
137     }
138     if (pagePos != m_yPosition) {
139         m_yPosition = pagePos;
140         changeY = true;
141     }
142
143     // Horizontal
144     const qreal viewwidth = flickable->width();
145     const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
146     pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth);
147     pageSize = viewwidth / (maxxextent + viewwidth);
148
149     if (pageSize != m_widthRatio) {
150         m_widthRatio = pageSize;
151         changeWidth = true;
152     }
153     if (pagePos != m_xPosition) {
154         m_xPosition = pagePos;
155         changeX = true;
156     }
157
158     if (changeX)
159         emit xPositionChanged(m_xPosition);
160     if (changeY)
161         emit yPositionChanged(m_yPosition);
162     if (changeWidth)
163         emit widthRatioChanged(m_widthRatio);
164     if (changeHeight)
165         emit heightRatioChanged(m_heightRatio);
166 }
167
168
169 QSGFlickablePrivate::QSGFlickablePrivate()
170   : contentItem(new QSGItem)
171     , hData(this, &QSGFlickablePrivate::setViewportX)
172     , vData(this, &QSGFlickablePrivate::setViewportY)
173     , flickingHorizontally(false), flickingVertically(false)
174     , hMoved(false), vMoved(false)
175     , movingHorizontally(false), movingVertically(false)
176     , stealMouse(false), pressed(false), interactive(true), calcVelocity(false)
177     , deceleration(QML_FLICK_DEFAULTDECELERATION)
178     , maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100)
179     , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(400)
180     , fixupMode(Normal), vTime(0), visibleArea(0)
181     , flickableDirection(QSGFlickable::AutoFlickDirection)
182     , boundsBehavior(QSGFlickable::DragAndOvershootBounds)
183 {
184 }
185
186 void QSGFlickablePrivate::init()
187 {
188     Q_Q(QSGFlickable);
189     QDeclarative_setParent_noEvent(contentItem, q);
190     contentItem->setParentItem(q);
191     static int timelineUpdatedIdx = -1;
192     static int timelineCompletedIdx = -1;
193     static int flickableTickedIdx = -1;
194     static int flickableMovementEndingIdx = -1;
195     if (timelineUpdatedIdx == -1) {
196         timelineUpdatedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("updated()");
197         timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()");
198         flickableTickedIdx = QSGFlickable::staticMetaObject.indexOfSlot("ticked()");
199         flickableMovementEndingIdx = QSGFlickable::staticMetaObject.indexOfSlot("movementEnding()");
200     }
201     QMetaObject::connect(&timeline, timelineUpdatedIdx,
202                          q, flickableTickedIdx, Qt::DirectConnection);
203     QMetaObject::connect(&timeline, timelineCompletedIdx,
204                          q, flickableMovementEndingIdx, Qt::DirectConnection);
205     q->setAcceptedMouseButtons(Qt::LeftButton);
206     q->setFiltersChildMouseEvents(true);
207     QSGItemPrivate *viewportPrivate = QSGItemPrivate::get(contentItem);
208     viewportPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
209     lastPosTime.invalidate();
210 }
211
212 /*
213     Returns the amount to overshoot by given a velocity.
214     Will be roughly in range 0 - size/4
215 */
216 qreal QSGFlickablePrivate::overShootDistance(qreal size)
217 {
218     if (maxVelocity <= 0)
219         return 0.0;
220
221     return qMin(qreal(QML_FLICK_OVERSHOOT), size/3);
222 }
223
224 void QSGFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity)
225 {
226     if (v > maxVelocity)
227         v = maxVelocity;
228     else if (v < -maxVelocity)
229         v = -maxVelocity;
230     velocityBuffer.append(v);
231     if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER)
232         velocityBuffer.remove(0);
233 }
234
235 void QSGFlickablePrivate::AxisData::updateVelocity()
236 {
237     if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) {
238         velocity = 0;
239         int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES;
240         for (int i = 0; i < count; ++i) {
241             qreal v = velocityBuffer.at(i);
242             velocity += v;
243         }
244         velocity /= count;
245     }
246 }
247
248 void QSGFlickablePrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeom, const QRectF &oldGeom)
249 {
250     Q_Q(QSGFlickable);
251     if (item == contentItem) {
252         if (newGeom.x() != oldGeom.x())
253             emit q->contentXChanged();
254         if (newGeom.y() != oldGeom.y())
255             emit q->contentYChanged();
256     }
257 }
258
259 void QSGFlickablePrivate::flickX(qreal velocity)
260 {
261     Q_Q(QSGFlickable);
262     flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity);
263 }
264
265 void QSGFlickablePrivate::flickY(qreal velocity)
266 {
267     Q_Q(QSGFlickable);
268     flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity);
269 }
270
271 void QSGFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal,
272                                          QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
273 {
274     Q_Q(QSGFlickable);
275     qreal maxDistance = -1;
276     data.fixingUp = false;
277     // -ve velocity means list is moving up
278     if (velocity > 0) {
279         maxDistance = qAbs(minExtent - data.move.value());
280         data.flickTarget = minExtent;
281     } else {
282         maxDistance = qAbs(maxExtent - data.move.value());
283         data.flickTarget = maxExtent;
284     }
285     if (maxDistance > 0) {
286         qreal v = velocity;
287         if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
288             if (v < 0)
289                 v = -maxVelocity;
290             else
291                 v = maxVelocity;
292         }
293         timeline.reset(data.move);
294         if (boundsBehavior == QSGFlickable::DragAndOvershootBounds)
295             timeline.accel(data.move, v, deceleration);
296         else
297             timeline.accel(data.move, v, deceleration, maxDistance);
298         timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
299         if (!flickingHorizontally && q->xflick()) {
300             flickingHorizontally = true;
301             emit q->flickingChanged();
302             emit q->flickingHorizontallyChanged();
303             if (!flickingVertically)
304                 emit q->flickStarted();
305         }
306         if (!flickingVertically && q->yflick()) {
307             flickingVertically = true;
308             emit q->flickingChanged();
309             emit q->flickingVerticallyChanged();
310             if (!flickingHorizontally)
311                 emit q->flickStarted();
312         }
313     } else {
314         timeline.reset(data.move);
315         fixup(data, minExtent, maxExtent);
316     }
317 }
318
319 void QSGFlickablePrivate::fixupY_callback(void *data)
320 {
321     ((QSGFlickablePrivate *)data)->fixupY();
322 }
323
324 void QSGFlickablePrivate::fixupX_callback(void *data)
325 {
326     ((QSGFlickablePrivate *)data)->fixupX();
327 }
328
329 void QSGFlickablePrivate::fixupX()
330 {
331     Q_Q(QSGFlickable);
332     fixup(hData, q->minXExtent(), q->maxXExtent());
333 }
334
335 void QSGFlickablePrivate::fixupY()
336 {
337     Q_Q(QSGFlickable);
338     fixup(vData, q->minYExtent(), q->maxYExtent());
339 }
340
341 void QSGFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
342 {
343     if (data.move.value() > minExtent || maxExtent > minExtent) {
344         timeline.reset(data.move);
345         if (data.move.value() != minExtent) {
346             switch (fixupMode) {
347             case Immediate:
348                 timeline.set(data.move, minExtent);
349                 break;
350             case ExtentChanged:
351                 // The target has changed. Don't start from the beginning; just complete the
352                 // second half of the animation using the new extent.
353                 timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
354                 data.fixingUp = true;
355                 break;
356             default: {
357                     qreal dist = minExtent - data.move;
358                     timeline.move(data.move, minExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
359                     timeline.move(data.move, minExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
360                     data.fixingUp = true;
361                 }
362             }
363         }
364     } else if (data.move.value() < maxExtent) {
365         timeline.reset(data.move);
366         switch (fixupMode) {
367         case Immediate:
368             timeline.set(data.move, maxExtent);
369             break;
370         case ExtentChanged:
371             // The target has changed. Don't start from the beginning; just complete the
372             // second half of the animation using the new extent.
373             timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
374             data.fixingUp = true;
375             break;
376         default: {
377                 qreal dist = maxExtent - data.move;
378                 timeline.move(data.move, maxExtent - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
379                 timeline.move(data.move, maxExtent, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
380                 data.fixingUp = true;
381             }
382         }
383     }
384     data.inOvershoot = false;
385     fixupMode = Normal;
386     vTime = timeline.time();
387 }
388
389 void QSGFlickablePrivate::updateBeginningEnd()
390 {
391     Q_Q(QSGFlickable);
392     bool atBoundaryChange = false;
393
394     // Vertical
395     const int maxyextent = int(-q->maxYExtent());
396     const qreal ypos = -vData.move.value();
397     bool atBeginning = (ypos <= -q->minYExtent());
398     bool atEnd = (maxyextent <= ypos);
399
400     if (atBeginning != vData.atBeginning) {
401         vData.atBeginning = atBeginning;
402         atBoundaryChange = true;
403     }
404     if (atEnd != vData.atEnd) {
405         vData.atEnd = atEnd;
406         atBoundaryChange = true;
407     }
408
409     // Horizontal
410     const int maxxextent = int(-q->maxXExtent());
411     const qreal xpos = -hData.move.value();
412     atBeginning = (xpos <= -q->minXExtent());
413     atEnd = (maxxextent <= xpos);
414
415     if (atBeginning != hData.atBeginning) {
416         hData.atBeginning = atBeginning;
417         atBoundaryChange = true;
418     }
419     if (atEnd != hData.atEnd) {
420         hData.atEnd = atEnd;
421         atBoundaryChange = true;
422     }
423
424     if (atBoundaryChange)
425         emit q->isAtBoundaryChanged();
426
427     if (visibleArea)
428         visibleArea->updateVisible();
429 }
430
431 QSGFlickable::QSGFlickable(QSGItem *parent)
432   : QSGItem(*(new QSGFlickablePrivate), parent)
433 {
434     Q_D(QSGFlickable);
435     d->init();
436 }
437
438 QSGFlickable::QSGFlickable(QSGFlickablePrivate &dd, QSGItem *parent)
439   : QSGItem(dd, parent)
440 {
441     Q_D(QSGFlickable);
442     d->init();
443 }
444
445 QSGFlickable::~QSGFlickable()
446 {
447 }
448
449 qreal QSGFlickable::contentX() const
450 {
451     Q_D(const QSGFlickable);
452     return -d->contentItem->x();
453 }
454
455 void QSGFlickable::setContentX(qreal pos)
456 {
457     Q_D(QSGFlickable);
458     d->timeline.reset(d->hData.move);
459     d->vTime = d->timeline.time();
460     movementXEnding();
461     if (-pos != d->hData.move.value()) {
462         d->hData.move.setValue(-pos);
463         viewportMoved();
464     }
465 }
466
467 qreal QSGFlickable::contentY() const
468 {
469     Q_D(const QSGFlickable);
470     return -d->contentItem->y();
471 }
472
473 void QSGFlickable::setContentY(qreal pos)
474 {
475     Q_D(QSGFlickable);
476     d->timeline.reset(d->vData.move);
477     d->vTime = d->timeline.time();
478     movementYEnding();
479     if (-pos != d->vData.move.value()) {
480         d->vData.move.setValue(-pos);
481         viewportMoved();
482     }
483 }
484
485 bool QSGFlickable::isInteractive() const
486 {
487     Q_D(const QSGFlickable);
488     return d->interactive;
489 }
490
491 void QSGFlickable::setInteractive(bool interactive)
492 {
493     Q_D(QSGFlickable);
494     if (interactive != d->interactive) {
495         d->interactive = interactive;
496         if (!interactive && (d->flickingHorizontally || d->flickingVertically)) {
497             d->timeline.clear();
498             d->vTime = d->timeline.time();
499             d->flickingHorizontally = false;
500             d->flickingVertically = false;
501             emit flickingChanged();
502             emit flickingHorizontallyChanged();
503             emit flickingVerticallyChanged();
504             emit flickEnded();
505         }
506         emit interactiveChanged();
507     }
508 }
509
510 qreal QSGFlickable::horizontalVelocity() const
511 {
512     Q_D(const QSGFlickable);
513     return d->hData.smoothVelocity.value();
514 }
515
516 qreal QSGFlickable::verticalVelocity() const
517 {
518     Q_D(const QSGFlickable);
519     return d->vData.smoothVelocity.value();
520 }
521
522 bool QSGFlickable::isAtXEnd() const
523 {
524     Q_D(const QSGFlickable);
525     return d->hData.atEnd;
526 }
527
528 bool QSGFlickable::isAtXBeginning() const
529 {
530     Q_D(const QSGFlickable);
531     return d->hData.atBeginning;
532 }
533
534 bool QSGFlickable::isAtYEnd() const
535 {
536     Q_D(const QSGFlickable);
537     return d->vData.atEnd;
538 }
539
540 bool QSGFlickable::isAtYBeginning() const
541 {
542     Q_D(const QSGFlickable);
543     return d->vData.atBeginning;
544 }
545
546 void QSGFlickable::ticked()
547 {
548     viewportMoved();
549 }
550
551 QSGItem *QSGFlickable::contentItem()
552 {
553     Q_D(QSGFlickable);
554     return d->contentItem;
555 }
556
557 QSGFlickableVisibleArea *QSGFlickable::visibleArea()
558 {
559     Q_D(QSGFlickable);
560     if (!d->visibleArea)
561         d->visibleArea = new QSGFlickableVisibleArea(this);
562     return d->visibleArea;
563 }
564
565 QSGFlickable::FlickableDirection QSGFlickable::flickableDirection() const
566 {
567     Q_D(const QSGFlickable);
568     return d->flickableDirection;
569 }
570
571 void QSGFlickable::setFlickableDirection(FlickableDirection direction)
572 {
573     Q_D(QSGFlickable);
574     if (direction != d->flickableDirection) {
575         d->flickableDirection = direction;
576         emit flickableDirectionChanged();
577     }
578 }
579
580 void QSGFlickablePrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event)
581 {
582     Q_Q(QSGFlickable);
583     if (interactive && timeline.isActive()
584         && (qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity
585             || qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity)) {
586         stealMouse = true; // If we've been flicked then steal the click.
587     } else {
588         stealMouse = false;
589     }
590     q->setKeepMouseGrab(stealMouse);
591     pressed = true;
592     timeline.clear();
593     hData.reset();
594     vData.reset();
595     hData.dragMinBound = q->minXExtent();
596     vData.dragMinBound = q->minYExtent();
597     hData.dragMaxBound = q->maxXExtent();
598     vData.dragMaxBound = q->maxYExtent();
599     fixupMode = Normal;
600     lastPos = QPoint();
601     QSGItemPrivate::start(lastPosTime);
602     pressPos = event->pos();
603     hData.pressPos = hData.move.value();
604     vData.pressPos = vData.move.value();
605     flickingHorizontally = false;
606     flickingVertically = false;
607     QSGItemPrivate::start(pressTime);
608     QSGItemPrivate::start(velocityTime);
609 }
610
611 void QSGFlickablePrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
612 {
613     Q_Q(QSGFlickable);
614     if (!interactive || !lastPosTime.isValid())
615         return;
616     bool rejectY = false;
617     bool rejectX = false;
618
619     bool stealY = stealMouse;
620     bool stealX = stealMouse;
621
622     if (q->yflick()) {
623         int dy = int(event->pos().y() - pressPos.y());
624         if (qAbs(dy) > QApplication::startDragDistance() || QSGItemPrivate::elapsed(pressTime) > 200) {
625             if (!vMoved)
626                 vData.dragStartOffset = dy;
627             qreal newY = dy + vData.pressPos - vData.dragStartOffset;
628             const qreal minY = vData.dragMinBound;
629             const qreal maxY = vData.dragMaxBound;
630             if (newY > minY)
631                 newY = minY + (newY - minY) / 2;
632             if (newY < maxY && maxY - minY <= 0)
633                 newY = maxY + (newY - maxY) / 2;
634             if (boundsBehavior == QSGFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
635                 rejectY = true;
636                 if (newY < maxY) {
637                     newY = maxY;
638                     rejectY = false;
639                 }
640                 if (newY > minY) {
641                     newY = minY;
642                     rejectY = false;
643                 }
644             }
645             if (!rejectY && stealMouse) {
646                 vData.move.setValue(qRound(newY));
647                 vMoved = true;
648             }
649             if (qAbs(dy) > QApplication::startDragDistance())
650                 stealY = true;
651         }
652     }
653
654     if (q->xflick()) {
655         int dx = int(event->pos().x() - pressPos.x());
656         if (qAbs(dx) > QApplication::startDragDistance() || QSGItemPrivate::elapsed(pressTime) > 200) {
657             if (!hMoved)
658                 hData.dragStartOffset = dx;
659             qreal newX = dx + hData.pressPos - hData.dragStartOffset;
660             const qreal minX = hData.dragMinBound;
661             const qreal maxX = hData.dragMaxBound;
662             if (newX > minX)
663                 newX = minX + (newX - minX) / 2;
664             if (newX < maxX && maxX - minX <= 0)
665                 newX = maxX + (newX - maxX) / 2;
666             if (boundsBehavior == QSGFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
667                 rejectX = true;
668                 if (newX < maxX) {
669                     newX = maxX;
670                     rejectX = false;
671                 }
672                 if (newX > minX) {
673                     newX = minX;
674                     rejectX = false;
675                 }
676             }
677             if (!rejectX && stealMouse) {
678                 hData.move.setValue(qRound(newX));
679                 hMoved = true;
680             }
681
682             if (qAbs(dx) > QApplication::startDragDistance())
683                 stealX = true;
684         }
685     }
686
687     stealMouse = stealX || stealY;
688     if (stealMouse)
689         q->setKeepMouseGrab(true);
690
691     if (rejectY) {
692         vData.velocityBuffer.clear();
693         vData.velocity = 0;
694     }
695     if (rejectX) {
696         hData.velocityBuffer.clear();
697         hData.velocity = 0;
698     }
699
700     if (hMoved || vMoved) {
701         q->movementStarting();
702         q->viewportMoved();
703     }
704
705     if (!lastPos.isNull()) {
706         qreal elapsed = qreal(QSGItemPrivate::elapsed(lastPosTime)) / 1000.;
707         if (elapsed <= 0)
708             return;
709         QSGItemPrivate::restart(lastPosTime);
710         qreal dy = event->pos().y()-lastPos.y();
711         if (q->yflick() && !rejectY)
712             vData.addVelocitySample(dy/elapsed, maxVelocity);
713         qreal dx = event->pos().x()-lastPos.x();
714         if (q->xflick() && !rejectX)
715             hData.addVelocitySample(dx/elapsed, maxVelocity);
716     }
717
718     lastPos = event->pos();
719 }
720
721 void QSGFlickablePrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *event)
722 {
723     Q_Q(QSGFlickable);
724     stealMouse = false;
725     q->setKeepMouseGrab(false);
726     pressed = false;
727     if (!lastPosTime.isValid())
728         return;
729
730     // if we drag then pause before release we should not cause a flick.
731     if (QSGItemPrivate::elapsed(lastPosTime) < 100) {
732         vData.updateVelocity();
733         hData.updateVelocity();
734     } else {
735         hData.velocity = 0.0;
736         vData.velocity = 0.0;
737     }
738
739     vTime = timeline.time();
740
741     qreal velocity = vData.velocity;
742     if (vData.atBeginning || vData.atEnd)
743         velocity /= 2;
744     if (qAbs(velocity) > MinimumFlickVelocity && qAbs(event->pos().y() - pressPos.y()) > FlickThreshold)
745         flickY(velocity);
746     else
747         fixupY();
748
749     velocity = hData.velocity;
750     if (hData.atBeginning || hData.atEnd)
751         velocity /= 2;
752     if (qAbs(velocity) > MinimumFlickVelocity && qAbs(event->pos().x() - pressPos.x()) > FlickThreshold)
753         flickX(velocity);
754     else
755         fixupX();
756
757     if (!timeline.isActive())
758         q->movementEnding();
759 }
760
761 void QSGFlickable::mousePressEvent(QGraphicsSceneMouseEvent *event)
762 {
763     Q_D(QSGFlickable);
764     if (d->interactive) {
765         if (!d->pressed)
766             d->handleMousePressEvent(event);
767         event->accept();
768     } else {
769         QSGItem::mousePressEvent(event);
770     }
771 }
772
773 void QSGFlickable::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
774 {
775     Q_D(QSGFlickable);
776     if (d->interactive) {
777         d->handleMouseMoveEvent(event);
778         event->accept();
779     } else {
780         QSGItem::mouseMoveEvent(event);
781     }
782 }
783
784 void QSGFlickable::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
785 {
786     Q_D(QSGFlickable);
787     if (d->interactive) {
788         d->clearDelayedPress();
789         d->handleMouseReleaseEvent(event);
790         event->accept();
791         ungrabMouse();
792     } else {
793         QSGItem::mouseReleaseEvent(event);
794     }
795 }
796
797 void QSGFlickable::wheelEvent(QWheelEvent *event)
798 {
799     Q_D(QSGFlickable);
800     if (!d->interactive) {
801         QSGItem::wheelEvent(event);
802     } else if (yflick() && event->orientation() == Qt::Vertical) {
803         bool valid = false;
804         if (event->delta() > 0 && contentY() > -minYExtent()) {
805             d->vData.velocity = qMax(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
806             valid = true;
807         } else if (event->delta() < 0 && contentY() < -maxYExtent()) {
808             d->vData.velocity = qMin(event->delta()*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
809             valid = true;
810         }
811         if (valid) {
812             d->flickingVertically = false;
813             d->flickY(d->vData.velocity);
814             if (d->flickingVertically) {
815                 d->vMoved = true;
816                 movementStarting();
817             }
818             event->accept();
819         }
820     } else if (xflick() && event->orientation() == Qt::Horizontal) {
821         bool valid = false;
822         if (event->delta() > 0 && contentX() > -minXExtent()) {
823             d->hData.velocity = qMax(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
824             valid = true;
825         } else if (event->delta() < 0 && contentX() < -maxXExtent()) {
826             d->hData.velocity = qMin(event->delta()*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
827             valid = true;
828         }
829         if (valid) {
830             d->flickingHorizontally = false;
831             d->flickX(d->hData.velocity);
832             if (d->flickingHorizontally) {
833                 d->hMoved = true;
834                 movementStarting();
835             }
836             event->accept();
837         }
838     } else {
839         QSGItem::wheelEvent(event);
840     }
841 }
842
843 bool QSGFlickablePrivate::isOutermostPressDelay() const
844 {
845     Q_Q(const QSGFlickable);
846     QSGItem *item = q->parentItem();
847     while (item) {
848         QSGFlickable *flick = qobject_cast<QSGFlickable*>(item);
849         if (flick && flick->pressDelay() > 0 && flick->isInteractive())
850             return false;
851         item = item->parentItem();
852     }
853
854     return true;
855 }
856
857 void QSGFlickablePrivate::captureDelayedPress(QGraphicsSceneMouseEvent *event)
858 {
859     Q_Q(QSGFlickable);
860     if (!q->canvas() || pressDelay <= 0)
861         return;
862     if (!isOutermostPressDelay())
863         return;
864     delayedPressTarget = q->canvas()->mouseGrabberItem();
865     delayedPressEvent = new QGraphicsSceneMouseEvent(event->type());
866     delayedPressEvent->setAccepted(false);
867     for (int i = 0x1; i <= 0x10; i <<= 1) {
868         if (event->buttons() & i) {
869             Qt::MouseButton button = Qt::MouseButton(i);
870             delayedPressEvent->setButtonDownPos(button, event->buttonDownPos(button));
871             delayedPressEvent->setButtonDownScenePos(button, event->buttonDownScenePos(button));
872             delayedPressEvent->setButtonDownScreenPos(button, event->buttonDownScreenPos(button));
873         }
874     }
875     delayedPressEvent->setButtons(event->buttons());
876     delayedPressEvent->setButton(event->button());
877     delayedPressEvent->setPos(event->pos());
878     delayedPressEvent->setScenePos(event->scenePos());
879     delayedPressEvent->setScreenPos(event->screenPos());
880     delayedPressEvent->setLastPos(event->lastPos());
881     delayedPressEvent->setLastScenePos(event->lastScenePos());
882     delayedPressEvent->setLastScreenPos(event->lastScreenPos());
883     delayedPressEvent->setModifiers(event->modifiers());
884     delayedPressTimer.start(pressDelay, q);
885 }
886
887 void QSGFlickablePrivate::clearDelayedPress()
888 {
889     if (delayedPressEvent) {
890         delayedPressTimer.stop();
891         delete delayedPressEvent;
892         delayedPressEvent = 0;
893     }
894 }
895
896 void QSGFlickablePrivate::setViewportX(qreal x)
897 {
898     contentItem->setX(x);
899 }
900
901 void QSGFlickablePrivate::setViewportY(qreal y)
902 {
903     contentItem->setY(y);
904 }
905
906 void QSGFlickable::timerEvent(QTimerEvent *event)
907 {
908     Q_D(QSGFlickable);
909     if (event->timerId() == d->delayedPressTimer.timerId()) {
910         d->delayedPressTimer.stop();
911         if (d->delayedPressEvent) {
912             QSGItem *grabber = canvas() ? canvas()->mouseGrabberItem() : 0;
913             if (!grabber || grabber != this) {
914                 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
915                 // so we reset the grabber
916                 if (canvas()->mouseGrabberItem() == d->delayedPressTarget)
917                     d->delayedPressTarget->ungrabMouse();
918                 // Use the event handler that will take care of finding the proper item to propagate the event
919                 QSGCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
920             }
921             delete d->delayedPressEvent;
922             d->delayedPressEvent = 0;
923         }
924     }
925 }
926
927 qreal QSGFlickable::minYExtent() const
928 {
929     return 0.0;
930 }
931
932 qreal QSGFlickable::minXExtent() const
933 {
934     return 0.0;
935 }
936
937 /* returns -ve */
938 qreal QSGFlickable::maxXExtent() const
939 {
940     return width() - vWidth();
941 }
942 /* returns -ve */
943 qreal QSGFlickable::maxYExtent() const
944 {
945     return height() - vHeight();
946 }
947
948 void QSGFlickable::viewportMoved()
949 {
950     Q_D(QSGFlickable);
951
952     qreal prevX = d->lastFlickablePosition.x();
953     qreal prevY = d->lastFlickablePosition.y();
954     d->velocityTimeline.clear();
955     if (d->pressed || d->calcVelocity) {
956         int elapsed = QSGItemPrivate::restart(d->velocityTime);
957         if (elapsed > 0) {
958             qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / elapsed;
959             qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / elapsed;
960             d->velocityTimeline.move(d->hData.smoothVelocity, horizontalVelocity, d->reportedVelocitySmoothing);
961             d->velocityTimeline.move(d->hData.smoothVelocity, 0, d->reportedVelocitySmoothing);
962             d->velocityTimeline.move(d->vData.smoothVelocity, verticalVelocity, d->reportedVelocitySmoothing);
963             d->velocityTimeline.move(d->vData.smoothVelocity, 0, d->reportedVelocitySmoothing);
964         }
965     } else {
966         if (d->timeline.time() > d->vTime) {
967             qreal horizontalVelocity = (prevX - d->hData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
968             qreal verticalVelocity = (prevY - d->vData.move.value()) * 1000 / (d->timeline.time() - d->vTime);
969             d->hData.smoothVelocity.setValue(horizontalVelocity);
970             d->vData.smoothVelocity.setValue(verticalVelocity);
971         }
972     }
973
974     if (!d->vData.inOvershoot && !d->vData.fixingUp && d->flickingVertically
975             && (d->vData.move.value() > minYExtent() || d->vData.move.value() < maxYExtent())
976             && qAbs(d->vData.smoothVelocity.value()) > 100) {
977         // Increase deceleration if we've passed a bound
978         d->vData.inOvershoot = true;
979         qreal maxDistance = d->overShootDistance(height());
980         d->timeline.reset(d->vData.move);
981         d->timeline.accel(d->vData.move, -d->vData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
982         d->timeline.callback(QDeclarativeTimeLineCallback(&d->vData.move, d->fixupY_callback, d));
983     }
984     if (!d->hData.inOvershoot && !d->hData.fixingUp && d->flickingHorizontally
985             && (d->hData.move.value() > minXExtent() || d->hData.move.value() < maxXExtent())
986             && qAbs(d->hData.smoothVelocity.value()) > 100) {
987         // Increase deceleration if we've passed a bound
988         d->hData.inOvershoot = true;
989         qreal maxDistance = d->overShootDistance(width());
990         d->timeline.reset(d->hData.move);
991         d->timeline.accel(d->hData.move, -d->hData.smoothVelocity.value(), d->deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
992         d->timeline.callback(QDeclarativeTimeLineCallback(&d->hData.move, d->fixupX_callback, d));
993     }
994
995     d->lastFlickablePosition = QPointF(d->hData.move.value(), d->vData.move.value());
996
997     d->vTime = d->timeline.time();
998     d->updateBeginningEnd();
999 }
1000
1001 void QSGFlickable::geometryChanged(const QRectF &newGeometry,
1002                              const QRectF &oldGeometry)
1003 {
1004     Q_D(QSGFlickable);
1005     QSGItem::geometryChanged(newGeometry, oldGeometry);
1006
1007     bool changed = false;
1008     if (newGeometry.width() != oldGeometry.width()) {
1009         if (xflick())
1010             changed = true;
1011         if (d->hData.viewSize < 0) {
1012             d->contentItem->setWidth(width());
1013             emit contentWidthChanged();
1014         }
1015         // Make sure that we're entirely in view.
1016         if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1017             d->fixupMode = QSGFlickablePrivate::Immediate;
1018             d->fixupX();
1019         }
1020     }
1021     if (newGeometry.height() != oldGeometry.height()) {
1022         if (yflick())
1023             changed = true;
1024         if (d->vData.viewSize < 0) {
1025             d->contentItem->setHeight(height());
1026             emit contentHeightChanged();
1027         }
1028         // Make sure that we're entirely in view.
1029         if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1030             d->fixupMode = QSGFlickablePrivate::Immediate;
1031             d->fixupY();
1032         }
1033     }
1034
1035     if (changed)
1036         d->updateBeginningEnd();
1037 }
1038
1039 void QSGFlickable::cancelFlick()
1040 {
1041     Q_D(QSGFlickable);
1042     d->timeline.reset(d->hData.move);
1043     d->timeline.reset(d->vData.move);
1044     movementEnding();
1045 }
1046
1047 void QSGFlickablePrivate::data_append(QDeclarativeListProperty<QObject> *prop, QObject *o)
1048 {
1049     QSGItem *i = qobject_cast<QSGItem *>(o);
1050     if (i) {
1051         i->setParentItem(static_cast<QSGFlickablePrivate*>(prop->data)->contentItem);
1052     } else {
1053         o->setParent(prop->object); // XXX todo - do we want this?
1054     }
1055 }
1056
1057 int QSGFlickablePrivate::data_count(QDeclarativeListProperty<QObject> *)
1058 {
1059     // XXX todo
1060     return 0;
1061 }
1062
1063 QObject *QSGFlickablePrivate::data_at(QDeclarativeListProperty<QObject> *, int)
1064 {
1065     // XXX todo
1066     return 0;
1067 }
1068
1069 void QSGFlickablePrivate::data_clear(QDeclarativeListProperty<QObject> *)
1070 {
1071     // XXX todo
1072 }
1073
1074 QDeclarativeListProperty<QObject> QSGFlickable::flickableData()
1075 {
1076     Q_D(QSGFlickable);
1077     return QDeclarativeListProperty<QObject>(this, (void *)d, QSGFlickablePrivate::data_append,
1078                                              QSGFlickablePrivate::data_count,
1079                                              QSGFlickablePrivate::data_at,
1080                                              QSGFlickablePrivate::data_clear);
1081 }
1082
1083 QDeclarativeListProperty<QSGItem> QSGFlickable::flickableChildren()
1084 {
1085     Q_D(QSGFlickable);
1086     return QSGItemPrivate::get(d->contentItem)->children();
1087 }
1088
1089 QSGFlickable::BoundsBehavior QSGFlickable::boundsBehavior() const
1090 {
1091     Q_D(const QSGFlickable);
1092     return d->boundsBehavior;
1093 }
1094
1095 void QSGFlickable::setBoundsBehavior(BoundsBehavior b)
1096 {
1097     Q_D(QSGFlickable);
1098     if (b == d->boundsBehavior)
1099         return;
1100     d->boundsBehavior = b;
1101     emit boundsBehaviorChanged();
1102 }
1103
1104 qreal QSGFlickable::contentWidth() const
1105 {
1106     Q_D(const QSGFlickable);
1107     return d->hData.viewSize;
1108 }
1109
1110 void QSGFlickable::setContentWidth(qreal w)
1111 {
1112     Q_D(QSGFlickable);
1113     if (d->hData.viewSize == w)
1114         return;
1115     d->hData.viewSize = w;
1116     if (w < 0)
1117         d->contentItem->setWidth(width());
1118     else
1119         d->contentItem->setWidth(w);
1120     // Make sure that we're entirely in view.
1121     if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1122         d->fixupMode = QSGFlickablePrivate::Immediate;
1123         d->fixupX();
1124     } else if (!d->pressed && d->hData.fixingUp) {
1125         d->fixupMode = QSGFlickablePrivate::ExtentChanged;
1126         d->fixupX();
1127     }
1128     emit contentWidthChanged();
1129     d->updateBeginningEnd();
1130 }
1131
1132 qreal QSGFlickable::contentHeight() const
1133 {
1134     Q_D(const QSGFlickable);
1135     return d->vData.viewSize;
1136 }
1137
1138 void QSGFlickable::setContentHeight(qreal h)
1139 {
1140     Q_D(QSGFlickable);
1141     if (d->vData.viewSize == h)
1142         return;
1143     d->vData.viewSize = h;
1144     if (h < 0)
1145         d->contentItem->setHeight(height());
1146     else
1147         d->contentItem->setHeight(h);
1148     // Make sure that we're entirely in view.
1149     if (!d->pressed && !d->movingHorizontally && !d->movingVertically) {
1150         d->fixupMode = QSGFlickablePrivate::Immediate;
1151         d->fixupY();
1152     } else if (!d->pressed && d->vData.fixingUp) {
1153         d->fixupMode = QSGFlickablePrivate::ExtentChanged;
1154         d->fixupY();
1155     }
1156     emit contentHeightChanged();
1157     d->updateBeginningEnd();
1158 }
1159
1160 void QSGFlickable::resizeContent(qreal w, qreal h, QPointF center)
1161 {
1162     Q_D(QSGFlickable);
1163     if (w != d->hData.viewSize) {
1164         qreal oldSize = d->hData.viewSize;
1165         d->hData.viewSize = w;
1166         d->contentItem->setWidth(w);
1167         emit contentWidthChanged();
1168         if (center.x() != 0) {
1169             qreal pos = center.x() * w / oldSize;
1170             setContentX(contentX() + pos - center.x());
1171         }
1172     }
1173     if (h != d->vData.viewSize) {
1174         qreal oldSize = d->vData.viewSize;
1175         d->vData.viewSize = h;
1176         d->contentItem->setHeight(h);
1177         emit contentHeightChanged();
1178         if (center.y() != 0) {
1179             qreal pos = center.y() * h / oldSize;
1180             setContentY(contentY() + pos - center.y());
1181         }
1182     }
1183     d->updateBeginningEnd();
1184 }
1185
1186 void QSGFlickable::returnToBounds()
1187 {
1188     Q_D(QSGFlickable);
1189     d->fixupX();
1190     d->fixupY();
1191 }
1192
1193 qreal QSGFlickable::vWidth() const
1194 {
1195     Q_D(const QSGFlickable);
1196     if (d->hData.viewSize < 0)
1197         return width();
1198     else
1199         return d->hData.viewSize;
1200 }
1201
1202 qreal QSGFlickable::vHeight() const
1203 {
1204     Q_D(const QSGFlickable);
1205     if (d->vData.viewSize < 0)
1206         return height();
1207     else
1208         return d->vData.viewSize;
1209 }
1210
1211 bool QSGFlickable::xflick() const
1212 {
1213     Q_D(const QSGFlickable);
1214     if (d->flickableDirection == QSGFlickable::AutoFlickDirection)
1215         return vWidth() != width();
1216     return d->flickableDirection & QSGFlickable::HorizontalFlick;
1217 }
1218
1219 bool QSGFlickable::yflick() const
1220 {
1221     Q_D(const QSGFlickable);
1222     if (d->flickableDirection == QSGFlickable::AutoFlickDirection)
1223         return vHeight() !=  height();
1224     return d->flickableDirection & QSGFlickable::VerticalFlick;
1225 }
1226
1227 void QSGFlickable::mouseUngrabEvent()
1228 {
1229     Q_D(QSGFlickable);
1230     if (d->pressed) {
1231         // if our mouse grab has been removed (probably by another Flickable),
1232         // fix our state
1233         d->pressed = false;
1234         d->stealMouse = false;
1235         setKeepMouseGrab(false);
1236     }
1237 }
1238
1239 bool QSGFlickable::sendMouseEvent(QGraphicsSceneMouseEvent *event)
1240 {
1241     Q_D(QSGFlickable);
1242     QGraphicsSceneMouseEvent mouseEvent(event->type());
1243     QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
1244
1245     QSGCanvas *c = canvas();
1246     QSGItem *grabber = c ? c->mouseGrabberItem() : 0;
1247     bool disabledItem = grabber && !grabber->isEnabled();
1248     bool stealThisEvent = d->stealMouse;
1249     if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab() || disabledItem)) {
1250         mouseEvent.setAccepted(false);
1251         for (int i = 0x1; i <= 0x10; i <<= 1) {
1252             if (event->buttons() & i) {
1253                 Qt::MouseButton button = Qt::MouseButton(i);
1254                 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
1255             }
1256         }
1257         mouseEvent.setScenePos(event->scenePos());
1258         mouseEvent.setLastScenePos(event->lastScenePos());
1259         mouseEvent.setPos(mapFromScene(event->scenePos()));
1260         mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
1261
1262         switch(mouseEvent.type()) {
1263         case QEvent::GraphicsSceneMouseMove:
1264             d->handleMouseMoveEvent(&mouseEvent);
1265             break;
1266         case QEvent::GraphicsSceneMousePress:
1267             if (d->pressed) // we are already pressed - this is a delayed replay
1268                 return false;
1269
1270             d->handleMousePressEvent(&mouseEvent);
1271             d->captureDelayedPress(event);
1272             stealThisEvent = d->stealMouse;   // Update stealThisEvent in case changed by function call above
1273             break;
1274         case QEvent::GraphicsSceneMouseRelease:
1275             if (d->delayedPressEvent) {
1276                 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1277                 // so we reset the grabber
1278                 if (c->mouseGrabberItem() == d->delayedPressTarget)
1279                     d->delayedPressTarget->ungrabMouse();
1280                 //Use the event handler that will take care of finding the proper item to propagate the event
1281                 QSGCanvasPrivate::get(canvas())->deliverMouseEvent(d->delayedPressEvent);
1282                 d->clearDelayedPress();
1283                 // We send the release
1284                 canvas()->sendEvent(c->mouseGrabberItem(), event);
1285                 // And the event has been consumed
1286                 d->stealMouse = false;
1287                 d->pressed = false;
1288                 return true;
1289             }
1290             d->handleMouseReleaseEvent(&mouseEvent);
1291             break;
1292         default:
1293             break;
1294         }
1295         grabber = qobject_cast<QSGItem*>(c->mouseGrabberItem());
1296         if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || disabledItem) {
1297             d->clearDelayedPress();
1298             grabMouse();
1299         }
1300
1301         return stealThisEvent || d->delayedPressEvent || disabledItem;
1302     } else if (d->lastPosTime.isValid()) {
1303         d->lastPosTime.invalidate();
1304     }
1305     if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease) {
1306         d->clearDelayedPress();
1307         d->stealMouse = false;
1308         d->pressed = false;
1309     }
1310     return false;
1311 }
1312
1313
1314 bool QSGFlickable::childMouseEventFilter(QSGItem *i, QEvent *e)
1315 {
1316     Q_D(QSGFlickable);
1317     if (!isVisible() || !d->interactive)
1318         return QSGItem::childMouseEventFilter(i, e);
1319     switch (e->type()) {
1320     case QEvent::GraphicsSceneMousePress:
1321     case QEvent::GraphicsSceneMouseMove:
1322     case QEvent::GraphicsSceneMouseRelease:
1323         return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
1324     default:
1325         break;
1326     }
1327
1328     return QSGItem::childMouseEventFilter(i, e);
1329 }
1330
1331 qreal QSGFlickable::maximumFlickVelocity() const
1332 {
1333     Q_D(const QSGFlickable);
1334     return d->maxVelocity;
1335 }
1336
1337 void QSGFlickable::setMaximumFlickVelocity(qreal v)
1338 {
1339     Q_D(QSGFlickable);
1340     if (v == d->maxVelocity)
1341         return;
1342     d->maxVelocity = v;
1343     emit maximumFlickVelocityChanged();
1344 }
1345
1346 qreal QSGFlickable::flickDeceleration() const
1347 {
1348     Q_D(const QSGFlickable);
1349     return d->deceleration;
1350 }
1351
1352 void QSGFlickable::setFlickDeceleration(qreal deceleration)
1353 {
1354     Q_D(QSGFlickable);
1355     if (deceleration == d->deceleration)
1356         return;
1357     d->deceleration = deceleration;
1358     emit flickDecelerationChanged();
1359 }
1360
1361 bool QSGFlickable::isFlicking() const
1362 {
1363     Q_D(const QSGFlickable);
1364     return d->flickingHorizontally ||  d->flickingVertically;
1365 }
1366
1367 bool QSGFlickable::isFlickingHorizontally() const
1368 {
1369     Q_D(const QSGFlickable);
1370     return d->flickingHorizontally;
1371 }
1372
1373 bool QSGFlickable::isFlickingVertically() const
1374 {
1375     Q_D(const QSGFlickable);
1376     return d->flickingVertically;
1377 }
1378
1379 int QSGFlickable::pressDelay() const
1380 {
1381     Q_D(const QSGFlickable);
1382     return d->pressDelay;
1383 }
1384
1385 void QSGFlickable::setPressDelay(int delay)
1386 {
1387     Q_D(QSGFlickable);
1388     if (d->pressDelay == delay)
1389         return;
1390     d->pressDelay = delay;
1391     emit pressDelayChanged();
1392 }
1393
1394
1395 bool QSGFlickable::isMoving() const
1396 {
1397     Q_D(const QSGFlickable);
1398     return d->movingHorizontally || d->movingVertically;
1399 }
1400
1401 bool QSGFlickable::isMovingHorizontally() const
1402 {
1403     Q_D(const QSGFlickable);
1404     return d->movingHorizontally;
1405 }
1406
1407 bool QSGFlickable::isMovingVertically() const
1408 {
1409     Q_D(const QSGFlickable);
1410     return d->movingVertically;
1411 }
1412
1413 void QSGFlickable::movementStarting()
1414 {
1415     Q_D(QSGFlickable);
1416     if (d->hMoved && !d->movingHorizontally) {
1417         d->movingHorizontally = true;
1418         emit movingChanged();
1419         emit movingHorizontallyChanged();
1420         if (!d->movingVertically)
1421             emit movementStarted();
1422     }
1423     else if (d->vMoved && !d->movingVertically) {
1424         d->movingVertically = true;
1425         emit movingChanged();
1426         emit movingVerticallyChanged();
1427         if (!d->movingHorizontally)
1428             emit movementStarted();
1429     }
1430 }
1431
1432 void QSGFlickable::movementEnding()
1433 {
1434     Q_D(QSGFlickable);
1435     movementXEnding();
1436     movementYEnding();
1437     d->hData.smoothVelocity.setValue(0);
1438     d->vData.smoothVelocity.setValue(0);
1439 }
1440
1441 void QSGFlickable::movementXEnding()
1442 {
1443     Q_D(QSGFlickable);
1444     if (d->flickingHorizontally) {
1445         d->flickingHorizontally = false;
1446         emit flickingChanged();
1447         emit flickingHorizontallyChanged();
1448         if (!d->flickingVertically)
1449            emit flickEnded();
1450     }
1451     if (!d->pressed && !d->stealMouse) {
1452         if (d->movingHorizontally) {
1453             d->movingHorizontally = false;
1454             d->hMoved = false;
1455             emit movingChanged();
1456             emit movingHorizontallyChanged();
1457             if (!d->movingVertically)
1458                 emit movementEnded();
1459         }
1460     }
1461     d->hData.fixingUp = false;
1462 }
1463
1464 void QSGFlickable::movementYEnding()
1465 {
1466     Q_D(QSGFlickable);
1467     if (d->flickingVertically) {
1468         d->flickingVertically = false;
1469         emit flickingChanged();
1470         emit flickingVerticallyChanged();
1471         if (!d->flickingHorizontally)
1472            emit flickEnded();
1473     }
1474     if (!d->pressed && !d->stealMouse) {
1475         if (d->movingVertically) {
1476             d->movingVertically = false;
1477             d->vMoved = false;
1478             emit movingChanged();
1479             emit movingVerticallyChanged();
1480             if (!d->movingHorizontally)
1481                 emit movementEnded();
1482         }
1483     }
1484     d->vData.fixingUp = false;
1485 }
1486
1487 void QSGFlickablePrivate::updateVelocity()
1488 {
1489     Q_Q(QSGFlickable);
1490     emit q->horizontalVelocityChanged();
1491     emit q->verticalVelocityChanged();
1492 }
1493
1494 QT_END_NAMESPACE