Fix emitting originYChanged signals even when originY wouldn't change
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickflickable.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickflickable_p.h"
43 #include "qquickflickable_p_p.h"
44 #include "qquickwindow.h"
45 #include "qquickwindow_p.h"
46 #include "qquickevents_p_p.h"
47
48 #include <QtQuick/private/qquicktransition_p.h>
49 #include <private/qqmlglobal_p.h>
50
51 #include <QtQml/qqmlinfo.h>
52 #include <QtGui/qevent.h>
53 #include <QtGui/qguiapplication.h>
54 #include <QtGui/private/qguiapplication_p.h>
55 #include <QtGui/qstylehints.h>
56 #include "qplatformdefs.h"
57
58 QT_BEGIN_NAMESPACE
59
60 // The maximum number of pixels a flick can overshoot
61 #ifndef QML_FLICK_OVERSHOOT
62 #define QML_FLICK_OVERSHOOT 150
63 #endif
64
65 // The number of samples to use in calculating the velocity of a flick
66 #ifndef QML_FLICK_SAMPLEBUFFER
67 #define QML_FLICK_SAMPLEBUFFER 3
68 #endif
69
70 // The number of samples to discard when calculating the flick velocity.
71 // Touch panels often produce inaccurate results as the finger is lifted.
72 #ifndef QML_FLICK_DISCARDSAMPLES
73 #define QML_FLICK_DISCARDSAMPLES 0
74 #endif
75
76 // The default maximum velocity of a flick.
77 #ifndef QML_FLICK_DEFAULTMAXVELOCITY
78 #define QML_FLICK_DEFAULTMAXVELOCITY 2500
79 #endif
80
81 // The default deceleration of a flick.
82 #ifndef QML_FLICK_DEFAULTDECELERATION
83 #define QML_FLICK_DEFAULTDECELERATION 1500
84 #endif
85
86 // How much faster to decelerate when overshooting
87 #ifndef QML_FLICK_OVERSHOOTFRICTION
88 #define QML_FLICK_OVERSHOOTFRICTION 8
89 #endif
90
91 // Multiflick acceleration minimum flick velocity threshold
92 #ifndef QML_FLICK_MULTIFLICK_THRESHOLD
93 #define QML_FLICK_MULTIFLICK_THRESHOLD 1250
94 #endif
95
96 // Multiflick acceleration minimum contentSize/viewSize ratio
97 #ifndef QML_FLICK_MULTIFLICK_RATIO
98 #define QML_FLICK_MULTIFLICK_RATIO 10
99 #endif
100
101 // Multiflick acceleration maximum velocity multiplier
102 #ifndef QML_FLICK_MULTIFLICK_MAXBOOST
103 #define QML_FLICK_MULTIFLICK_MAXBOOST 3.0
104 #endif
105
106 // FlickThreshold determines how far the "mouse" must have moved
107 // before we perform a flick.
108 static const int FlickThreshold = 15;
109
110 // RetainGrabVelocity is the maxmimum instantaneous velocity that
111 // will ensure the Flickable retains the grab on consecutive flicks.
112 static const int RetainGrabVelocity = 100;
113
114 QQuickFlickableVisibleArea::QQuickFlickableVisibleArea(QQuickFlickable *parent)
115     : QObject(parent), flickable(parent), m_xPosition(0.), m_widthRatio(0.)
116     , m_yPosition(0.), m_heightRatio(0.)
117 {
118 }
119
120 qreal QQuickFlickableVisibleArea::widthRatio() const
121 {
122     return m_widthRatio;
123 }
124
125 qreal QQuickFlickableVisibleArea::xPosition() const
126 {
127     return m_xPosition;
128 }
129
130 qreal QQuickFlickableVisibleArea::heightRatio() const
131 {
132     return m_heightRatio;
133 }
134
135 qreal QQuickFlickableVisibleArea::yPosition() const
136 {
137     return m_yPosition;
138 }
139
140 void QQuickFlickableVisibleArea::updateVisible()
141 {
142     QQuickFlickablePrivate *p = QQuickFlickablePrivate::get(flickable);
143
144     bool changeX = false;
145     bool changeY = false;
146     bool changeWidth = false;
147     bool changeHeight = false;
148
149     // Vertical
150     const qreal viewheight = flickable->height();
151     const qreal maxyextent = -flickable->maxYExtent() + flickable->minYExtent();
152     qreal pagePos = (-p->vData.move.value() + flickable->minYExtent()) / (maxyextent + viewheight);
153     qreal pageSize = viewheight / (maxyextent + viewheight);
154
155     if (pageSize != m_heightRatio) {
156         m_heightRatio = pageSize;
157         changeHeight = true;
158     }
159     if (pagePos != m_yPosition) {
160         m_yPosition = pagePos;
161         changeY = true;
162     }
163
164     // Horizontal
165     const qreal viewwidth = flickable->width();
166     const qreal maxxextent = -flickable->maxXExtent() + flickable->minXExtent();
167     pagePos = (-p->hData.move.value() + flickable->minXExtent()) / (maxxextent + viewwidth);
168     pageSize = viewwidth / (maxxextent + viewwidth);
169
170     if (pageSize != m_widthRatio) {
171         m_widthRatio = pageSize;
172         changeWidth = true;
173     }
174     if (pagePos != m_xPosition) {
175         m_xPosition = pagePos;
176         changeX = true;
177     }
178
179     if (changeX)
180         emit xPositionChanged(m_xPosition);
181     if (changeY)
182         emit yPositionChanged(m_yPosition);
183     if (changeWidth)
184         emit widthRatioChanged(m_widthRatio);
185     if (changeHeight)
186         emit heightRatioChanged(m_heightRatio);
187 }
188
189
190 class QQuickFlickableReboundTransition : public QQuickTransitionManager
191 {
192 public:
193     QQuickFlickableReboundTransition(QQuickFlickable *f, const QString &name)
194         : flickable(f), axisData(0), propName(name), active(false)
195     {
196     }
197
198     ~QQuickFlickableReboundTransition()
199     {
200         flickable = 0;
201     }
202
203     bool startTransition(QQuickFlickablePrivate::AxisData *data, qreal toPos) {
204         QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flickable);
205         if (!fp->rebound || !fp->rebound->enabled())
206             return false;
207         active = true;
208         axisData = data;
209         axisData->transitionTo = toPos;
210         axisData->transitionToSet = true;
211
212         actions.clear();
213         actions << QQuickAction(fp->contentItem, propName, toPos);
214         QQuickTransitionManager::transition(actions, fp->rebound, fp->contentItem);
215         return true;
216     }
217
218     bool isActive() const {
219         return active;
220     }
221
222     void stopTransition() {
223         if (!flickable || !isRunning())
224             return;
225         QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flickable);
226         if (axisData == &fp->hData)
227             axisData->move.setValue(-flickable->contentX());
228         else
229             axisData->move.setValue(-flickable->contentY());
230         cancel();
231         active = false;
232     }
233
234 protected:
235     virtual void finished() {
236         if (!flickable)
237             return;
238         axisData->move.setValue(axisData->transitionTo);
239         QQuickFlickablePrivate *fp = QQuickFlickablePrivate::get(flickable);
240         active = false;
241
242         if (!fp->hData.transitionToBounds->isActive()
243                 && !fp->vData.transitionToBounds->isActive()) {
244             flickable->movementEnding();
245         }
246     }
247
248 private:
249     QQuickStateOperation::ActionList actions;
250     QQuickFlickable *flickable;
251     QQuickFlickablePrivate::AxisData *axisData;
252     QString propName;
253     bool active;
254 };
255
256 QQuickFlickablePrivate::AxisData::~AxisData()
257 {
258     delete transitionToBounds;
259 }
260
261
262 QQuickFlickablePrivate::QQuickFlickablePrivate()
263   : contentItem(new QQuickItem)
264     , hData(this, &QQuickFlickablePrivate::setViewportX)
265     , vData(this, &QQuickFlickablePrivate::setViewportY)
266     , hMoved(false), vMoved(false)
267     , stealMouse(false), pressed(false), interactive(true), calcVelocity(false)
268     , pixelAligned(false)
269     , lastPosTime(-1)
270     , lastPressTime(0)
271     , deceleration(QML_FLICK_DEFAULTDECELERATION)
272     , maxVelocity(QML_FLICK_DEFAULTMAXVELOCITY), reportedVelocitySmoothing(100)
273     , delayedPressEvent(0), delayedPressTarget(0), pressDelay(0), fixupDuration(400)
274     , flickBoost(1.0), fixupMode(Normal), vTime(0), visibleArea(0)
275     , flickableDirection(QQuickFlickable::AutoFlickDirection)
276     , boundsBehavior(QQuickFlickable::DragAndOvershootBounds)
277     , rebound(0)
278 {
279 }
280
281 void QQuickFlickablePrivate::init()
282 {
283     Q_Q(QQuickFlickable);
284     QQml_setParent_noEvent(contentItem, q);
285     contentItem->setParentItem(q);
286     qmlobject_connect(&timeline, QQuickTimeLine, SIGNAL(completed()),
287                       q, QQuickFlickable, SLOT(timelineCompleted()))
288     q->setAcceptedMouseButtons(Qt::LeftButton);
289     q->setFiltersChildMouseEvents(true);
290     QQuickItemPrivate *viewportPrivate = QQuickItemPrivate::get(contentItem);
291     viewportPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
292 }
293
294 /*
295     Returns the amount to overshoot by given a velocity.
296     Will be roughly in range 0 - size/4
297 */
298 qreal QQuickFlickablePrivate::overShootDistance(qreal size)
299 {
300     if (maxVelocity <= 0)
301         return 0.0;
302
303     return qMin(qreal(QML_FLICK_OVERSHOOT), size/3);
304 }
305
306 void QQuickFlickablePrivate::AxisData::addVelocitySample(qreal v, qreal maxVelocity)
307 {
308     if (v > maxVelocity)
309         v = maxVelocity;
310     else if (v < -maxVelocity)
311         v = -maxVelocity;
312     velocityBuffer.append(v);
313     if (velocityBuffer.count() > QML_FLICK_SAMPLEBUFFER)
314         velocityBuffer.remove(0);
315 }
316
317 void QQuickFlickablePrivate::AxisData::updateVelocity()
318 {
319     velocity = 0;
320     if (velocityBuffer.count() > QML_FLICK_DISCARDSAMPLES) {
321         int count = velocityBuffer.count()-QML_FLICK_DISCARDSAMPLES;
322         for (int i = 0; i < count; ++i) {
323             qreal v = velocityBuffer.at(i);
324             velocity += v;
325         }
326         velocity /= count;
327     }
328 }
329
330 void QQuickFlickablePrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeom, const QRectF &oldGeom)
331 {
332     Q_Q(QQuickFlickable);
333     if (item == contentItem) {
334         Qt::Orientations orient = 0;
335         if (newGeom.x() != oldGeom.x())
336             orient |= Qt::Horizontal;
337         if (newGeom.y() != oldGeom.y())
338             orient |= Qt::Vertical;
339         if (orient)
340             q->viewportMoved(orient);
341         if (orient & Qt::Horizontal)
342             emit q->contentXChanged();
343         if (orient & Qt::Vertical)
344             emit q->contentYChanged();
345     }
346 }
347
348 bool QQuickFlickablePrivate::flickX(qreal velocity)
349 {
350     Q_Q(QQuickFlickable);
351     return flick(hData, q->minXExtent(), q->maxXExtent(), q->width(), fixupX_callback, velocity);
352 }
353
354 bool QQuickFlickablePrivate::flickY(qreal velocity)
355 {
356     Q_Q(QQuickFlickable);
357     return flick(vData, q->minYExtent(), q->maxYExtent(), q->height(), fixupY_callback, velocity);
358 }
359
360 bool QQuickFlickablePrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal,
361                                          QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
362 {
363     Q_Q(QQuickFlickable);
364     qreal maxDistance = -1;
365     data.fixingUp = false;
366     // -ve velocity means list is moving up
367     if (velocity > 0) {
368         maxDistance = qAbs(minExtent - data.move.value());
369         data.flickTarget = minExtent;
370     } else {
371         maxDistance = qAbs(maxExtent - data.move.value());
372         data.flickTarget = maxExtent;
373     }
374     if (maxDistance > 0) {
375         qreal v = velocity;
376         if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
377             if (v < 0)
378                 v = -maxVelocity;
379             else
380                 v = maxVelocity;
381         }
382
383         // adjust accel so that we hit a full pixel
384         qreal accel = deceleration;
385         qreal v2 = v * v;
386         qreal dist = v2 / (accel * 2.0);
387         if (v > 0)
388             dist = -dist;
389         qreal target = qRound(data.move.value() - dist);
390         dist = -target + data.move.value();
391         accel = v2 / (2.0f * qAbs(dist));
392
393         resetTimeline(data);
394         if (boundsBehavior == QQuickFlickable::DragAndOvershootBounds)
395             timeline.accel(data.move, v, accel);
396         else
397             timeline.accel(data.move, v, accel, maxDistance);
398         timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
399
400         if (&data == &hData)
401             return !hData.flicking && q->xflick();
402         else if (&data == &vData)
403             return !vData.flicking && q->yflick();
404         return false;
405     } else {
406         resetTimeline(data);
407         fixup(data, minExtent, maxExtent);
408         return false;
409     }
410 }
411
412 void QQuickFlickablePrivate::fixupY_callback(void *data)
413 {
414     ((QQuickFlickablePrivate *)data)->fixupY();
415 }
416
417 void QQuickFlickablePrivate::fixupX_callback(void *data)
418 {
419     ((QQuickFlickablePrivate *)data)->fixupX();
420 }
421
422 void QQuickFlickablePrivate::fixupX()
423 {
424     Q_Q(QQuickFlickable);
425     fixup(hData, q->minXExtent(), q->maxXExtent());
426 }
427
428 void QQuickFlickablePrivate::fixupY()
429 {
430     Q_Q(QQuickFlickable);
431     fixup(vData, q->minYExtent(), q->maxYExtent());
432 }
433
434 void QQuickFlickablePrivate::adjustContentPos(AxisData &data, qreal toPos)
435 {
436     Q_Q(QQuickFlickable);
437     switch (fixupMode) {
438     case Immediate:
439         timeline.set(data.move, toPos);
440         break;
441     case ExtentChanged:
442         // The target has changed. Don't start from the beginning; just complete the
443         // second half of the animation using the new extent.
444         timeline.move(data.move, toPos, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
445         data.fixingUp = true;
446         break;
447     default: {
448             if (data.transitionToBounds && data.transitionToBounds->startTransition(&data, toPos)) {
449                 q->movementStarting();
450                 data.fixingUp = true;
451             } else {
452                 qreal dist = toPos - data.move;
453                 timeline.move(data.move, toPos - dist/2, QEasingCurve(QEasingCurve::InQuad), fixupDuration/4);
454                 timeline.move(data.move, toPos, QEasingCurve(QEasingCurve::OutExpo), 3*fixupDuration/4);
455                 data.fixingUp = true;
456             }
457         }
458     }
459 }
460
461 void QQuickFlickablePrivate::resetTimeline(AxisData &data)
462 {
463     timeline.reset(data.move);
464     if (data.transitionToBounds)
465         data.transitionToBounds->stopTransition();
466 }
467
468 void QQuickFlickablePrivate::clearTimeline()
469 {
470     timeline.clear();
471     if (hData.transitionToBounds)
472         hData.transitionToBounds->stopTransition();
473     if (vData.transitionToBounds)
474         vData.transitionToBounds->stopTransition();
475 }
476
477 void QQuickFlickablePrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
478 {
479     if (data.move.value() > minExtent || maxExtent > minExtent) {
480         resetTimeline(data);
481         if (data.move.value() != minExtent) {
482             adjustContentPos(data, minExtent);
483         }
484     } else if (data.move.value() < maxExtent) {
485         resetTimeline(data);
486         adjustContentPos(data, maxExtent);
487     } else if (qRound(data.move.value()) != data.move.value()) {
488         // We could animate, but since it is less than 0.5 pixel it's probably not worthwhile.
489         resetTimeline(data);
490         qreal val = data.move.value();
491         if (qAbs(qRound(val) - val) < 0.25) // round small differences
492             val = qRound(val);
493         else if (data.smoothVelocity.value() > 0) // continue direction of motion for larger
494             val = qFloor(val);
495         else if (data.smoothVelocity.value() < 0)
496             val = qCeil(val);
497         else // otherwise round
498             val = qRound(val);
499         timeline.set(data.move, val);
500     }
501     data.inOvershoot = false;
502     fixupMode = Normal;
503     data.vTime = timeline.time();
504 }
505
506 void QQuickFlickablePrivate::updateBeginningEnd()
507 {
508     Q_Q(QQuickFlickable);
509     bool atBoundaryChange = false;
510
511     // Vertical
512     const int maxyextent = int(-q->maxYExtent());
513     const qreal ypos = -vData.move.value();
514     bool atBeginning = (ypos <= -q->minYExtent());
515     bool atEnd = (maxyextent <= ypos);
516
517     if (atBeginning != vData.atBeginning) {
518         vData.atBeginning = atBeginning;
519         atBoundaryChange = true;
520     }
521     if (atEnd != vData.atEnd) {
522         vData.atEnd = atEnd;
523         atBoundaryChange = true;
524     }
525
526     // Horizontal
527     const int maxxextent = int(-q->maxXExtent());
528     const qreal xpos = -hData.move.value();
529     atBeginning = (xpos <= -q->minXExtent());
530     atEnd = (maxxextent <= xpos);
531
532     if (atBeginning != hData.atBeginning) {
533         hData.atBeginning = atBeginning;
534         atBoundaryChange = true;
535     }
536     if (atEnd != hData.atEnd) {
537         hData.atEnd = atEnd;
538         atBoundaryChange = true;
539     }
540
541     if (vData.extentsChanged) {
542         vData.extentsChanged = false;
543         qreal originY = q->originY();
544         if (vData.origin != originY) {
545             vData.origin = originY;
546             emit q->originYChanged();
547         }
548     }
549
550     if (hData.extentsChanged) {
551         hData.extentsChanged = false;
552         qreal originX = q->originX();
553         if (hData.origin != originX) {
554             hData.origin = originX;
555             emit q->originXChanged();
556         }
557     }
558
559     if (atBoundaryChange)
560         emit q->isAtBoundaryChanged();
561
562     if (visibleArea)
563         visibleArea->updateVisible();
564 }
565
566 /*
567 XXXTODO add docs describing moving, dragging, flicking properties, e.g.
568
569 When the user starts dragging the Flickable, the dragging and moving properties
570 will be true.
571
572 If the velocity is sufficient when the drag is ended, flicking may begin.
573
574 The moving properties will remain true until all dragging and flicking
575 is finished.
576 */
577
578 /*!
579     \qmlsignal QtQuick2::Flickable::onDragStarted()
580
581     This handler is called when the view starts to be dragged due to user
582     interaction.
583 */
584
585 /*!
586     \qmlsignal QtQuick2::Flickable::onDragEnded()
587
588     This handler is called when the user stops dragging the view.
589
590     If the velocity of the drag is sufficient at the time the
591     touch/mouse button is released then a flick will start.
592 */
593
594 /*!
595     \qmltype Flickable
596     \instantiates QQuickFlickable
597     \inqmlmodule QtQuick 2
598     \ingroup qtquick-input
599     \ingroup qtquick-containers
600
601     \brief Provides a surface that can be "flicked"
602     \inherits Item
603
604     The Flickable item places its children on a surface that can be dragged
605     and flicked, causing the view onto the child items to scroll. This
606     behavior forms the basis of Items that are designed to show large numbers
607     of child items, such as \l ListView and \l GridView.
608
609     In traditional user interfaces, views can be scrolled using standard
610     controls, such as scroll bars and arrow buttons. In some situations, it
611     is also possible to drag the view directly by pressing and holding a
612     mouse button while moving the cursor. In touch-based user interfaces,
613     this dragging action is often complemented with a flicking action, where
614     scrolling continues after the user has stopped touching the view.
615
616     Flickable does not automatically clip its contents. If it is not used as
617     a full-screen item, you should consider setting the \l{Item::}{clip} property
618     to true.
619
620     \section1 Example Usage
621
622     \div {class="float-right"}
623     \inlineimage flickable.gif
624     \enddiv
625
626     The following example shows a small view onto a large image in which the
627     user can drag or flick the image in order to view different parts of it.
628
629     \snippet qml/flickable.qml document
630
631     \clearfloat
632
633     Items declared as children of a Flickable are automatically parented to the
634     Flickable's \l contentItem.  This should be taken into account when
635     operating on the children of the Flickable; it is usually the children of
636     \c contentItem that are relevant.  For example, the bound of Items added
637     to the Flickable will be available by \c contentItem.childrenRect
638
639     \section1 Limitations
640
641     \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by
642     \c id. Use \c parent instead.
643 */
644
645 /*!
646     \qmlsignal QtQuick2::Flickable::onMovementStarted()
647
648     This handler is called when the view begins moving due to user
649     interaction.
650 */
651
652 /*!
653     \qmlsignal QtQuick2::Flickable::onMovementEnded()
654
655     This handler is called when the view stops moving due to user
656     interaction.  If a flick was generated, this handler will
657     be triggered once the flick stops.  If a flick was not
658     generated, the handler will be triggered when the
659     user stops dragging - i.e. a mouse or touch release.
660 */
661
662 /*!
663     \qmlsignal QtQuick2::Flickable::onFlickStarted()
664
665     This handler is called when the view is flicked.  A flick
666     starts from the point that the mouse or touch is released,
667     while still in motion.
668 */
669
670 /*!
671     \qmlsignal QtQuick2::Flickable::onFlickEnded()
672
673     This handler is called when the view stops moving due to a flick.
674 */
675
676 /*!
677     \qmlproperty real QtQuick2::Flickable::visibleArea.xPosition
678     \qmlproperty real QtQuick2::Flickable::visibleArea.widthRatio
679     \qmlproperty real QtQuick2::Flickable::visibleArea.yPosition
680     \qmlproperty real QtQuick2::Flickable::visibleArea.heightRatio
681
682     These properties describe the position and size of the currently viewed area.
683     The size is defined as the percentage of the full view currently visible,
684     scaled to 0.0 - 1.0.  The page position is usually in the range 0.0 (beginning) to
685     1.0 minus size ratio (end), i.e. \c yPosition is in the range 0.0 to 1.0-\c heightRatio.
686     However, it is possible for the contents to be dragged outside of the normal
687     range, resulting in the page positions also being outside the normal range.
688
689     These properties are typically used to draw a scrollbar. For example:
690
691     \snippet qml/flickableScrollbar.qml 0
692     \dots 8
693     \snippet qml/flickableScrollbar.qml 1
694
695     \sa {declarative/ui-components/scrollbar}{scrollbar example}
696 */
697 QQuickFlickable::QQuickFlickable(QQuickItem *parent)
698   : QQuickItem(*(new QQuickFlickablePrivate), parent)
699 {
700     Q_D(QQuickFlickable);
701     d->init();
702 }
703
704 QQuickFlickable::QQuickFlickable(QQuickFlickablePrivate &dd, QQuickItem *parent)
705   : QQuickItem(dd, parent)
706 {
707     Q_D(QQuickFlickable);
708     d->init();
709 }
710
711 QQuickFlickable::~QQuickFlickable()
712 {
713 }
714
715 /*!
716     \qmlproperty real QtQuick2::Flickable::contentX
717     \qmlproperty real QtQuick2::Flickable::contentY
718
719     These properties hold the surface coordinate currently at the top-left
720     corner of the Flickable. For example, if you flick an image up 100 pixels,
721     \c contentY will be 100.
722 */
723 qreal QQuickFlickable::contentX() const
724 {
725     Q_D(const QQuickFlickable);
726     return -d->contentItem->x();
727 }
728
729 void QQuickFlickable::setContentX(qreal pos)
730 {
731     Q_D(QQuickFlickable);
732     d->hData.explicitValue = true;
733     d->resetTimeline(d->hData);
734     d->hData.vTime = d->timeline.time();
735     movementEnding(true, false);
736     if (-pos != d->hData.move.value())
737         d->hData.move.setValue(-pos);
738 }
739
740 qreal QQuickFlickable::contentY() const
741 {
742     Q_D(const QQuickFlickable);
743     return -d->contentItem->y();
744 }
745
746 void QQuickFlickable::setContentY(qreal pos)
747 {
748     Q_D(QQuickFlickable);
749     d->vData.explicitValue = true;
750     d->resetTimeline(d->vData);
751     d->vData.vTime = d->timeline.time();
752     movementEnding(false, true);
753     if (-pos != d->vData.move.value())
754         d->vData.move.setValue(-pos);
755 }
756
757 /*!
758     \qmlproperty bool QtQuick2::Flickable::interactive
759
760     This property describes whether the user can interact with the Flickable.
761     A user cannot drag or flick a Flickable that is not interactive.
762
763     By default, this property is true.
764
765     This property is useful for temporarily disabling flicking. This allows
766     special interaction with Flickable's children; for example, you might want
767     to freeze a flickable map while scrolling through a pop-up dialog that
768     is a child of the Flickable.
769 */
770 bool QQuickFlickable::isInteractive() const
771 {
772     Q_D(const QQuickFlickable);
773     return d->interactive;
774 }
775
776 void QQuickFlickable::setInteractive(bool interactive)
777 {
778     Q_D(QQuickFlickable);
779     if (interactive != d->interactive) {
780         d->interactive = interactive;
781         if (!interactive && (d->hData.flicking || d->vData.flicking)) {
782             d->clearTimeline();
783             d->hData.vTime = d->vData.vTime = d->timeline.time();
784             d->hData.flicking = false;
785             d->vData.flicking = false;
786             emit flickingChanged();
787             emit flickingHorizontallyChanged();
788             emit flickingVerticallyChanged();
789             emit flickEnded();
790         }
791         emit interactiveChanged();
792     }
793 }
794
795 /*!
796     \qmlproperty real QtQuick2::Flickable::horizontalVelocity
797     \qmlproperty real QtQuick2::Flickable::verticalVelocity
798
799     The instantaneous velocity of movement along the x and y axes, in pixels/sec.
800
801     The reported velocity is smoothed to avoid erratic output.
802
803     Note that for views with a large content size (more than 10 times the view size),
804     the velocity of the flick may exceed the velocity of the touch in the case
805     of multiple quick consecutive flicks.  This allows the user to flick faster
806     through large content.
807 */
808 qreal QQuickFlickable::horizontalVelocity() const
809 {
810     Q_D(const QQuickFlickable);
811     return d->hData.smoothVelocity.value();
812 }
813
814 qreal QQuickFlickable::verticalVelocity() const
815 {
816     Q_D(const QQuickFlickable);
817     return d->vData.smoothVelocity.value();
818 }
819
820 /*!
821     \qmlproperty bool QtQuick2::Flickable::atXBeginning
822     \qmlproperty bool QtQuick2::Flickable::atXEnd
823     \qmlproperty bool QtQuick2::Flickable::atYBeginning
824     \qmlproperty bool QtQuick2::Flickable::atYEnd
825
826     These properties are true if the flickable view is positioned at the beginning,
827     or end respectively.
828 */
829 bool QQuickFlickable::isAtXEnd() const
830 {
831     Q_D(const QQuickFlickable);
832     return d->hData.atEnd;
833 }
834
835 bool QQuickFlickable::isAtXBeginning() const
836 {
837     Q_D(const QQuickFlickable);
838     return d->hData.atBeginning;
839 }
840
841 bool QQuickFlickable::isAtYEnd() const
842 {
843     Q_D(const QQuickFlickable);
844     return d->vData.atEnd;
845 }
846
847 bool QQuickFlickable::isAtYBeginning() const
848 {
849     Q_D(const QQuickFlickable);
850     return d->vData.atBeginning;
851 }
852
853 /*!
854     \qmlproperty Item QtQuick2::Flickable::contentItem
855
856     The internal item that contains the Items to be moved in the Flickable.
857
858     Items declared as children of a Flickable are automatically parented to the Flickable's contentItem.
859
860     Items created dynamically need to be explicitly parented to the \e contentItem:
861     \code
862     Flickable {
863         id: myFlickable
864         function addItem(file) {
865             var component = Qt.createComponent(file)
866             component.createObject(myFlickable.contentItem);
867         }
868     }
869     \endcode
870 */
871 QQuickItem *QQuickFlickable::contentItem()
872 {
873     Q_D(QQuickFlickable);
874     return d->contentItem;
875 }
876
877 QQuickFlickableVisibleArea *QQuickFlickable::visibleArea()
878 {
879     Q_D(QQuickFlickable);
880     if (!d->visibleArea)
881         d->visibleArea = new QQuickFlickableVisibleArea(this);
882     return d->visibleArea;
883 }
884
885 /*!
886     \qmlproperty enumeration QtQuick2::Flickable::flickableDirection
887
888     This property determines which directions the view can be flicked.
889
890     \list
891     \li Flickable.AutoFlickDirection (default) - allows flicking vertically if the
892     \e contentHeight is not equal to the \e height of the Flickable.
893     Allows flicking horizontally if the \e contentWidth is not equal
894     to the \e width of the Flickable.
895     \li Flickable.HorizontalFlick - allows flicking horizontally.
896     \li Flickable.VerticalFlick - allows flicking vertically.
897     \li Flickable.HorizontalAndVerticalFlick - allows flicking in both directions.
898     \endlist
899 */
900 QQuickFlickable::FlickableDirection QQuickFlickable::flickableDirection() const
901 {
902     Q_D(const QQuickFlickable);
903     return d->flickableDirection;
904 }
905
906 void QQuickFlickable::setFlickableDirection(FlickableDirection direction)
907 {
908     Q_D(QQuickFlickable);
909     if (direction != d->flickableDirection) {
910         d->flickableDirection = direction;
911         emit flickableDirectionChanged();
912     }
913 }
914
915 bool QQuickFlickable::pixelAligned() const
916 {
917     Q_D(const QQuickFlickable);
918     return d->pixelAligned;
919 }
920
921 void QQuickFlickable::setPixelAligned(bool align)
922 {
923     Q_D(QQuickFlickable);
924     if (align != d->pixelAligned) {
925         d->pixelAligned = align;
926         emit pixelAlignedChanged();
927     }
928 }
929
930 qint64 QQuickFlickablePrivate::computeCurrentTime(QInputEvent *event)
931 {
932     if (0 != event->timestamp() && QQuickItemPrivate::consistentTime == -1)
933         return event->timestamp();
934
935     return QQuickItemPrivate::elapsed(timer);
936 }
937
938 void QQuickFlickablePrivate::handleMousePressEvent(QMouseEvent *event)
939 {
940     Q_Q(QQuickFlickable);
941     QQuickItemPrivate::start(timer);
942     if (interactive && timeline.isActive()
943         && ((qAbs(hData.smoothVelocity.value()) > RetainGrabVelocity && !hData.fixingUp && !hData.inOvershoot)
944             || (qAbs(vData.smoothVelocity.value()) > RetainGrabVelocity && !vData.fixingUp && !vData.inOvershoot))) {
945         stealMouse = true; // If we've been flicked then steal the click.
946         int flickTime = timeline.time();
947         if (flickTime > 600) {
948             // too long between flicks - cancel boost
949             hData.continuousFlickVelocity = 0;
950             vData.continuousFlickVelocity = 0;
951             flickBoost = 1.0;
952         } else {
953             hData.continuousFlickVelocity = -hData.smoothVelocity.value();
954             vData.continuousFlickVelocity = -vData.smoothVelocity.value();
955             if (flickTime > 300) // slower flicking - reduce boost
956                 flickBoost = qMax(1.0, flickBoost - 0.5);
957         }
958     } else {
959         stealMouse = false;
960         hData.continuousFlickVelocity = 0;
961         vData.continuousFlickVelocity = 0;
962         flickBoost = 1.0;
963     }
964     q->setKeepMouseGrab(stealMouse);
965     pressed = true;
966     if (hData.transitionToBounds)
967         hData.transitionToBounds->stopTransition();
968     if (vData.transitionToBounds)
969         vData.transitionToBounds->stopTransition();
970     if (!hData.fixingUp)
971         resetTimeline(hData);
972     if (!vData.fixingUp)
973         resetTimeline(vData);
974
975     hData.reset();
976     vData.reset();
977     hData.dragMinBound = q->minXExtent();
978     vData.dragMinBound = q->minYExtent();
979     hData.dragMaxBound = q->maxXExtent();
980     vData.dragMaxBound = q->maxYExtent();
981     fixupMode = Normal;
982     lastPos = QPointF();
983     pressPos = event->localPos();
984     hData.pressPos = hData.move.value();
985     vData.pressPos = vData.move.value();
986     bool wasFlicking = hData.flicking || vData.flicking;
987     if (hData.flicking) {
988         hData.flicking = false;
989         emit q->flickingHorizontallyChanged();
990     }
991     if (vData.flicking) {
992         vData.flicking = false;
993         emit q->flickingVerticallyChanged();
994     }
995     if (wasFlicking)
996         emit q->flickingChanged();
997     lastPosTime = lastPressTime = computeCurrentTime(event);
998     QQuickItemPrivate::start(vData.velocityTime);
999     QQuickItemPrivate::start(hData.velocityTime);
1000 }
1001
1002 void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event)
1003 {
1004     Q_Q(QQuickFlickable);
1005     if (!interactive || lastPosTime == -1)
1006         return;
1007     bool rejectY = false;
1008     bool rejectX = false;
1009
1010     bool stealY = stealMouse;
1011     bool stealX = stealMouse;
1012
1013     bool prevHMoved = hMoved;
1014     bool prevVMoved = vMoved;
1015
1016     qint64 elapsedSincePress = computeCurrentTime(event) - lastPressTime;
1017     if (q->yflick()) {
1018         qreal dy = event->localPos().y() - pressPos.y();
1019         bool overThreshold = QQuickWindowPrivate::dragOverThreshold(dy, Qt::YAxis, event);
1020         if (overThreshold || elapsedSincePress > 200) {
1021             if (!vMoved)
1022                 vData.dragStartOffset = dy;
1023             qreal newY = dy + vData.pressPos - vData.dragStartOffset;
1024             const qreal minY = vData.dragMinBound;
1025             const qreal maxY = vData.dragMaxBound;
1026             if (newY > minY)
1027                 newY = minY + (newY - minY) / 2;
1028             if (newY < maxY && maxY - minY <= 0)
1029                 newY = maxY + (newY - maxY) / 2;
1030             if (boundsBehavior == QQuickFlickable::StopAtBounds && (newY > minY || newY < maxY)) {
1031                 rejectY = true;
1032                 if (newY < maxY) {
1033                     newY = maxY;
1034                     rejectY = false;
1035                 }
1036                 if (newY > minY) {
1037                     newY = minY;
1038                     rejectY = false;
1039                 }
1040             }
1041             if (!rejectY && stealMouse && dy != 0.0) {
1042                 clearTimeline();
1043                 vData.move.setValue(newY);
1044                 vMoved = true;
1045             }
1046             if (overThreshold)
1047                 stealY = true;
1048         }
1049     }
1050
1051     if (q->xflick()) {
1052         qreal dx = event->localPos().x() - pressPos.x();
1053         bool overThreshold = QQuickWindowPrivate::dragOverThreshold(dx, Qt::XAxis, event);
1054         if (overThreshold || elapsedSincePress > 200) {
1055             if (!hMoved)
1056                 hData.dragStartOffset = dx;
1057             qreal newX = dx + hData.pressPos - hData.dragStartOffset;
1058             const qreal minX = hData.dragMinBound;
1059             const qreal maxX = hData.dragMaxBound;
1060             if (newX > minX)
1061                 newX = minX + (newX - minX) / 2;
1062             if (newX < maxX && maxX - minX <= 0)
1063                 newX = maxX + (newX - maxX) / 2;
1064             if (boundsBehavior == QQuickFlickable::StopAtBounds && (newX > minX || newX < maxX)) {
1065                 rejectX = true;
1066                 if (newX < maxX) {
1067                     newX = maxX;
1068                     rejectX = false;
1069                 }
1070                 if (newX > minX) {
1071                     newX = minX;
1072                     rejectX = false;
1073                 }
1074             }
1075             if (!rejectX && stealMouse && dx != 0.0) {
1076                 clearTimeline();
1077                 hData.move.setValue(newX);
1078                 hMoved = true;
1079             }
1080
1081             if (overThreshold)
1082                 stealX = true;
1083         }
1084     }
1085
1086     stealMouse = stealX || stealY;
1087     if (stealMouse)
1088         q->setKeepMouseGrab(true);
1089
1090     if (rejectY) {
1091         vData.velocityBuffer.clear();
1092         vData.velocity = 0;
1093     }
1094     if (rejectX) {
1095         hData.velocityBuffer.clear();
1096         hData.velocity = 0;
1097     }
1098
1099     if ((hMoved && !prevHMoved) || (vMoved && !prevVMoved)) {
1100         draggingStarting();
1101         q->movementStarting();
1102     }
1103
1104     qint64 currentTimestamp = computeCurrentTime(event);
1105     qreal elapsed = qreal(currentTimestamp - (lastPos.isNull() ? lastPressTime : lastPosTime)) / 1000.;
1106     if (elapsed <= 0)
1107         return;
1108     lastPosTime = currentTimestamp;
1109     if (q->yflick() && !rejectY) {
1110         if (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity) {
1111             vData.addVelocitySample(QGuiApplicationPrivate::mouseEventVelocity(event).y(), maxVelocity);
1112         } else {
1113             qreal dy = event->localPos().y() - (lastPos.isNull() ? pressPos.y() : lastPos.y());
1114             vData.addVelocitySample(dy/elapsed, maxVelocity);
1115         }
1116     }
1117     if (q->xflick() && !rejectX) {
1118         if (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity) {
1119             hData.addVelocitySample(QGuiApplicationPrivate::mouseEventVelocity(event).x(), maxVelocity);
1120         } else {
1121             qreal dx = event->localPos().x() - (lastPos.isNull() ? pressPos.x() : lastPos.x());
1122             hData.addVelocitySample(dx/elapsed, maxVelocity);
1123         }
1124     }
1125
1126     lastPos = event->localPos();
1127 }
1128
1129 void QQuickFlickablePrivate::handleMouseReleaseEvent(QMouseEvent *event)
1130 {
1131     Q_Q(QQuickFlickable);
1132     stealMouse = false;
1133     q->setKeepMouseGrab(false);
1134     pressed = false;
1135
1136     // if we drag then pause before release we should not cause a flick.
1137     qint64 elapsed = computeCurrentTime(event) - lastPosTime;
1138
1139     vData.updateVelocity();
1140     hData.updateVelocity();
1141
1142     draggingEnding();
1143
1144     if (lastPosTime == -1)
1145         return;
1146
1147     hData.vTime = vData.vTime = timeline.time();
1148
1149     bool canBoost = false;
1150
1151     qreal vVelocity = 0;
1152     if (elapsed < 100 && vData.velocity != 0.) {
1153         vVelocity = (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)
1154                 ? QGuiApplicationPrivate::mouseEventVelocity(event).y() : vData.velocity;
1155     }
1156     if ((vData.atBeginning && vVelocity > 0.) || (vData.atEnd && vVelocity < 0.)) {
1157         vVelocity /= 2;
1158     } else if (vData.continuousFlickVelocity != 0.0
1159                && vData.viewSize/q->height() > QML_FLICK_MULTIFLICK_RATIO
1160                && ((vVelocity > 0) == (vData.continuousFlickVelocity > 0))
1161                && qAbs(vVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1162         // accelerate flick for large view flicked quickly
1163         canBoost = true;
1164     }
1165
1166     qreal hVelocity = 0;
1167     if (elapsed < 100 && hData.velocity != 0.) {
1168         hVelocity = (QGuiApplicationPrivate::mouseEventCaps(event) & QTouchDevice::Velocity)
1169                 ? QGuiApplicationPrivate::mouseEventVelocity(event).x() : hData.velocity;
1170     }
1171     if ((hData.atBeginning && hVelocity > 0.) || (hData.atEnd && hVelocity < 0.)) {
1172         hVelocity /= 2;
1173     } else if (hData.continuousFlickVelocity != 0.0
1174                && hData.viewSize/q->width() > QML_FLICK_MULTIFLICK_RATIO
1175                && ((hVelocity > 0) == (hData.continuousFlickVelocity > 0))
1176                && qAbs(hVelocity) > QML_FLICK_MULTIFLICK_THRESHOLD) {
1177         // accelerate flick for large view flicked quickly
1178         canBoost = true;
1179     }
1180
1181     flickBoost = canBoost ? qBound(1.0, flickBoost+0.25, QML_FLICK_MULTIFLICK_MAXBOOST) : 1.0;
1182
1183     bool flickedVertically = false;
1184     vVelocity *= flickBoost;
1185     bool isVerticalFlickAllowed = q->yflick() && qAbs(vVelocity) > MinimumFlickVelocity && qAbs(event->localPos().y() - pressPos.y()) > FlickThreshold;
1186     if (isVerticalFlickAllowed) {
1187         velocityTimeline.reset(vData.smoothVelocity);
1188         vData.smoothVelocity.setValue(-vVelocity);
1189         flickedVertically = flickY(vVelocity);
1190     }
1191
1192     bool flickedHorizontally = false;
1193     hVelocity *= flickBoost;
1194     bool isHorizontalFlickAllowed = q->xflick() && qAbs(hVelocity) > MinimumFlickVelocity && qAbs(event->localPos().x() - pressPos.x()) > FlickThreshold;
1195     if (isHorizontalFlickAllowed) {
1196         velocityTimeline.reset(hData.smoothVelocity);
1197         hData.smoothVelocity.setValue(-hVelocity);
1198         flickedHorizontally = flickX(hVelocity);
1199     }
1200
1201     if (!isVerticalFlickAllowed)
1202         fixupY();
1203
1204     if (!isHorizontalFlickAllowed)
1205         fixupX();
1206
1207     flickingStarted(flickedHorizontally, flickedVertically);
1208     if (!isViewMoving())
1209         q->movementEnding();
1210 }
1211
1212 void QQuickFlickable::mousePressEvent(QMouseEvent *event)
1213 {
1214     Q_D(QQuickFlickable);
1215     if (d->interactive) {
1216         if (!d->pressed)
1217             d->handleMousePressEvent(event);
1218         event->accept();
1219     } else {
1220         QQuickItem::mousePressEvent(event);
1221     }
1222 }
1223
1224 void QQuickFlickable::mouseMoveEvent(QMouseEvent *event)
1225 {
1226     Q_D(QQuickFlickable);
1227     if (d->interactive) {
1228         d->handleMouseMoveEvent(event);
1229         event->accept();
1230     } else {
1231         QQuickItem::mouseMoveEvent(event);
1232     }
1233 }
1234
1235 void QQuickFlickable::mouseReleaseEvent(QMouseEvent *event)
1236 {
1237     Q_D(QQuickFlickable);
1238     if (d->interactive) {
1239         d->clearDelayedPress();
1240         d->handleMouseReleaseEvent(event);
1241         event->accept();
1242         if (window() && window()->mouseGrabberItem() == this)
1243             ungrabMouse();
1244     } else {
1245         QQuickItem::mouseReleaseEvent(event);
1246     }
1247 }
1248
1249 void QQuickFlickable::wheelEvent(QWheelEvent *event)
1250 {
1251     Q_D(QQuickFlickable);
1252     if (!d->interactive) {
1253         QQuickItem::wheelEvent(event);
1254         return;
1255     }
1256
1257     int yDelta = event->angleDelta().y();
1258     int xDelta = event->angleDelta().x();
1259     if (yflick() && yDelta != 0) {
1260         bool valid = false;
1261         if (yDelta > 0 && contentY() > -minYExtent()) {
1262             d->vData.velocity = qMax(yDelta*2 - d->vData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1263             valid = true;
1264         } else if (yDelta < 0 && contentY() < -maxYExtent()) {
1265             d->vData.velocity = qMin(yDelta*2 - d->vData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1266             valid = true;
1267         }
1268         if (valid) {
1269             d->vData.flicking = false;
1270             d->flickY(d->vData.velocity);
1271             d->flickingStarted(false, true);
1272             if (d->vData.flicking) {
1273                 d->vMoved = true;
1274                 movementStarting();
1275             }
1276             event->accept();
1277         }
1278     }
1279     if (xflick() && xDelta != 0) {
1280         bool valid = false;
1281         if (xDelta > 0 && contentX() > -minXExtent()) {
1282             d->hData.velocity = qMax(xDelta*2 - d->hData.smoothVelocity.value(), qreal(d->maxVelocity/4));
1283             valid = true;
1284         } else if (xDelta < 0 && contentX() < -maxXExtent()) {
1285             d->hData.velocity = qMin(xDelta*2 - d->hData.smoothVelocity.value(), qreal(-d->maxVelocity/4));
1286             valid = true;
1287         }
1288         if (valid) {
1289             d->hData.flicking = false;
1290             d->flickX(d->hData.velocity);
1291             d->flickingStarted(true, false);
1292             if (d->hData.flicking) {
1293                 d->hMoved = true;
1294                 movementStarting();
1295             }
1296             event->accept();
1297         }
1298     }
1299
1300     if (!event->isAccepted())
1301         QQuickItem::wheelEvent(event);
1302 }
1303
1304 bool QQuickFlickablePrivate::isOutermostPressDelay() const
1305 {
1306     Q_Q(const QQuickFlickable);
1307     QQuickItem *item = q->parentItem();
1308     while (item) {
1309         QQuickFlickable *flick = qobject_cast<QQuickFlickable*>(item);
1310         if (flick && flick->pressDelay() > 0 && flick->isInteractive())
1311             return false;
1312         item = item->parentItem();
1313     }
1314
1315     return true;
1316 }
1317
1318 void QQuickFlickablePrivate::captureDelayedPress(QMouseEvent *event)
1319 {
1320     Q_Q(QQuickFlickable);
1321     if (!q->window() || pressDelay <= 0)
1322         return;
1323     if (!isOutermostPressDelay())
1324         return;
1325     delayedPressTarget = q->window()->mouseGrabberItem();
1326     delayedPressEvent = QQuickWindowPrivate::cloneMouseEvent(event);
1327     delayedPressEvent->setAccepted(false);
1328     delayedPressTimer.start(pressDelay, q);
1329 }
1330
1331 void QQuickFlickablePrivate::clearDelayedPress()
1332 {
1333     if (delayedPressEvent) {
1334         delayedPressTimer.stop();
1335         delete delayedPressEvent;
1336         delayedPressEvent = 0;
1337     }
1338 }
1339
1340 //XXX pixelAligned ignores the global position of the Flickable, i.e. assumes Flickable itself is pixel aligned.
1341 void QQuickFlickablePrivate::setViewportX(qreal x)
1342 {
1343     contentItem->setX(pixelAligned ? qRound(x) : x);
1344 }
1345
1346 void QQuickFlickablePrivate::setViewportY(qreal y)
1347 {
1348     contentItem->setY(pixelAligned ? qRound(y) : y);
1349 }
1350
1351 void QQuickFlickable::timerEvent(QTimerEvent *event)
1352 {
1353     Q_D(QQuickFlickable);
1354     if (event->timerId() == d->delayedPressTimer.timerId()) {
1355         d->delayedPressTimer.stop();
1356         if (d->delayedPressEvent) {
1357             QQuickItem *grabber = window() ? window()->mouseGrabberItem() : 0;
1358             if (!grabber || grabber != this) {
1359                 // We replay the mouse press but the grabber we had might not be interessted by the event (e.g. overlay)
1360                 // so we reset the grabber
1361                 if (window()->mouseGrabberItem() == d->delayedPressTarget)
1362                     d->delayedPressTarget->ungrabMouse();
1363                 // Use the event handler that will take care of finding the proper item to propagate the event
1364                 QQuickWindowPrivate::get(window())->deliverMouseEvent(d->delayedPressEvent);
1365             }
1366             delete d->delayedPressEvent;
1367             d->delayedPressEvent = 0;
1368         }
1369     }
1370 }
1371
1372 qreal QQuickFlickable::minYExtent() const
1373 {
1374     Q_D(const QQuickFlickable);
1375     return d->vData.startMargin;
1376 }
1377
1378 qreal QQuickFlickable::minXExtent() const
1379 {
1380     Q_D(const QQuickFlickable);
1381     return d->hData.startMargin;
1382 }
1383
1384 /* returns -ve */
1385 qreal QQuickFlickable::maxXExtent() const
1386 {
1387     Q_D(const QQuickFlickable);
1388     return width() - vWidth() - d->hData.endMargin;
1389 }
1390 /* returns -ve */
1391 qreal QQuickFlickable::maxYExtent() const
1392 {
1393     Q_D(const QQuickFlickable);
1394     return height() - vHeight() - d->vData.endMargin;
1395 }
1396
1397 void QQuickFlickable::componentComplete()
1398 {
1399     Q_D(QQuickFlickable);
1400     QQuickItem::componentComplete();
1401     if (!d->hData.explicitValue && d->hData.startMargin != 0.)
1402         setContentX(-minXExtent());
1403     if (!d->vData.explicitValue && d->vData.startMargin != 0.)
1404         setContentY(-minYExtent());
1405 }
1406
1407 void QQuickFlickable::viewportMoved(Qt::Orientations orient)
1408 {
1409     Q_D(QQuickFlickable);
1410     if (orient & Qt::Vertical)
1411         d->viewportAxisMoved(d->vData, minYExtent(), maxYExtent(), height(), d->fixupY_callback);
1412     if (orient & Qt::Horizontal)
1413         d->viewportAxisMoved(d->hData, minXExtent(), maxXExtent(), width(), d->fixupX_callback);
1414     d->updateBeginningEnd();
1415 }
1416
1417 void QQuickFlickablePrivate::viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1418                                            QQuickTimeLineCallback::Callback fixupCallback)
1419 {
1420     if (pressed || calcVelocity) {
1421         int elapsed = QQuickItemPrivate::restart(data.velocityTime);
1422         if (elapsed > 0) {
1423             qreal velocity = (data.lastPos - data.move.value()) * 1000 / elapsed;
1424             if (qAbs(velocity) > 0) {
1425                 velocityTimeline.reset(data.smoothVelocity);
1426                 if (calcVelocity)
1427                     velocityTimeline.set(data.smoothVelocity, velocity);
1428                 else
1429                     velocityTimeline.move(data.smoothVelocity, velocity, reportedVelocitySmoothing);
1430                 velocityTimeline.move(data.smoothVelocity, 0, reportedVelocitySmoothing);
1431             }
1432         }
1433     } else {
1434         if (timeline.time() > data.vTime) {
1435             velocityTimeline.reset(data.smoothVelocity);
1436             qreal velocity = (data.lastPos - data.move.value()) * 1000 / (timeline.time() - data.vTime);
1437             data.smoothVelocity.setValue(velocity);
1438         }
1439     }
1440
1441     if (!data.inOvershoot && !data.fixingUp && data.flicking
1442             && (data.move.value() > minExtent || data.move.value() < maxExtent)
1443             && qAbs(data.smoothVelocity.value()) > 10) {
1444         // Increase deceleration if we've passed a bound
1445         qreal overBound = data.move.value() > minExtent
1446                 ? data.move.value() - minExtent
1447                 : maxExtent - data.move.value();
1448         data.inOvershoot = true;
1449         qreal maxDistance = overShootDistance(vSize) - overBound;
1450         resetTimeline(data);
1451         if (maxDistance > 0)
1452             timeline.accel(data.move, -data.smoothVelocity.value(), deceleration*QML_FLICK_OVERSHOOTFRICTION, maxDistance);
1453         timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1454     }
1455
1456     data.lastPos = data.move.value();
1457     data.vTime = timeline.time();
1458 }
1459
1460 void QQuickFlickable::geometryChanged(const QRectF &newGeometry,
1461                              const QRectF &oldGeometry)
1462 {
1463     Q_D(QQuickFlickable);
1464     QQuickItem::geometryChanged(newGeometry, oldGeometry);
1465
1466     bool changed = false;
1467     if (newGeometry.width() != oldGeometry.width()) {
1468         if (xflick())
1469             changed = true;
1470         if (d->hData.viewSize < 0) {
1471             d->contentItem->setWidth(width());
1472             emit contentWidthChanged();
1473         }
1474         // Make sure that we're entirely in view.
1475         if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1476             d->fixupMode = QQuickFlickablePrivate::Immediate;
1477             d->fixupX();
1478         }
1479     }
1480     if (newGeometry.height() != oldGeometry.height()) {
1481         if (yflick())
1482             changed = true;
1483         if (d->vData.viewSize < 0) {
1484             d->contentItem->setHeight(height());
1485             emit contentHeightChanged();
1486         }
1487         // Make sure that we're entirely in view.
1488         if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1489             d->fixupMode = QQuickFlickablePrivate::Immediate;
1490             d->fixupY();
1491         }
1492     }
1493
1494     if (changed)
1495         d->updateBeginningEnd();
1496 }
1497
1498 /*!
1499     \qmlmethod QtQuick2::Flickable::flick(qreal xVelocity, qreal yVelocity)
1500
1501     Flicks the content with \a xVelocity horizontally and \a yVelocity vertically in pixels/sec.
1502 */
1503
1504 void QQuickFlickable::flick(qreal xVelocity, qreal yVelocity)
1505 {
1506     Q_D(QQuickFlickable);
1507     bool flickedX = d->flickX(xVelocity);
1508     bool flickedY = d->flickY(yVelocity);
1509     d->flickingStarted(flickedX, flickedY);
1510 }
1511
1512 void QQuickFlickablePrivate::flickingStarted(bool flickingH, bool flickingV)
1513 {
1514     Q_Q(QQuickFlickable);
1515     if (!flickingH && !flickingV)
1516         return;
1517
1518     bool wasFlicking = hData.flicking || vData.flicking;
1519     if (flickingH && !hData.flicking) {
1520         hData.flicking = true;
1521         emit q->flickingHorizontallyChanged();
1522     }
1523     if (flickingV && !vData.flicking) {
1524         vData.flicking = true;
1525         emit q->flickingVerticallyChanged();
1526     }
1527     if (!wasFlicking && (hData.flicking || vData.flicking)) {
1528         emit q->flickingChanged();
1529         emit q->flickStarted();
1530     }
1531 }
1532
1533 /*!
1534     \qmlmethod QtQuick2::Flickable::cancelFlick()
1535
1536     Cancels the current flick animation.
1537 */
1538
1539 void QQuickFlickable::cancelFlick()
1540 {
1541     Q_D(QQuickFlickable);
1542     d->resetTimeline(d->hData);
1543     d->resetTimeline(d->vData);
1544     movementEnding();
1545 }
1546
1547 void QQuickFlickablePrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o)
1548 {
1549     if (QQuickItem *i = qmlobject_cast<QQuickItem *>(o)) {
1550         i->setParentItem(static_cast<QQuickFlickablePrivate*>(prop->data)->contentItem);
1551     } else {
1552         o->setParent(prop->object); // XXX todo - do we want this?
1553     }
1554 }
1555
1556 int QQuickFlickablePrivate::data_count(QQmlListProperty<QObject> *)
1557 {
1558     // XXX todo
1559     return 0;
1560 }
1561
1562 QObject *QQuickFlickablePrivate::data_at(QQmlListProperty<QObject> *, int)
1563 {
1564     // XXX todo
1565     return 0;
1566 }
1567
1568 void QQuickFlickablePrivate::data_clear(QQmlListProperty<QObject> *)
1569 {
1570     // XXX todo
1571 }
1572
1573 QQmlListProperty<QObject> QQuickFlickable::flickableData()
1574 {
1575     Q_D(QQuickFlickable);
1576     return QQmlListProperty<QObject>(this, (void *)d, QQuickFlickablePrivate::data_append,
1577                                              QQuickFlickablePrivate::data_count,
1578                                              QQuickFlickablePrivate::data_at,
1579                                              QQuickFlickablePrivate::data_clear);
1580 }
1581
1582 QQmlListProperty<QQuickItem> QQuickFlickable::flickableChildren()
1583 {
1584     Q_D(QQuickFlickable);
1585     return QQuickItemPrivate::get(d->contentItem)->children();
1586 }
1587
1588 /*!
1589     \qmlproperty enumeration QtQuick2::Flickable::boundsBehavior
1590     This property holds whether the surface may be dragged
1591     beyond the Flickable's boundaries, or overshoot the
1592     Flickable's boundaries when flicked.
1593
1594     This enables the feeling that the edges of the view are soft,
1595     rather than a hard physical boundary.
1596
1597     The \c boundsBehavior can be one of:
1598
1599     \list
1600     \li Flickable.StopAtBounds - the contents can not be dragged beyond the boundary
1601     of the flickable, and flicks will not overshoot.
1602     \li Flickable.DragOverBounds - the contents can be dragged beyond the boundary
1603     of the Flickable, but flicks will not overshoot.
1604     \li Flickable.DragAndOvershootBounds (default) - the contents can be dragged
1605     beyond the boundary of the Flickable, and can overshoot the
1606     boundary when flicked.
1607     \endlist
1608 */
1609 QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior() const
1610 {
1611     Q_D(const QQuickFlickable);
1612     return d->boundsBehavior;
1613 }
1614
1615 void QQuickFlickable::setBoundsBehavior(BoundsBehavior b)
1616 {
1617     Q_D(QQuickFlickable);
1618     if (b == d->boundsBehavior)
1619         return;
1620     d->boundsBehavior = b;
1621     emit boundsBehaviorChanged();
1622 }
1623
1624 /*!
1625     \qmlproperty Transition QtQuick2::Flickable::rebound
1626
1627     This holds the transition to be applied to the content view when
1628     it snaps back to the bounds of the flickable. The transition is
1629     triggered when the view is flicked or dragged past the edge of the
1630     content area, or when returnToBounds() is called.
1631
1632     \qml
1633     import QtQuick 2.0
1634
1635     Flickable {
1636         width: 150; height: 150
1637         contentWidth: 300; contentHeight: 300
1638
1639         rebound: Transition {
1640             NumberAnimation {
1641                 properties: "x,y"
1642                 duration: 1000
1643                 easing.type: Easing.OutBounce
1644             }
1645         }
1646
1647         Rectangle {
1648             width: 300; height: 300
1649             gradient: Gradient {
1650                 GradientStop { position: 0.0; color: "lightsteelblue" }
1651                 GradientStop { position: 1.0; color: "blue" }
1652             }
1653         }
1654     }
1655     \endqml
1656
1657     When the above view is flicked beyond its bounds, it will return to its
1658     bounds using the transition specified:
1659
1660     \image flickable-rebound.gif
1661
1662     If this property is not set, a default animation is applied.
1663   */
1664 QQuickTransition *QQuickFlickable::rebound() const
1665 {
1666     Q_D(const QQuickFlickable);
1667     return d->rebound;
1668 }
1669
1670 void QQuickFlickable::setRebound(QQuickTransition *transition)
1671 {
1672     Q_D(QQuickFlickable);
1673     if (transition) {
1674         if (!d->hData.transitionToBounds)
1675             d->hData.transitionToBounds = new QQuickFlickableReboundTransition(this, QLatin1String("x"));
1676         if (!d->vData.transitionToBounds)
1677             d->vData.transitionToBounds = new QQuickFlickableReboundTransition(this, QLatin1String("y"));
1678     }
1679     if (d->rebound != transition) {
1680         d->rebound = transition;
1681         emit reboundChanged();
1682     }
1683 }
1684
1685 /*!
1686     \qmlproperty real QtQuick2::Flickable::contentWidth
1687     \qmlproperty real QtQuick2::Flickable::contentHeight
1688
1689     The dimensions of the content (the surface controlled by Flickable).
1690     This should typically be set to the combined size of the items placed in the
1691     Flickable.
1692
1693     The following snippet shows how these properties are used to display
1694     an image that is larger than the Flickable item itself:
1695
1696     \snippet qml/flickable.qml document
1697
1698     In some cases, the content dimensions can be automatically set
1699     based on the \l {Item::childrenRect.width}{childrenRect.width}
1700     and \l {Item::childrenRect.height}{childrenRect.height} properties
1701     of the \l contentItem. For example, the previous snippet could be rewritten with:
1702
1703     \qml
1704     contentWidth: contentItem.childrenRect.width; contentHeight: contentItem.childrenRect.height
1705     \endqml
1706
1707     Though this assumes that the origin of the childrenRect is 0,0.
1708 */
1709 qreal QQuickFlickable::contentWidth() const
1710 {
1711     Q_D(const QQuickFlickable);
1712     return d->hData.viewSize;
1713 }
1714
1715 void QQuickFlickable::setContentWidth(qreal w)
1716 {
1717     Q_D(QQuickFlickable);
1718     if (d->hData.viewSize == w)
1719         return;
1720     d->hData.viewSize = w;
1721     if (w < 0)
1722         d->contentItem->setWidth(width());
1723     else
1724         d->contentItem->setWidth(w);
1725     d->hData.markExtentsDirty();
1726     // Make sure that we're entirely in view.
1727     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1728         d->fixupMode = QQuickFlickablePrivate::Immediate;
1729         d->fixupX();
1730     } else if (!d->pressed && d->hData.fixingUp) {
1731         d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1732         d->fixupX();
1733     }
1734     emit contentWidthChanged();
1735     d->updateBeginningEnd();
1736 }
1737
1738 qreal QQuickFlickable::contentHeight() const
1739 {
1740     Q_D(const QQuickFlickable);
1741     return d->vData.viewSize;
1742 }
1743
1744 void QQuickFlickable::setContentHeight(qreal h)
1745 {
1746     Q_D(QQuickFlickable);
1747     if (d->vData.viewSize == h)
1748         return;
1749     d->vData.viewSize = h;
1750     if (h < 0)
1751         d->contentItem->setHeight(height());
1752     else
1753         d->contentItem->setHeight(h);
1754     d->vData.markExtentsDirty();
1755     // Make sure that we're entirely in view.
1756     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1757         d->fixupMode = QQuickFlickablePrivate::Immediate;
1758         d->fixupY();
1759     } else if (!d->pressed && d->vData.fixingUp) {
1760         d->fixupMode = QQuickFlickablePrivate::ExtentChanged;
1761         d->fixupY();
1762     }
1763     emit contentHeightChanged();
1764     d->updateBeginningEnd();
1765 }
1766
1767 /*!
1768     \qmlproperty real QtQuick2::Flickable::topMargin
1769     \qmlproperty real QtQuick2::Flickable::leftMargin
1770     \qmlproperty real QtQuick2::Flickable::bottomMargin
1771     \qmlproperty real QtQuick2::Flickable::rightMargin
1772
1773     These properties hold the margins around the content.  This space is reserved
1774     in addition to the contentWidth and contentHeight.
1775 */
1776
1777
1778 qreal QQuickFlickable::topMargin() const
1779 {
1780     Q_D(const QQuickFlickable);
1781     return d->vData.startMargin;
1782 }
1783
1784 void QQuickFlickable::setTopMargin(qreal m)
1785 {
1786     Q_D(QQuickFlickable);
1787     if (d->vData.startMargin == m)
1788         return;
1789     d->vData.startMargin = m;
1790     d->vData.markExtentsDirty();
1791     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1792         d->fixupMode = QQuickFlickablePrivate::Immediate;
1793         d->fixupY();
1794     }
1795     emit topMarginChanged();
1796     d->updateBeginningEnd();
1797 }
1798
1799 qreal QQuickFlickable::bottomMargin() const
1800 {
1801     Q_D(const QQuickFlickable);
1802     return d->vData.endMargin;
1803 }
1804
1805 void QQuickFlickable::setBottomMargin(qreal m)
1806 {
1807     Q_D(QQuickFlickable);
1808     if (d->vData.endMargin == m)
1809         return;
1810     d->vData.endMargin = m;
1811     d->vData.markExtentsDirty();
1812     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1813         d->fixupMode = QQuickFlickablePrivate::Immediate;
1814         d->fixupY();
1815     }
1816     emit bottomMarginChanged();
1817     d->updateBeginningEnd();
1818 }
1819
1820 qreal QQuickFlickable::leftMargin() const
1821 {
1822     Q_D(const QQuickFlickable);
1823     return d->hData.startMargin;
1824 }
1825
1826 void QQuickFlickable::setLeftMargin(qreal m)
1827 {
1828     Q_D(QQuickFlickable);
1829     if (d->hData.startMargin == m)
1830         return;
1831     d->hData.startMargin = m;
1832     d->hData.markExtentsDirty();
1833     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1834         d->fixupMode = QQuickFlickablePrivate::Immediate;
1835         d->fixupX();
1836     }
1837     emit leftMarginChanged();
1838     d->updateBeginningEnd();
1839 }
1840
1841 qreal QQuickFlickable::rightMargin() const
1842 {
1843     Q_D(const QQuickFlickable);
1844     return d->hData.endMargin;
1845 }
1846
1847 void QQuickFlickable::setRightMargin(qreal m)
1848 {
1849     Q_D(QQuickFlickable);
1850     if (d->hData.endMargin == m)
1851         return;
1852     d->hData.endMargin = m;
1853     d->hData.markExtentsDirty();
1854     if (!d->pressed && !d->hData.moving && !d->vData.moving) {
1855         d->fixupMode = QQuickFlickablePrivate::Immediate;
1856         d->fixupX();
1857     }
1858     emit rightMarginChanged();
1859     d->updateBeginningEnd();
1860 }
1861
1862 /*!
1863     \qmlproperty real QtQuick2::Flickable::originX
1864     \qmlproperty real QtQuick2::Flickable::originY
1865
1866     These properties hold the origin of the content. This value always refers
1867     to the top-left position of the content regardless of layout direction.
1868
1869     This is usually (0,0), however ListView and GridView may have an arbitrary
1870     origin due to delegate size variation, or item insertion/removal outside
1871     the visible region.
1872 */
1873
1874 qreal QQuickFlickable::originY() const
1875 {
1876     Q_D(const QQuickFlickable);
1877     return -minYExtent() + d->vData.startMargin;
1878 }
1879
1880 qreal QQuickFlickable::originX() const
1881 {
1882     Q_D(const QQuickFlickable);
1883     return -minXExtent() + d->hData.startMargin;
1884 }
1885
1886
1887 /*!
1888     \qmlmethod QtQuick2::Flickable::resizeContent(real width, real height, QPointF center)
1889
1890     Resizes the content to \a width x \a height about \a center.
1891
1892     This does not scale the contents of the Flickable - it only resizes the \l contentWidth
1893     and \l contentHeight.
1894
1895     Resizing the content may result in the content being positioned outside
1896     the bounds of the Flickable.  Calling \l returnToBounds() will
1897     move the content back within legal bounds.
1898 */
1899 void QQuickFlickable::resizeContent(qreal w, qreal h, QPointF center)
1900 {
1901     Q_D(QQuickFlickable);
1902     if (w != d->hData.viewSize) {
1903         qreal oldSize = d->hData.viewSize;
1904         d->hData.viewSize = w;
1905         d->contentItem->setWidth(w);
1906         emit contentWidthChanged();
1907         if (center.x() != 0) {
1908             qreal pos = center.x() * w / oldSize;
1909             setContentX(contentX() + pos - center.x());
1910         }
1911     }
1912     if (h != d->vData.viewSize) {
1913         qreal oldSize = d->vData.viewSize;
1914         d->vData.viewSize = h;
1915         d->contentItem->setHeight(h);
1916         emit contentHeightChanged();
1917         if (center.y() != 0) {
1918             qreal pos = center.y() * h / oldSize;
1919             setContentY(contentY() + pos - center.y());
1920         }
1921     }
1922     d->updateBeginningEnd();
1923 }
1924
1925 /*!
1926     \qmlmethod QtQuick2::Flickable::returnToBounds()
1927
1928     Ensures the content is within legal bounds.
1929
1930     This may be called to ensure that the content is within legal bounds
1931     after manually positioning the content.
1932 */
1933 void QQuickFlickable::returnToBounds()
1934 {
1935     Q_D(QQuickFlickable);
1936     d->fixupX();
1937     d->fixupY();
1938 }
1939
1940 qreal QQuickFlickable::vWidth() const
1941 {
1942     Q_D(const QQuickFlickable);
1943     if (d->hData.viewSize < 0)
1944         return width();
1945     else
1946         return d->hData.viewSize;
1947 }
1948
1949 qreal QQuickFlickable::vHeight() const
1950 {
1951     Q_D(const QQuickFlickable);
1952     if (d->vData.viewSize < 0)
1953         return height();
1954     else
1955         return d->vData.viewSize;
1956 }
1957
1958 bool QQuickFlickable::xflick() const
1959 {
1960     Q_D(const QQuickFlickable);
1961     if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1962         return floor(qAbs(vWidth() - width()));
1963     return d->flickableDirection & QQuickFlickable::HorizontalFlick;
1964 }
1965
1966 bool QQuickFlickable::yflick() const
1967 {
1968     Q_D(const QQuickFlickable);
1969     if (d->flickableDirection == QQuickFlickable::AutoFlickDirection)
1970         return floor(qAbs(vHeight() - height()));
1971     return d->flickableDirection & QQuickFlickable::VerticalFlick;
1972 }
1973
1974 void QQuickFlickable::mouseUngrabEvent()
1975 {
1976     Q_D(QQuickFlickable);
1977     if (d->pressed) {
1978         // if our mouse grab has been removed (probably by another Flickable),
1979         // fix our state
1980         d->clearDelayedPress();
1981         d->pressed = false;
1982         d->draggingEnding();
1983         d->stealMouse = false;
1984         setKeepMouseGrab(false);
1985         d->fixupX();
1986         d->fixupY();
1987         if (!d->isViewMoving())
1988             movementEnding();
1989     }
1990 }
1991
1992 bool QQuickFlickable::sendMouseEvent(QMouseEvent *event)
1993 {
1994     Q_D(QQuickFlickable);
1995     QPointF localPos = mapFromScene(event->windowPos());
1996
1997     QQuickWindow *c = window();
1998     QQuickItem *grabber = c ? c->mouseGrabberItem() : 0;
1999     bool grabberDisabled = grabber && !grabber->isEnabled();
2000     bool stealThisEvent = d->stealMouse;
2001     if ((stealThisEvent || contains(localPos)) && (!grabber || !grabber->keepMouseGrab() || grabberDisabled)) {
2002         QScopedPointer<QMouseEvent> mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, &localPos));
2003         mouseEvent->setAccepted(false);
2004
2005         switch (mouseEvent->type()) {
2006         case QEvent::MouseMove:
2007             d->handleMouseMoveEvent(mouseEvent.data());
2008             break;
2009         case QEvent::MouseButtonPress:
2010             if (d->pressed) // we are already pressed - this is a delayed replay
2011                 return false;
2012
2013             d->handleMousePressEvent(mouseEvent.data());
2014             d->captureDelayedPress(event);
2015             stealThisEvent = d->stealMouse;   // Update stealThisEvent in case changed by function call above
2016             break;
2017         case QEvent::MouseButtonRelease:
2018             if (d->delayedPressEvent) {
2019                 // We replay the mouse press but the grabber we had might not be interested in the event (e.g. overlay)
2020                 // so we reset the grabber
2021                 if (c->mouseGrabberItem() == d->delayedPressTarget)
2022                     d->delayedPressTarget->ungrabMouse();
2023                 // Use the event handler that will take care of finding the proper item to propagate the event
2024                 QQuickWindowPrivate::get(window())->deliverMouseEvent(d->delayedPressEvent);
2025                 d->clearDelayedPress();
2026                 // We send the release
2027                 window()->sendEvent(c->mouseGrabberItem(), event);
2028                 // And the event has been consumed
2029                 d->stealMouse = false;
2030                 d->pressed = false;
2031                 return true;
2032             }
2033             d->handleMouseReleaseEvent(mouseEvent.data());
2034             break;
2035         default:
2036             break;
2037         }
2038         grabber = qobject_cast<QQuickItem*>(c->mouseGrabberItem());
2039         if ((grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this) || grabberDisabled) {
2040             d->clearDelayedPress();
2041             grabMouse();
2042         }
2043
2044         return stealThisEvent || d->delayedPressEvent || grabberDisabled;
2045     } else if (d->lastPosTime != -1) {
2046         d->lastPosTime = -1;
2047         returnToBounds();
2048     }
2049     if (event->type() == QEvent::MouseButtonRelease) {
2050         d->lastPosTime = -1;
2051         d->clearDelayedPress();
2052         d->stealMouse = false;
2053         d->pressed = false;
2054     }
2055     return false;
2056 }
2057
2058
2059 bool QQuickFlickable::childMouseEventFilter(QQuickItem *i, QEvent *e)
2060 {
2061     Q_D(QQuickFlickable);
2062     if (!isVisible() || !isEnabled())
2063         return QQuickItem::childMouseEventFilter(i, e);
2064     switch (e->type()) {
2065     case QEvent::MouseButtonPress:
2066     case QEvent::MouseMove:
2067     case QEvent::MouseButtonRelease:
2068         return sendMouseEvent(static_cast<QMouseEvent *>(e));
2069     case QEvent::UngrabMouse:
2070         if (d->window && d->window->mouseGrabberItem() && d->window->mouseGrabberItem() != this) {
2071             // The grab has been taken away from a child and given to some other item.
2072             mouseUngrabEvent();
2073         }
2074         break;
2075     default:
2076         break;
2077     }
2078
2079     return QQuickItem::childMouseEventFilter(i, e);
2080 }
2081
2082 /*!
2083     \qmlproperty real QtQuick2::Flickable::maximumFlickVelocity
2084     This property holds the maximum velocity that the user can flick the view in pixels/second.
2085
2086     The default value is platform dependent.
2087 */
2088 qreal QQuickFlickable::maximumFlickVelocity() const
2089 {
2090     Q_D(const QQuickFlickable);
2091     return d->maxVelocity;
2092 }
2093
2094 void QQuickFlickable::setMaximumFlickVelocity(qreal v)
2095 {
2096     Q_D(QQuickFlickable);
2097     if (v == d->maxVelocity)
2098         return;
2099     d->maxVelocity = v;
2100     emit maximumFlickVelocityChanged();
2101 }
2102
2103 /*!
2104     \qmlproperty real QtQuick2::Flickable::flickDeceleration
2105     This property holds the rate at which a flick will decelerate.
2106
2107     The default value is platform dependent.
2108 */
2109 qreal QQuickFlickable::flickDeceleration() const
2110 {
2111     Q_D(const QQuickFlickable);
2112     return d->deceleration;
2113 }
2114
2115 void QQuickFlickable::setFlickDeceleration(qreal deceleration)
2116 {
2117     Q_D(QQuickFlickable);
2118     if (deceleration == d->deceleration)
2119         return;
2120     d->deceleration = deceleration;
2121     emit flickDecelerationChanged();
2122 }
2123
2124 bool QQuickFlickable::isFlicking() const
2125 {
2126     Q_D(const QQuickFlickable);
2127     return d->hData.flicking ||  d->vData.flicking;
2128 }
2129
2130 /*!
2131     \qmlproperty bool QtQuick2::Flickable::flicking
2132     \qmlproperty bool QtQuick2::Flickable::flickingHorizontally
2133     \qmlproperty bool QtQuick2::Flickable::flickingVertically
2134
2135     These properties describe whether the view is currently moving horizontally,
2136     vertically or in either direction, due to the user flicking the view.
2137 */
2138 bool QQuickFlickable::isFlickingHorizontally() const
2139 {
2140     Q_D(const QQuickFlickable);
2141     return d->hData.flicking;
2142 }
2143
2144 bool QQuickFlickable::isFlickingVertically() const
2145 {
2146     Q_D(const QQuickFlickable);
2147     return d->vData.flicking;
2148 }
2149
2150 /*!
2151     \qmlproperty bool QtQuick2::Flickable::dragging
2152     \qmlproperty bool QtQuick2::Flickable::draggingHorizontally
2153     \qmlproperty bool QtQuick2::Flickable::draggingVertically
2154
2155     These properties describe whether the view is currently moving horizontally,
2156     vertically or in either direction, due to the user dragging the view.
2157 */
2158 bool QQuickFlickable::isDragging() const
2159 {
2160     Q_D(const QQuickFlickable);
2161     return d->hData.dragging ||  d->vData.dragging;
2162 }
2163
2164 bool QQuickFlickable::isDraggingHorizontally() const
2165 {
2166     Q_D(const QQuickFlickable);
2167     return d->hData.dragging;
2168 }
2169
2170 bool QQuickFlickable::isDraggingVertically() const
2171 {
2172     Q_D(const QQuickFlickable);
2173     return d->vData.dragging;
2174 }
2175
2176 void QQuickFlickablePrivate::draggingStarting()
2177 {
2178     Q_Q(QQuickFlickable);
2179     bool wasDragging = hData.dragging || vData.dragging;
2180     if (hMoved && !hData.dragging) {
2181         hData.dragging = true;
2182         emit q->draggingHorizontallyChanged();
2183     }
2184     if (vMoved && !vData.dragging) {
2185         vData.dragging = true;
2186         emit q->draggingVerticallyChanged();
2187     }
2188     if (!wasDragging && (hData.dragging || vData.dragging)) {
2189         emit q->draggingChanged();
2190         emit q->dragStarted();
2191     }
2192 }
2193
2194 void QQuickFlickablePrivate::draggingEnding()
2195 {
2196     Q_Q(QQuickFlickable);
2197     bool wasDragging = hData.dragging || vData.dragging;
2198     if (hData.dragging) {
2199         hData.dragging = false;
2200         emit q->draggingHorizontallyChanged();
2201     }
2202     if (vData.dragging) {
2203         vData.dragging = false;
2204         emit q->draggingVerticallyChanged();
2205     }
2206     if (wasDragging && !hData.dragging && !vData.dragging) {
2207         emit q->draggingChanged();
2208         emit q->dragEnded();
2209     }
2210 }
2211
2212 bool QQuickFlickablePrivate::isViewMoving() const
2213 {
2214     if (timeline.isActive()
2215             || (hData.transitionToBounds && hData.transitionToBounds->isActive())
2216             || (vData.transitionToBounds && vData.transitionToBounds->isActive()) ) {
2217         return true;
2218     }
2219     return false;
2220 }
2221
2222 /*!
2223     \qmlproperty int QtQuick2::Flickable::pressDelay
2224
2225     This property holds the time to delay (ms) delivering a press to
2226     children of the Flickable.  This can be useful where reacting
2227     to a press before a flicking action has undesirable effects.
2228
2229     If the flickable is dragged/flicked before the delay times out
2230     the press event will not be delivered.  If the button is released
2231     within the timeout, both the press and release will be delivered.
2232
2233     Note that for nested Flickables with pressDelay set, the pressDelay of
2234     inner Flickables is overridden by the outermost Flickable.
2235 */
2236 int QQuickFlickable::pressDelay() const
2237 {
2238     Q_D(const QQuickFlickable);
2239     return d->pressDelay;
2240 }
2241
2242 void QQuickFlickable::setPressDelay(int delay)
2243 {
2244     Q_D(QQuickFlickable);
2245     if (d->pressDelay == delay)
2246         return;
2247     d->pressDelay = delay;
2248     emit pressDelayChanged();
2249 }
2250
2251 /*!
2252     \qmlproperty bool QtQuick2::Flickable::moving
2253     \qmlproperty bool QtQuick2::Flickable::movingHorizontally
2254     \qmlproperty bool QtQuick2::Flickable::movingVertically
2255
2256     These properties describe whether the view is currently moving horizontally,
2257     vertically or in either direction, due to the user either dragging or
2258     flicking the view.
2259 */
2260
2261 bool QQuickFlickable::isMoving() const
2262 {
2263     Q_D(const QQuickFlickable);
2264     return d->hData.moving || d->vData.moving;
2265 }
2266
2267 bool QQuickFlickable::isMovingHorizontally() const
2268 {
2269     Q_D(const QQuickFlickable);
2270     return d->hData.moving;
2271 }
2272
2273 bool QQuickFlickable::isMovingVertically() const
2274 {
2275     Q_D(const QQuickFlickable);
2276     return d->vData.moving;
2277 }
2278
2279 void QQuickFlickable::timelineCompleted()
2280 {
2281     Q_D(QQuickFlickable);
2282     if ( (d->hData.transitionToBounds && d->hData.transitionToBounds->isActive())
2283          || (d->vData.transitionToBounds && d->vData.transitionToBounds->isActive()) ) {
2284         return;
2285     }
2286     movementEnding();
2287 }
2288
2289 void QQuickFlickable::movementStarting()
2290 {
2291     Q_D(QQuickFlickable);
2292     bool wasMoving = d->hData.moving || d->vData.moving;
2293     if (d->hMoved && !d->hData.moving) {
2294         d->hData.moving = true;
2295         emit movingHorizontallyChanged();
2296     }
2297     if (d->vMoved && !d->vData.moving) {
2298         d->vData.moving = true;
2299         emit movingVerticallyChanged();
2300     }
2301
2302     if (!wasMoving && (d->hData.moving || d->vData.moving)) {
2303         emit movingChanged();
2304         emit movementStarted();
2305     }
2306 }
2307
2308 void QQuickFlickable::movementEnding()
2309 {
2310     movementEnding(true, true);
2311 }
2312
2313 void QQuickFlickable::movementEnding(bool hMovementEnding, bool vMovementEnding)
2314 {
2315     Q_D(QQuickFlickable);
2316
2317     // emit flicking signals
2318     bool wasFlicking = d->hData.flicking || d->vData.flicking;
2319     if (hMovementEnding && d->hData.flicking) {
2320         d->hData.flicking = false;
2321         emit flickingHorizontallyChanged();
2322     }
2323     if (vMovementEnding && d->vData.flicking) {
2324         d->vData.flicking = false;
2325         emit flickingVerticallyChanged();
2326     }
2327     if (wasFlicking && (!d->hData.flicking || !d->vData.flicking)) {
2328         emit flickingChanged();
2329         emit flickEnded();
2330     }
2331
2332     // emit moving signals
2333     bool wasMoving = d->hData.moving || d->vData.moving;
2334     if (hMovementEnding && d->hData.moving
2335             && (!d->pressed && !d->stealMouse)) {
2336         d->hData.moving = false;
2337         d->hMoved = false;
2338         emit movingHorizontallyChanged();
2339     }
2340     if (vMovementEnding && d->vData.moving
2341             && (!d->pressed && !d->stealMouse)) {
2342         d->vData.moving = false;
2343         d->vMoved = false;
2344         emit movingVerticallyChanged();
2345     }
2346     if (wasMoving && (!d->hData.moving || !d->vData.moving)) {
2347         emit movingChanged();
2348         emit movementEnded();
2349     }
2350
2351     if (hMovementEnding) {
2352         d->hData.fixingUp = false;
2353         d->hData.smoothVelocity.setValue(0);
2354     }
2355     if (vMovementEnding) {
2356         d->vData.fixingUp = false;
2357         d->vData.smoothVelocity.setValue(0);
2358     }
2359 }
2360
2361 void QQuickFlickablePrivate::updateVelocity()
2362 {
2363     Q_Q(QQuickFlickable);
2364     emit q->horizontalVelocityChanged();
2365     emit q->verticalVelocityChanged();
2366 }
2367
2368 QT_END_NAMESPACE