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