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