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