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