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