Fixes warnings about unused variables
[profile/ivi/qtbase.git] / src / gui / util / qscroller.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 QtGui module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qevent.h"
43 #include "qwidget.h"
44 #include "qscroller.h"
45 #include "private/qflickgesture_p.h"
46 #include "private/qscroller_p.h"
47 #include "qscrollerproperties.h"
48 #include "private/qscrollerproperties_p.h"
49 #include "qnumeric.h"
50 #include "math.h"
51
52 #include <QTime>
53 #include <QElapsedTimer>
54 #include <QMap>
55 #include <QApplication>
56 #include <QAbstractScrollArea>
57 #include <QGraphicsObject>
58 #include <QGraphicsScene>
59 #include <QGraphicsView>
60 #include <QDesktopWidget>
61 #include <QtCore/qmath.h>
62 #include <QtGui/qevent.h>
63 #include <qnumeric.h>
64
65 #include <QtDebug>
66
67 #if defined(Q_WS_X11)
68 #  include "private/qt_x11_p.h"
69 #endif
70
71
72 QT_BEGIN_NAMESPACE
73
74 bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event);
75
76 //#define QSCROLLER_DEBUG
77
78 #ifdef QSCROLLER_DEBUG
79 #  define qScrollerDebug  qDebug
80 #else
81 #  define qScrollerDebug  while (false) qDebug
82 #endif
83
84 QDebug &operator<<(QDebug &dbg, const QScrollerPrivate::ScrollSegment &s)
85 {
86     dbg << "\n  Time: start:" << s.startTime << " duration:" << s.deltaTime << " stop progress:" << s.stopProgress;
87     dbg << "\n  Pos: start:" << s.startPos << " delta:" << s.deltaPos << " stop:" << s.stopPos;
88     dbg << "\n  Curve: type:" << s.curve.type() << "\n";
89     return dbg;
90 }
91
92
93 // a few helper operators to make the code below a lot more readable:
94 // otherwise a lot of ifs would have to be multi-line to check both the x
95 // and y coordinate separately.
96
97 // returns true only if the abs. value of BOTH x and y are <= f
98 inline bool operator<=(const QPointF &p, qreal f)
99 {
100     return (qAbs(p.x()) <= f) && (qAbs(p.y()) <= f);
101 }
102
103 // returns true only if the abs. value of BOTH x and y are < f
104 inline bool operator<(const QPointF &p, qreal f)
105 {
106     return (qAbs(p.x()) < f) && (qAbs(p.y()) < f);
107 }
108
109 // returns true if the abs. value of EITHER x or y are >= f
110 inline bool operator>=(const QPointF &p, qreal f)
111 {
112     return (qAbs(p.x()) >= f) || (qAbs(p.y()) >= f);
113 }
114
115 // returns true if the abs. value of EITHER x or y are > f
116 inline bool operator>(const QPointF &p, qreal f)
117 {
118     return (qAbs(p.x()) > f) || (qAbs(p.y()) > f);
119 }
120
121 // returns a new point with both coordinates having the abs. value of the original one
122 inline QPointF qAbs(const QPointF &p)
123 {
124     return QPointF(qAbs(p.x()), qAbs(p.y()));
125 }
126
127 // returns a new point with all components of p1 multiplied by the corresponding components of p2
128 inline QPointF operator*(const QPointF &p1, const QPointF &p2)
129 {
130     return QPointF(p1.x() * p2.x(), p1.y() * p2.y());
131 }
132
133 // returns a new point with all components of p1 divided by the corresponding components of p2
134 inline QPointF operator/(const QPointF &p1, const QPointF &p2)
135 {
136     return QPointF(p1.x() / p2.x(), p1.y() / p2.y());
137 }
138
139 inline QPointF clampToRect(const QPointF &p, const QRectF &rect)
140 {
141     qreal x = qBound(rect.left(), p.x(), rect.right());
142     qreal y = qBound(rect.top(), p.y(), rect.bottom());
143     return QPointF(x, y);
144 }
145
146 // returns -1, 0 or +1 according to r being <0, ==0 or >0
147 inline int qSign(qreal r)
148 {
149     return (r < 0) ? -1 : ((r > 0) ? 1 : 0);
150 }
151
152 // this version is not mathematically exact, but it just works for every
153 // easing curve type (even custom ones)
154
155 static qreal differentialForProgress(const QEasingCurve &curve, qreal pos)
156 {
157     const qreal dx = 0.01;
158     qreal left = (pos < qreal(0.5)) ? pos : pos - qreal(dx);
159     qreal right = (pos >= qreal(0.5)) ? pos : pos + qreal(dx);
160     qreal d = (curve.valueForProgress(right) - curve.valueForProgress(left)) / qreal(dx);
161
162     //qScrollerDebug() << "differentialForProgress(type: " << curve.type() << ", pos: " << pos << ") = " << d;
163
164     return d;
165 }
166
167 // this version is not mathematically exact, but it just works for every
168 // easing curve type (even custom ones)
169
170 static qreal progressForValue(const QEasingCurve &curve, qreal value)
171 {
172     if (curve.type() >= QEasingCurve::InElastic &&
173         curve.type() < QEasingCurve::Custom) {
174         qWarning("progressForValue(): QEasingCurves of type %d do not have an inverse, since they are not injective.", curve.type());
175         return value;
176     }
177     if (value < qreal(0) || value > qreal(1))
178         return value;
179
180     qreal progress = value, left(0), right(1);
181     for (int iterations = 6; iterations; --iterations) {
182         qreal v = curve.valueForProgress(progress);
183         if (v < value)
184             left = progress;
185         else if (v > value)
186             right = progress;
187         else
188             break;
189         progress = (left + right) / qreal(2);
190     }
191     return progress;
192 }
193
194
195 #ifndef QT_NO_ANIMATION
196 class QScrollTimer : public QAbstractAnimation
197 {
198 public:
199     QScrollTimer(QScrollerPrivate *_d)
200         : d(_d), ignoreUpdate(false), skip(0)
201     { }
202
203     int duration() const
204     {
205         return -1;
206     }
207
208     void start()
209     {
210         // QAbstractAnimation::start() will immediately call
211         // updateCurrentTime(), but our state is not set correctly yet
212         ignoreUpdate = true;
213         QAbstractAnimation::start();
214         ignoreUpdate = false;
215         skip = 0;
216     }
217
218 protected:
219     void updateCurrentTime(int /*currentTime*/)
220    {
221         if (!ignoreUpdate) {
222             if (++skip >= d->frameRateSkip()) {
223                 skip = 0;
224                 d->timerTick();
225             }
226         }
227     }
228
229 private:
230     QScrollerPrivate *d;
231     bool ignoreUpdate;
232     int skip;
233 };
234 #endif // QT_NO_ANIMATION
235
236 /*!
237     \class QScroller
238     \brief The QScroller class enables kinetic scrolling for any scrolling widget or graphics item.
239     \since 4.8
240
241     With kinetic scrolling, the user can push the widget in a given
242     direction and it will continue to scroll in this direction until it is
243     stopped either by the user or by friction.  Aspects of inertia, friction
244     and other physical concepts can be changed in order to fine-tune an
245     intuitive user experience.
246
247     The QScroller object is the object that stores the current position and
248     scrolling speed and takes care of updates.
249     QScroller can be triggered by a flick gesture
250
251     \code
252         QWidget *w = ...;
253         QScroller::grabGesture(w, QScroller::LeftMouseButtonGesture);
254     \endcode
255
256     or directly like this:
257
258     \code
259         QWidget *w = ...;
260         QScroller *scroller = QScroller::scroller(w);
261         scroller->scrollTo(QPointF(100, 100));
262     \endcode
263
264     The scrolled QObjects receive a QScrollPrepareEvent whenever the scroller needs to
265     update its geometry information and a QScrollEvent whenever the content of the object should
266     actually be scrolled.
267
268     The scroller uses the global QAbstractAnimation timer to generate its QScrollEvents. This
269     can be changed with QScrollerProperties::FrameRate on a per-QScroller basis.
270
271     Several examples in the \c scroller examples directory show how QScroller,
272     QScrollEvent and the scroller gesture can be used.
273
274     Even though this kinetic scroller has a large number of settings available via
275     QScrollerProperties, we recommend that you leave them all at their default, platform optimized
276     values. Before changing them you can experiment with the \c plot example in
277     the \c scroller examples directory.
278
279     \sa QScrollEvent, QScrollPrepareEvent, QScrollerProperties
280 */
281
282 typedef QMap<QObject *, QScroller *> ScrollerHash;
283 typedef QSet<QScroller *> ScrollerSet;
284
285 Q_GLOBAL_STATIC(ScrollerHash, qt_allScrollers)
286 Q_GLOBAL_STATIC(ScrollerSet, qt_activeScrollers)
287
288 /*!
289     Returns \c true if a QScroller object was already created for \a target; \c false otherwise.
290
291     \sa scroller()
292 */
293 bool QScroller::hasScroller(QObject *target)
294 {
295     return (qt_allScrollers()->value(target));
296 }
297
298 /*!
299     Returns the scroller for the given \a target.
300     As long as the object exists this function will always return the same QScroller instance.
301     If no QScroller exists for the \a target, one will implicitly be created.
302     At no point more than one QScroller will be active on an object.
303
304     \sa hasScroller(), target()
305 */
306 QScroller *QScroller::scroller(QObject *target)
307 {
308     if (!target) {
309         qWarning("QScroller::scroller() was called with a null target.");
310         return 0;
311     }
312
313     if (qt_allScrollers()->contains(target))
314         return qt_allScrollers()->value(target);
315
316     QScroller *s = new QScroller(target);
317     qt_allScrollers()->insert(target, s);
318     return s;
319 }
320
321 /*!
322     \overload
323     This is the const version of scroller().
324 */
325 const QScroller *QScroller::scroller(const QObject *target)
326 {
327     return scroller(const_cast<QObject*>(target));
328 }
329
330 /*!
331     Returns an application wide list of currently active QScroller objects.
332     Active QScroller objects are in a state() that is not QScroller::Inactive.
333     This function is useful when writing your own gesture recognizer.
334 */
335 QList<QScroller *> QScroller::activeScrollers()
336 {
337     return qt_activeScrollers()->toList();
338 }
339
340 /*!
341     Returns the target object of this scroller.
342     \sa hasScroller(), scroller()
343  */
344 QObject *QScroller::target() const
345 {
346     Q_D(const QScroller);
347     return d->target;
348 }
349
350 /*!
351     \fn QScroller::scrollerPropertiesChanged(const QScrollerProperties &newProperties);
352
353     QScroller emits this signal whenever its scroller properties change.
354     \a newProperties are the new scroller properties.
355
356     \sa scrollerProperties
357 */
358
359
360 /*! \property QScroller::scrollerProperties
361     \brief The scroller properties of this scroller.
362     The properties are used by the QScroller to determine its scrolling behavior.
363 */
364 QScrollerProperties QScroller::scrollerProperties() const
365 {
366     Q_D(const QScroller);
367     return d->properties;
368 }
369
370 void QScroller::setScrollerProperties(const QScrollerProperties &sp)
371 {
372     Q_D(QScroller);
373     if (d->properties != sp) {
374         d->properties = sp;
375         emit scrollerPropertiesChanged(sp);
376
377         // we need to force the recalculation here, since the overshootPolicy may have changed and
378         // existing segments may include an overshoot animation.
379         d->recalcScrollingSegments(true);
380     }
381 }
382
383 #ifndef QT_NO_GESTURES
384
385 /*!
386     Registers a custom scroll gesture recognizer, grabs it for the \a
387     target and returns the resulting gesture type.  If \a scrollGestureType is
388     set to TouchGesture the gesture triggers on touch events. If it is set to
389     one of LeftMouseButtonGesture, RightMouseButtonGesture or
390     MiddleMouseButtonGesture it triggers on mouse events of the
391     corresponding button.
392
393     Only one scroll gesture can be active on a single object at the same
394     time. If you call this function twice on the same object, it will
395     ungrab the existing gesture before grabbing the new one.
396
397     \note To avoid unwanted side-effects, mouse events are consumed while
398     the gesture is triggered. Since the initial mouse press event is
399     not consumed, the gesture sends a fake mouse release event
400     at the global position \c{(INT_MIN, INT_MIN)}. This ensures that
401     internal states of the widget that received the original mouse press
402     are consistent.
403
404     \sa ungrabGesture, grabbedGesture
405 */
406 Qt::GestureType QScroller::grabGesture(QObject *target, ScrollerGestureType scrollGestureType)
407 {
408     // ensure that a scroller for target is created
409     QScroller *s = scroller(target);
410     if (!s)
411         return Qt::GestureType(0);
412
413     QScrollerPrivate *sp = s->d_ptr;
414     if (sp->recognizer)
415         ungrabGesture(target); // ungrab the old gesture
416
417     Qt::MouseButton button;
418     switch (scrollGestureType) {
419     case LeftMouseButtonGesture  : button = Qt::LeftButton; break;
420     case RightMouseButtonGesture : button = Qt::RightButton; break;
421     case MiddleMouseButtonGesture: button = Qt::MiddleButton; break;
422     default                      :
423     case TouchGesture            : button = Qt::NoButton; break; // NoButton == Touch
424     }
425
426     sp->recognizer = new QFlickGestureRecognizer(button);
427     sp->recognizerType = QGestureRecognizer::registerRecognizer(sp->recognizer);
428
429     if (target->isWidgetType()) {
430         QWidget *widget = static_cast<QWidget *>(target);
431         widget->grabGesture(sp->recognizerType);
432         if (scrollGestureType == TouchGesture)
433             widget->setAttribute(Qt::WA_AcceptTouchEvents);
434 #ifndef QT_NO_GRAPHICSVIEW
435     } else if (QGraphicsObject *go = qobject_cast<QGraphicsObject*>(target)) {
436         if (scrollGestureType == TouchGesture)
437             go->setAcceptTouchEvents(true);
438         go->grabGesture(sp->recognizerType);
439 #endif // QT_NO_GRAPHICSVIEW
440     }
441     return sp->recognizerType;
442 }
443
444 /*!
445     Returns the gesture type currently grabbed for the \a target or 0 if no
446     gesture is grabbed.
447
448     \sa grabGesture, ungrabGesture
449 */
450 Qt::GestureType QScroller::grabbedGesture(QObject *target)
451 {
452     QScroller *s = scroller(target);
453     if (s && s->d_ptr)
454         return s->d_ptr->recognizerType;
455     else
456         return Qt::GestureType(0);
457 }
458
459 /*!
460     Ungrabs the gesture for the \a target.
461     Does nothing if no gesture is grabbed.
462
463     \sa grabGesture, grabbedGesture
464 */
465 void QScroller::ungrabGesture(QObject *target)
466 {
467     QScroller *s = scroller(target);
468     if (!s)
469         return;
470
471     QScrollerPrivate *sp = s->d_ptr;
472     if (!sp->recognizer)
473         return; // nothing to do
474
475     if (target->isWidgetType()) {
476         QWidget *widget = static_cast<QWidget *>(target);
477         widget->ungrabGesture(sp->recognizerType);
478 #ifndef QT_NO_GRAPHICSVIEW
479     } else if (QGraphicsObject *go = qobject_cast<QGraphicsObject*>(target)) {
480         go->ungrabGesture(sp->recognizerType);
481 #endif
482     }
483
484     QGestureRecognizer::unregisterRecognizer(sp->recognizerType);
485     // do not delete the recognizer. The QGestureManager is doing this.
486     sp->recognizer = 0;
487 }
488
489 #endif // QT_NO_GESTURES
490
491 /*!
492     \internal
493 */
494 QScroller::QScroller(QObject *target)
495     : d_ptr(new QScrollerPrivate(this, target))
496 {
497     Q_ASSERT(target); // you can't create a scroller without a target in any normal way
498     Q_D(QScroller);
499     d->init();
500 }
501
502 /*!
503     \internal
504 */
505 QScroller::~QScroller()
506 {
507     Q_D(QScroller);
508 #ifndef QT_NO_GESTURES
509     QGestureRecognizer::unregisterRecognizer(d->recognizerType);
510     // do not delete the recognizer. The QGestureManager is doing this.
511     d->recognizer = 0;
512 #endif
513     qt_allScrollers()->remove(d->target);
514     qt_activeScrollers()->remove(this);
515
516     delete d_ptr;
517 }
518
519
520 /*!
521     \fn QScroller::stateChanged(QScroller::State newState);
522
523     QScroller emits this signal whenever the state changes. \a newState is the new State.
524
525     \sa state
526 */
527
528 /*!
529     \property QScroller::state
530     \brief the state of the scroller
531
532     \sa QScroller::State
533 */
534 QScroller::State QScroller::state() const
535 {
536     Q_D(const QScroller);
537     return d->state;
538 }
539
540 /*!
541     Stops the scroller and resets its state back to Inactive.
542 */
543 void QScroller::stop()
544 {
545     Q_D(QScroller);
546     if (d->state != Inactive) {
547         QPointF here = clampToRect(d->contentPosition, d->contentPosRange);
548         qreal snapX = d->nextSnapPos(here.x(), 0, Qt::Horizontal);
549         qreal snapY = d->nextSnapPos(here.y(), 0, Qt::Vertical);
550         QPointF snap = here;
551         if (!qIsNaN(snapX))
552             snap.setX(snapX);
553         if (!qIsNaN(snapY))
554             snap.setY(snapY);
555         d->contentPosition = snap;
556         d->overshootPosition = QPointF(0, 0);
557
558         d->setState(Inactive);
559     }
560 }
561
562 /*!
563     Returns the pixel per meter metric for the scrolled widget.
564
565     The value is reported for both the x and y axis separately by using a QPointF.
566
567     \note Please note that this value should be physically correct. The actual DPI settings
568     that Qt returns for the display may be reported wrongly on purpose by the underlying
569     windowing system, for example on Mac OS X or Maemo 5.
570 */
571 QPointF QScroller::pixelPerMeter() const
572 {
573     Q_D(const QScroller);
574     QPointF ppm = d->pixelPerMeter;
575
576 #ifndef QT_NO_GRAPHICSVIEW
577     if (QGraphicsObject *go = qobject_cast<QGraphicsObject *>(d->target)) {
578         QTransform viewtr;
579         //TODO: the first view isn't really correct - maybe use an additional field in the prepare event?
580         if (go->scene() && !go->scene()->views().isEmpty())
581             viewtr = go->scene()->views().first()->viewportTransform();
582         QTransform tr = go->deviceTransform(viewtr);
583         if (tr.isScaling()) {
584             QPointF p0 = tr.map(QPointF(0, 0));
585             QPointF px = tr.map(QPointF(1, 0));
586             QPointF py = tr.map(QPointF(0, 1));
587             ppm.rx() /= QLineF(p0, px).length();
588             ppm.ry() /= QLineF(p0, py).length();
589         }
590     }
591 #endif // QT_NO_GRAPHICSVIEW
592     return ppm;
593 }
594
595 /*!
596     Returns the current scrolling velocity in meter per second when the state is Scrolling or Dragging.
597     Returns a zero velocity otherwise.
598
599     The velocity is reported for both the x and y axis separately by using a QPointF.
600
601     \sa pixelPerMeter()
602 */
603 QPointF QScroller::velocity() const
604 {
605     Q_D(const QScroller);
606     const QScrollerPropertiesPrivate *sp = d->properties.d.data();
607
608      switch (state()) {
609      case Dragging:
610         return d->releaseVelocity;
611      case Scrolling: {
612         QPointF vel;
613         qint64 now = d->monotonicTimer.elapsed();
614
615         if (!d->xSegments.isEmpty()) {
616             const QScrollerPrivate::ScrollSegment &s = d->xSegments.head();
617             qreal progress = qreal(now - s.startTime) / qreal(s.deltaTime);
618             qreal v = qSign(s.deltaPos) * qreal(s.deltaTime) / qreal(1000) * sp->decelerationFactor * qreal(0.5) * differentialForProgress(s.curve, progress);
619             vel.setX(v);
620         }
621
622         if (!d->ySegments.isEmpty()) {
623             const QScrollerPrivate::ScrollSegment &s = d->ySegments.head();
624             qreal progress = qreal(now - s.startTime) / qreal(s.deltaTime);
625             qreal v = qSign(s.deltaPos) * qreal(s.deltaTime) / qreal(1000) * sp->decelerationFactor * qreal(0.5) * differentialForProgress(s.curve, progress);
626             vel.setY(v);
627         }
628         return vel;
629      }
630      default:
631          return QPointF(0, 0);
632      }
633 }
634
635 /*!
636     Returns the estimated final position for the current scroll movement.
637     Returns the current position if the scroller state is not Scrolling.
638     The result is undefined when the scroller state is Inactive.
639
640     The target position is in pixel.
641
642     \sa pixelPerMeter(), scrollTo()
643 */
644 QPointF QScroller::finalPosition() const
645 {
646     Q_D(const QScroller);
647     return QPointF(d->scrollingSegmentsEndPos(Qt::Horizontal),
648                    d->scrollingSegmentsEndPos(Qt::Vertical));
649 }
650
651 /*!
652     Starts scrolling the widget so that point \a pos is at the top-left position in
653     the viewport.
654
655     The behaviour when scrolling outside the valid scroll area is undefined.
656     In this case the scroller might or might not overshoot.
657
658     The scrolling speed will be calculated so that the given position will
659     be reached after a platform-defined time span.
660
661     \a pos is given in viewport coordinates.
662
663     \sa ensureVisible()
664 */
665 void QScroller::scrollTo(const QPointF &pos)
666 {
667     // we could make this adjustable via QScrollerProperties
668     scrollTo(pos, 300);
669 }
670
671 /*! \overload
672
673     This version will reach its destination position in \a scrollTime milliseconds.
674 */
675 void QScroller::scrollTo(const QPointF &pos, int scrollTime)
676 {
677     Q_D(QScroller);
678
679     if (d->state == Pressed || d->state == Dragging )
680         return;
681
682     // no need to resend a prepare event if we are already scrolling
683     if (d->state == Inactive && !d->prepareScrolling(QPointF()))
684         return;
685
686     QPointF newpos = clampToRect(pos, d->contentPosRange);
687     qreal snapX = d->nextSnapPos(newpos.x(), 0, Qt::Horizontal);
688     qreal snapY = d->nextSnapPos(newpos.y(), 0, Qt::Vertical);
689     if (!qIsNaN(snapX))
690         newpos.setX(snapX);
691     if (!qIsNaN(snapY))
692         newpos.setY(snapY);
693
694     qScrollerDebug() << "QScroller::scrollTo(req:" << pos << " [pix] / snap:" << newpos << ", " << scrollTime << " [ms])";
695
696     if (newpos == d->contentPosition + d->overshootPosition)
697         return;
698
699     QPointF vel = velocity();
700
701     if (scrollTime < 0)
702         scrollTime = 0;
703     qreal time = qreal(scrollTime) / 1000;
704
705     d->createScrollToSegments(vel.x(), time, newpos.x(), Qt::Horizontal, QScrollerPrivate::ScrollTypeScrollTo);
706     d->createScrollToSegments(vel.y(), time, newpos.y(), Qt::Vertical, QScrollerPrivate::ScrollTypeScrollTo);
707
708     if (!scrollTime)
709         d->setContentPositionHelperScrolling();
710     d->setState(scrollTime ? Scrolling : Inactive);
711 }
712
713 /*!
714     Starts scrolling so that the rectangle \a rect is visible inside the
715     viewport with additional margins specified in pixels by \a xmargin and \a ymargin around
716     the rect.
717
718     In cases where it is not possible to fit the rect plus margins inside the viewport the contents
719     are scrolled so that as much as possible is visible from \a rect.
720
721     The scrolling speed is calculated so that the given position is reached after a platform-defined
722     time span.
723
724     This function performs the actual scrolling by calling scrollTo().
725
726     \sa scrollTo
727 */
728 void QScroller::ensureVisible(const QRectF &rect, qreal xmargin, qreal ymargin)
729 {
730     // we could make this adjustable via QScrollerProperties
731     ensureVisible(rect, xmargin, ymargin, 1000);
732 }
733
734 /*! \overload
735
736     This version will reach its destination position in \a scrollTime milliseconds.
737 */
738 void QScroller::ensureVisible(const QRectF &rect, qreal xmargin, qreal ymargin, int scrollTime)
739 {
740     Q_D(QScroller);
741
742     if (d->state == Pressed || d->state == Dragging )
743         return;
744
745     if (d->state == Inactive && !d->prepareScrolling(QPointF()))
746         return;
747
748     // -- calculate the current pos (or the position after the current scroll)
749     QPointF startPos = d->contentPosition + d->overshootPosition;
750     startPos = QPointF(d->scrollingSegmentsEndPos(Qt::Horizontal),
751                        d->scrollingSegmentsEndPos(Qt::Vertical));
752
753     QRectF marginRect(rect.x() - xmargin, rect.y() - ymargin,
754                       rect.width() + 2 * xmargin, rect.height() + 2 * ymargin);
755
756     QSizeF visible = d->viewportSize;
757     QRectF visibleRect(startPos, visible);
758
759     qScrollerDebug() << "QScroller::ensureVisible(" << rect << " [pix], " << xmargin << " [pix], " << ymargin << " [pix], " << scrollTime << "[ms])";
760     qScrollerDebug() << "  --> content position:" << d->contentPosition;
761
762     if (visibleRect.contains(marginRect))
763         return;
764
765     QPointF newPos = startPos;
766
767     if (visibleRect.width() < rect.width()) {
768         // at least try to move the rect into view
769         if (rect.left() > visibleRect.left())
770             newPos.setX(rect.left());
771         else if (rect.right() < visibleRect.right())
772             newPos.setX(rect.right() - visible.width());
773
774     } else if (visibleRect.width() < marginRect.width()) {
775         newPos.setX(rect.center().x() - visibleRect.width() / 2);
776     } else if (marginRect.left() > visibleRect.left()) {
777         newPos.setX(marginRect.left());
778     } else if (marginRect.right() < visibleRect.right()) {
779         newPos.setX(marginRect.right() - visible.width());
780     }
781
782     if (visibleRect.height() < rect.height()) {
783         // at least try to move the rect into view
784         if (rect.top() > visibleRect.top())
785             newPos.setX(rect.top());
786         else if (rect.bottom() < visibleRect.bottom())
787             newPos.setX(rect.bottom() - visible.height());
788
789     } else if (visibleRect.height() < marginRect.height()) {
790         newPos.setY(rect.center().y() - visibleRect.height() / 2);
791     } else if (marginRect.top() > visibleRect.top()) {
792         newPos.setY(marginRect.top());
793     } else if (marginRect.bottom() < visibleRect.bottom()) {
794         newPos.setY(marginRect.bottom() - visible.height());
795     }
796
797     // clamp to maximum content position
798     newPos = clampToRect(newPos, d->contentPosRange);
799     if (newPos == startPos)
800         return;
801
802     scrollTo(newPos, scrollTime);
803 }
804
805 /*! This function resends the QScrollPrepareEvent.
806     Calling resendPrepareEvent triggers a QScrollPrepareEvent from the scroller.
807     This allows the receiver to re-set content position and content size while
808     scrolling.
809     Calling this function while in the Inactive state is useless as the prepare event
810     is sent again before scrolling starts.
811  */
812 void QScroller::resendPrepareEvent()
813 {
814     Q_D(QScroller);
815     d->prepareScrolling(d->pressPosition);
816 }
817
818 /*! Set the snap positions for the horizontal axis to a list of \a positions.
819     This overwrites all previously set snap positions and also a previously
820     set snapping interval.
821     Snapping can be deactivated by setting an empty list of positions.
822  */
823 void QScroller::setSnapPositionsX(const QList<qreal> &positions)
824 {
825     Q_D(QScroller);
826     d->snapPositionsX = positions;
827     d->snapIntervalX = 0.0;
828
829     d->recalcScrollingSegments();
830 }
831
832 /*! Set the snap positions for the horizontal axis to regular spaced intervals.
833     The first snap position is at \a first. The next at \a first + \a interval.
834     This can be used to implement a list header.
835     This overwrites all previously set snap positions and also a previously
836     set snapping interval.
837     Snapping can be deactivated by setting an interval of 0.0
838  */
839 void QScroller::setSnapPositionsX(qreal first, qreal interval)
840 {
841     Q_D(QScroller);
842     d->snapFirstX = first;
843     d->snapIntervalX = interval;
844     d->snapPositionsX.clear();
845
846     d->recalcScrollingSegments();
847 }
848
849 /*! Set the snap positions for the vertical axis to a list of \a positions.
850     This overwrites all previously set snap positions and also a previously
851     set snapping interval.
852     Snapping can be deactivated by setting an empty list of positions.
853  */
854 void QScroller::setSnapPositionsY(const QList<qreal> &positions)
855 {
856     Q_D(QScroller);
857     d->snapPositionsY = positions;
858     d->snapIntervalY = 0.0;
859
860     d->recalcScrollingSegments();
861 }
862
863 /*! Set the snap positions for the vertical axis to regular spaced intervals.
864     The first snap position is at \a first. The next at \a first + \a interval.
865     This overwrites all previously set snap positions and also a previously
866     set snapping interval.
867     Snapping can be deactivated by setting an interval of 0.0
868  */
869 void QScroller::setSnapPositionsY(qreal first, qreal interval)
870 {
871     Q_D(QScroller);
872     d->snapFirstY = first;
873     d->snapIntervalY = interval;
874     d->snapPositionsY.clear();
875
876     d->recalcScrollingSegments();
877 }
878
879
880
881 // -------------- private ------------
882
883 QScrollerPrivate::QScrollerPrivate(QScroller *q, QObject *_target)
884     : target(_target)
885 #ifndef QT_NO_GESTURES
886     , recognizer(0)
887     , recognizerType(Qt::CustomGesture)
888 #endif
889     , state(QScroller::Inactive)
890     , firstScroll(true)
891     , pressTimestamp(0)
892     , lastTimestamp(0)
893     , snapFirstX(-1.0)
894     , snapIntervalX(0.0)
895     , snapFirstY(-1.0)
896     , snapIntervalY(0.0)
897 #ifndef QT_NO_ANIMATION
898     , scrollTimer(new QScrollTimer(this))
899 #endif
900     , q_ptr(q)
901 {
902     connect(target, SIGNAL(destroyed(QObject*)), this, SLOT(targetDestroyed()));
903 }
904
905 void QScrollerPrivate::init()
906 {
907     setDpiFromWidget(0);
908     monotonicTimer.start();
909 }
910
911 void QScrollerPrivate::sendEvent(QObject *o, QEvent *e)
912 {
913     qt_sendSpontaneousEvent(o, e);
914 }
915
916 const char *QScrollerPrivate::stateName(QScroller::State state)
917 {
918     switch (state) {
919     case QScroller::Inactive:  return "inactive";
920     case QScroller::Pressed:   return "pressed";
921     case QScroller::Dragging:  return "dragging";
922     case QScroller::Scrolling: return "scrolling";
923     default:                   return "(invalid)";
924     }
925 }
926
927 const char *QScrollerPrivate::inputName(QScroller::Input input)
928 {
929     switch (input) {
930     case QScroller::InputPress:   return "press";
931     case QScroller::InputMove:    return "move";
932     case QScroller::InputRelease: return "release";
933     default:                      return "(invalid)";
934     }
935 }
936
937 void QScrollerPrivate::targetDestroyed()
938 {
939 #ifndef QT_NO_ANIMATION
940     scrollTimer->stop();
941 #endif
942     delete q_ptr;
943 }
944
945 void QScrollerPrivate::timerTick()
946 {
947     struct timerevent {
948         QScroller::State state;
949         typedef void (QScrollerPrivate::*timerhandler_t)();
950         timerhandler_t handler;
951     };
952
953     timerevent timerevents[] = {
954         { QScroller::Dragging, &QScrollerPrivate::timerEventWhileDragging },
955         { QScroller::Scrolling, &QScrollerPrivate::timerEventWhileScrolling },
956     };
957
958     for (int i = 0; i < int(sizeof(timerevents) / sizeof(*timerevents)); ++i) {
959         timerevent *te = timerevents + i;
960
961         if (state == te->state) {
962             (this->*te->handler)();
963             return;
964         }
965     }
966
967 #ifndef QT_NO_ANIMATION
968     scrollTimer->stop();
969 #endif
970 }
971
972 /*!
973     This function is used by gesture recognizers to inform the scroller about a new input event.
974     The scroller changes its internal state() according to the input event and its attached
975     scroller properties. The scroller doesn't distinguish between the kind of input device the
976     event came from. Therefore the event needs to be split into the \a input type, a \a position and a
977     milli-second \a timestamp.  The \a position needs to be in the target's coordinate system.
978
979     The return value is \c true if the event should be consumed by the calling filter or \c false
980     if the event should be forwarded to the control.
981
982     \note Using grabGesture() should be sufficient for most use cases.
983 */
984 bool QScroller::handleInput(Input input, const QPointF &position, qint64 timestamp)
985 {
986     Q_D(QScroller);
987
988     qScrollerDebug() << "QScroller::handleInput(" << input << ", " << d->stateName(d->state) << ", " << position << ", " << timestamp << ")";
989     struct statechange {
990         State state;
991         Input input;
992         typedef bool (QScrollerPrivate::*inputhandler_t)(const QPointF &position, qint64 timestamp);
993         inputhandler_t handler;
994     };
995
996     statechange statechanges[] = {
997         { QScroller::Inactive,  InputPress,   &QScrollerPrivate::pressWhileInactive },
998         { QScroller::Pressed,   InputMove,    &QScrollerPrivate::moveWhilePressed },
999         { QScroller::Pressed,   InputRelease, &QScrollerPrivate::releaseWhilePressed },
1000         { QScroller::Dragging,  InputMove,    &QScrollerPrivate::moveWhileDragging },
1001         { QScroller::Dragging,  InputRelease, &QScrollerPrivate::releaseWhileDragging },
1002         { QScroller::Scrolling, InputPress,   &QScrollerPrivate::pressWhileScrolling }
1003     };
1004
1005     for (int i = 0; i < int(sizeof(statechanges) / sizeof(*statechanges)); ++i) {
1006         statechange *sc = statechanges + i;
1007
1008          if (d->state == sc->state && input == sc->input)
1009              return (d->*sc->handler)(position - d->overshootPosition, timestamp);
1010     }
1011     return false;
1012 }
1013
1014 #if !defined(Q_WS_MAC)
1015 // the Mac version is implemented in qscroller_mac.mm
1016
1017 QPointF QScrollerPrivate::realDpi(int screen)
1018 {
1019 #  ifdef Q_WS_MAEMO_5
1020     Q_UNUSED(screen);
1021
1022     // The DPI value is hardcoded to 96 on Maemo5:
1023     // https://projects.maemo.org/bugzilla/show_bug.cgi?id=152525
1024     // This value (260) is only correct for the N900 though, but
1025     // there's no way to get the real DPI at run time.
1026     return QPointF(260, 260);
1027
1028 #  elif defined(Q_WS_X11) && !defined(QT_NO_XRANDR)
1029     if (X11 && X11->use_xrandr && X11->ptrXRRSizes && X11->ptrXRRRootToScreen) {
1030         int nsizes = 0;
1031         // QDesktopWidget is based on Xinerama screens, which do not always
1032         // correspond to RandR screens: NVidia's TwinView e.g.  will show up
1033         // as 2 screens in QDesktopWidget, but libXRandR will only see 1 screen.
1034         // (although with the combined size of the Xinerama screens).
1035         // Additionally, libXrandr will simply crash when calling XRRSizes
1036         // for (the non-existent) screen 1 in this scenario.
1037         Window root =  RootWindow(X11->display, screen == -1 ? X11->defaultScreen : screen);
1038         int randrscreen = (root != XNone) ? X11->ptrXRRRootToScreen(X11->display, root) : -1;
1039
1040         XRRScreenSize *sizes = X11->ptrXRRSizes(X11->display, randrscreen == -1 ? 0 : randrscreen, &nsizes);
1041         if (nsizes > 0 && sizes && sizes->width && sizes->height && sizes->mwidth && sizes->mheight) {
1042             qScrollerDebug() << "XRandR DPI:" << QPointF(qreal(25.4) * qreal(sizes->width) / qreal(sizes->mwidth),
1043                                                          qreal(25.4) * qreal(sizes->height) / qreal(sizes->mheight));
1044             return QPointF(qreal(25.4) * qreal(sizes->width) / qreal(sizes->mwidth),
1045                            qreal(25.4) * qreal(sizes->height) / qreal(sizes->mheight));
1046         }
1047     }
1048 #  endif
1049
1050     QWidget *w = QApplication::desktop()->screen(screen);
1051     return QPointF(w->physicalDpiX(), w->physicalDpiY());
1052 }
1053
1054 #endif // !Q_WS_MAC
1055
1056
1057 /*! \internal
1058     Returns the resolution of the used screen.
1059 */
1060 QPointF QScrollerPrivate::dpi() const
1061 {
1062     return pixelPerMeter * qreal(0.0254);
1063 }
1064
1065 /*! \internal
1066     Sets the resolution used for scrolling.
1067     This resolution is only used by the kinetic scroller. If you change this
1068     then the scroller will behave quite different as a lot of the values are
1069     given in physical distances (millimeter).
1070 */
1071 void QScrollerPrivate::setDpi(const QPointF &dpi)
1072 {
1073     pixelPerMeter = dpi / qreal(0.0254);
1074 }
1075
1076 /*! \internal
1077     Sets the dpi used for scrolling to the value of the widget.
1078 */
1079 void QScrollerPrivate::setDpiFromWidget(QWidget *widget)
1080 {
1081     QDesktopWidget *dw = QApplication::desktop();
1082     setDpi(realDpi(widget ? dw->screenNumber(widget) : dw->primaryScreen()));
1083 }
1084
1085 /*! \internal
1086     Updates the velocity during dragging.
1087     Sets releaseVelocity.
1088 */
1089 void QScrollerPrivate::updateVelocity(const QPointF &deltaPixelRaw, qint64 deltaTime)
1090 {
1091     if (deltaTime <= 0)
1092         return;
1093
1094     Q_Q(QScroller);
1095     QPointF ppm = q->pixelPerMeter();
1096     const QScrollerPropertiesPrivate *sp = properties.d.data();
1097     QPointF deltaPixel = deltaPixelRaw;
1098
1099     qScrollerDebug() << "QScroller::updateVelocity(" << deltaPixelRaw << " [delta pix], " << deltaTime << " [delta ms])";
1100
1101     // faster than 2.5mm/ms seems bogus (that would be a screen height in ~20 ms)
1102     if (((deltaPixelRaw / qreal(deltaTime)).manhattanLength() / ((ppm.x() + ppm.y()) / 2) * 1000) > qreal(2.5))
1103         deltaPixel = deltaPixelRaw * qreal(2.5) * ppm / 1000 / (deltaPixelRaw / qreal(deltaTime)).manhattanLength();
1104
1105     QPointF newv = -deltaPixel / qreal(deltaTime) * qreal(1000) / ppm;
1106     // around 95% of all updates are in the [1..50] ms range, so make sure
1107     // to scale the smoothing factor over that range: this way a 50ms update
1108     // will have full impact, while 5ms update will only have a 10% impact.
1109     qreal smoothing = sp->dragVelocitySmoothingFactor * qMin(qreal(deltaTime), qreal(50)) / qreal(50);
1110
1111     // only smooth if we already have a release velocity and only if the
1112     // user hasn't stopped to move his finger for more than 100ms
1113     if ((releaseVelocity != QPointF(0, 0)) && (deltaTime < 100)) {
1114         qScrollerDebug() << "SMOOTHED from " << newv << " to " << newv * smoothing + releaseVelocity * (qreal(1) - smoothing);
1115         // smooth x or y only if the new velocity is either 0 or at least in
1116         // the same direction of the release velocity
1117         if (!newv.x() || (qSign(releaseVelocity.x()) == qSign(newv.x())))
1118             newv.setX(newv.x() * smoothing + releaseVelocity.x() * (qreal(1) - smoothing));
1119         if (!newv.y() || (qSign(releaseVelocity.y()) == qSign(newv.y())))
1120             newv.setY(newv.y() * smoothing + releaseVelocity.y() * (qreal(1) - smoothing));
1121     } else
1122         qScrollerDebug() << "NO SMOOTHING to " << newv;
1123
1124     releaseVelocity.setX(qBound(-sp->maximumVelocity, newv.x(), sp->maximumVelocity));
1125     releaseVelocity.setY(qBound(-sp->maximumVelocity, newv.y(), sp->maximumVelocity));
1126
1127     qScrollerDebug() << "  --> new velocity:" << releaseVelocity;
1128 }
1129
1130 void QScrollerPrivate::pushSegment(ScrollType type, qreal deltaTime, qreal stopProgress, qreal startPos, qreal deltaPos, qreal stopPos, QEasingCurve::Type curve, Qt::Orientation orientation)
1131 {
1132     if (startPos == stopPos || deltaPos == 0)
1133         return;
1134
1135     ScrollSegment s;
1136     if (orientation == Qt::Horizontal && !xSegments.isEmpty())
1137         s.startTime = xSegments.last().startTime + xSegments.last().deltaTime * xSegments.last().stopProgress;
1138     else if (orientation == Qt::Vertical && !ySegments.isEmpty())
1139         s.startTime = ySegments.last().startTime + ySegments.last().deltaTime * ySegments.last().stopProgress;
1140     else
1141         s.startTime = monotonicTimer.elapsed();
1142
1143     s.startPos = startPos;
1144     s.deltaPos = deltaPos;
1145     s.stopPos = stopPos;
1146     s.deltaTime = deltaTime * 1000;
1147     s.stopProgress = stopProgress;
1148     s.curve.setType(curve);
1149     s.type = type;
1150
1151     if (orientation == Qt::Horizontal)
1152         xSegments.enqueue(s);
1153     else
1154         ySegments.enqueue(s);
1155
1156     qScrollerDebug() << "+++ Added a new ScrollSegment: " << s;
1157 }
1158
1159
1160 /*! \internal
1161     Clears the old segments and recalculates them if the current segments are not longer valid
1162 */
1163 void QScrollerPrivate::recalcScrollingSegments(bool forceRecalc)
1164 {
1165     Q_Q(QScroller);
1166     QPointF ppm = q->pixelPerMeter();
1167
1168     releaseVelocity = q->velocity();
1169
1170     if (forceRecalc || !scrollingSegmentsValid(Qt::Horizontal))
1171         createScrollingSegments(releaseVelocity.x(), contentPosition.x() + overshootPosition.x(), ppm.x(), Qt::Horizontal);
1172
1173     if (forceRecalc || !scrollingSegmentsValid(Qt::Vertical))
1174         createScrollingSegments(releaseVelocity.y(), contentPosition.y() + overshootPosition.y(), ppm.y(), Qt::Vertical);
1175 }
1176
1177 /*! \internal
1178     Returns the end position after the current scroll has finished.
1179 */
1180 qreal QScrollerPrivate::scrollingSegmentsEndPos(Qt::Orientation orientation) const
1181 {
1182     if (orientation == Qt::Horizontal) {
1183         if (xSegments.isEmpty())
1184             return contentPosition.x() + overshootPosition.x();
1185         else
1186             return xSegments.last().stopPos;
1187     } else {
1188         if (ySegments.isEmpty())
1189             return contentPosition.y() + overshootPosition.y();
1190         else
1191             return ySegments.last().stopPos;
1192     }
1193 }
1194
1195 /*! \internal
1196     Checks if the scroller segment end in a valid position.
1197 */
1198 bool QScrollerPrivate::scrollingSegmentsValid(Qt::Orientation orientation)
1199 {
1200     QQueue<ScrollSegment> *segments;
1201     qreal minPos;
1202     qreal maxPos;
1203
1204     if (orientation == Qt::Horizontal) {
1205         segments = &xSegments;
1206         minPos = contentPosRange.left();
1207         maxPos = contentPosRange.right();
1208     } else {
1209         segments = &ySegments;
1210         minPos = contentPosRange.top();
1211         maxPos = contentPosRange.bottom();
1212     }
1213
1214     if (segments->isEmpty())
1215         return true;
1216
1217     const ScrollSegment &last = segments->last();
1218     qreal stopPos = last.stopPos;
1219
1220     if (last.type == ScrollTypeScrollTo)
1221         return true; // scrollTo is always valid
1222
1223     if (last.type == ScrollTypeOvershoot &&
1224         (stopPos != minPos && stopPos != maxPos))
1225         return false;
1226
1227     if (stopPos < minPos || stopPos > maxPos)
1228         return false;
1229
1230     if (stopPos == minPos || stopPos == maxPos) // the begin and the end of the list are always ok
1231         return true;
1232
1233     qreal nextSnap = nextSnapPos(stopPos, 0, orientation);
1234     if (!qIsNaN(nextSnap) && stopPos != nextSnap)
1235         return false;
1236
1237     return true;
1238 }
1239
1240 /*! \internal
1241    Creates the sections needed to scroll to the specific \a endPos to the segments queue.
1242 */
1243 void QScrollerPrivate::createScrollToSegments(qreal v, qreal deltaTime, qreal endPos, Qt::Orientation orientation, ScrollType type)
1244 {
1245     Q_UNUSED(v);
1246
1247     if (orientation == Qt::Horizontal)
1248         xSegments.clear();
1249     else
1250         ySegments.clear();
1251
1252     qScrollerDebug() << "+++ createScrollToSegments: t:" << deltaTime << "ep:" << endPos << "o:" << int(orientation);
1253
1254     const QScrollerPropertiesPrivate *sp = properties.d.data();
1255
1256     qreal startPos = (orientation == Qt::Horizontal) ? contentPosition.x() + overshootPosition.x()
1257                                                      : contentPosition.y() + overshootPosition.y();
1258     qreal deltaPos = (endPos - startPos) / 2;
1259
1260     pushSegment(type, deltaTime * qreal(0.3), qreal(1.0), startPos, deltaPos, startPos + deltaPos, QEasingCurve::InQuad, orientation);
1261     pushSegment(type, deltaTime * qreal(0.7), qreal(1.0), startPos + deltaPos, deltaPos, endPos, sp->scrollingCurve.type(), orientation);
1262 }
1263
1264 /*! \internal
1265 */
1266 void QScrollerPrivate::createScrollingSegments(qreal v, qreal startPos, qreal ppm, Qt::Orientation orientation)
1267 {
1268     const QScrollerPropertiesPrivate *sp = properties.d.data();
1269
1270     QScrollerProperties::OvershootPolicy policy;
1271     qreal minPos;
1272     qreal maxPos;
1273     qreal viewSize;
1274
1275     if (orientation == Qt::Horizontal) {
1276         xSegments.clear();
1277         policy = sp->hOvershootPolicy;
1278         minPos = contentPosRange.left();
1279         maxPos = contentPosRange.right();
1280         viewSize = viewportSize.width();
1281     } else {
1282         ySegments.clear();
1283         policy = sp->vOvershootPolicy;
1284         minPos = contentPosRange.top();
1285         maxPos = contentPosRange.bottom();
1286         viewSize = viewportSize.height();
1287     }
1288
1289     bool alwaysOvershoot = (policy == QScrollerProperties::OvershootAlwaysOn);
1290     bool noOvershoot = (policy == QScrollerProperties::OvershootAlwaysOff) || !sp->overshootScrollDistanceFactor;
1291     bool canOvershoot = !noOvershoot && (alwaysOvershoot || maxPos);
1292
1293     qScrollerDebug() << "+++ createScrollingSegments: s:" << startPos << "maxPos:" << maxPos << "o:" << int(orientation);
1294
1295     qScrollerDebug() << "v = " << v << ", decelerationFactor = " << sp->decelerationFactor << ", curveType = " << sp->scrollingCurve.type();
1296
1297     // This is only correct for QEasingCurve::OutQuad (linear velocity,
1298     // constant deceleration), but the results look and feel ok for OutExpo
1299     // and OutSine as well
1300
1301     // v(t) = deltaTime * a * 0.5 * differentialForProgress(t / deltaTime)
1302     // v(0) = vrelease
1303     // v(deltaTime) = 0
1304     // deltaTime = (2 * vrelease) / (a * differntial(0))
1305
1306     // pos(t) = integrate(v(t)dt)
1307     // pos(t) = vrelease * t - 0.5 * a * t * t
1308     // pos(t) = deltaTime * a * 0.5 * progress(t / deltaTime) * deltaTime
1309     // deltaPos = pos(deltaTime)
1310
1311     qreal deltaTime = (qreal(2) * qAbs(v)) / (sp->decelerationFactor * differentialForProgress(sp->scrollingCurve, 0));
1312     qreal deltaPos = qSign(v) * deltaTime * deltaTime * qreal(0.5) * sp->decelerationFactor * ppm;
1313     qreal endPos = startPos + deltaPos;
1314
1315     qScrollerDebug() << "  Real Delta:" << deltaPos;
1316
1317     // -- determine snap points
1318     qreal nextSnap = nextSnapPos(endPos, 0, orientation);
1319     qreal lowerSnapPos = nextSnapPos(startPos, -1, orientation);
1320     qreal higherSnapPos = nextSnapPos(startPos, 1, orientation);
1321
1322     qScrollerDebug() << "  Real Delta:" << lowerSnapPos <<"-"<<nextSnap <<"-"<<higherSnapPos;
1323
1324     // - check if we can reach another snap point
1325     if (nextSnap > higherSnapPos || qIsNaN(higherSnapPos))
1326         higherSnapPos = nextSnap;
1327     if (nextSnap < lowerSnapPos || qIsNaN(lowerSnapPos))
1328         lowerSnapPos = nextSnap;
1329
1330     // -- check if are in overshoot and end in overshoot
1331     if ((startPos < minPos && endPos < minPos) ||
1332         (startPos > maxPos && endPos > maxPos)) {
1333         qreal stopPos = endPos < minPos ? minPos : maxPos;
1334         qreal oDeltaTime = sp->overshootScrollTime;
1335
1336         pushSegment(ScrollTypeOvershoot, oDeltaTime * qreal(0.7), qreal(1.0), startPos, stopPos - startPos, stopPos, sp->scrollingCurve.type(), orientation);
1337         return;
1338     }
1339
1340     if (qAbs(v) < sp->minimumVelocity) {
1341
1342         qScrollerDebug() << "### below minimum Vel" << orientation;
1343
1344         // - no snap points or already at one
1345         if (qIsNaN(nextSnap) || nextSnap == startPos)
1346             return; // nothing to do, no scrolling needed.
1347
1348         // - decide which point to use
1349
1350         qreal snapDistance = higherSnapPos - lowerSnapPos;
1351
1352         qreal pressDistance = (orientation == Qt::Horizontal) ?
1353             lastPosition.x() - pressPosition.x() :
1354             lastPosition.y() - pressPosition.y();
1355
1356         // if not dragged far enough, pick the next snap point.
1357         if (sp->snapPositionRatio == 0.0 || qAbs(pressDistance / sp->snapPositionRatio) > snapDistance)
1358             endPos = nextSnap;
1359         else if (pressDistance < 0.0)
1360             endPos = lowerSnapPos;
1361         else
1362             endPos = higherSnapPos;
1363
1364         deltaPos = endPos - startPos;
1365         qreal midPos = startPos + deltaPos * qreal(0.3);
1366         pushSegment(ScrollTypeFlick, sp->snapTime * qreal(0.3), qreal(1.0), startPos, midPos - startPos, midPos, QEasingCurve::InQuad, orientation);
1367         pushSegment(ScrollTypeFlick, sp->snapTime * qreal(0.7), qreal(1.0), midPos, endPos - midPos, endPos, sp->scrollingCurve.type(), orientation);
1368         return;
1369     }
1370
1371     // - go to the next snappoint if there is one
1372     if (v > 0 && !qIsNaN(higherSnapPos)) {
1373         // change the time in relation to the changed end position
1374         if (endPos - startPos)
1375             deltaTime *= qAbs((higherSnapPos - startPos) / (endPos - startPos));
1376         if (deltaTime > sp->snapTime)
1377             deltaTime = sp->snapTime;
1378         endPos = higherSnapPos;
1379
1380     } else if (v < 0 && !qIsNaN(lowerSnapPos)) {
1381         // change the time in relation to the changed end position
1382         if (endPos - startPos)
1383             deltaTime *= qAbs((lowerSnapPos - startPos) / (endPos - startPos));
1384         if (deltaTime > sp->snapTime)
1385             deltaTime = sp->snapTime;
1386         endPos = lowerSnapPos;
1387
1388     // -- check if we are overshooting
1389     } else if (endPos < minPos || endPos > maxPos) {
1390         qreal stopPos = endPos < minPos ? minPos : maxPos;
1391
1392         qScrollerDebug() << "Overshoot: delta:" << (stopPos - startPos);
1393
1394         qreal stopProgress = progressForValue(sp->scrollingCurve, qAbs((stopPos - startPos) / deltaPos));
1395
1396         if (!canOvershoot) {
1397             qScrollerDebug() << "Overshoot stopp:" << stopProgress;
1398
1399             pushSegment(ScrollTypeFlick, deltaTime, stopProgress, startPos, endPos, stopPos, sp->scrollingCurve.type(), orientation);
1400         } else {
1401             qreal oDeltaTime = sp->overshootScrollTime;
1402             qreal oStopProgress = qMin(stopProgress + oDeltaTime * qreal(0.3) / deltaTime, qreal(1));
1403             qreal oDistance = startPos + deltaPos * sp->scrollingCurve.valueForProgress(oStopProgress) - stopPos;
1404             qreal oMaxDistance = qSign(oDistance) * (viewSize * sp->overshootScrollDistanceFactor);
1405
1406             qScrollerDebug() << "1 oDistance:" << oDistance << "Max:" << oMaxDistance << "stopP/oStopP" << stopProgress << oStopProgress;
1407
1408             if (qAbs(oDistance) > qAbs(oMaxDistance)) {
1409                 oStopProgress = progressForValue(sp->scrollingCurve, qAbs((stopPos + oMaxDistance - startPos) / deltaPos));
1410                 oDistance = oMaxDistance;
1411                 qScrollerDebug() << "2 oDistance:" << oDistance << "Max:" << oMaxDistance << "stopP/oStopP" << stopProgress << oStopProgress;
1412             }
1413
1414             pushSegment(ScrollTypeFlick, deltaTime, oStopProgress, startPos, deltaPos, stopPos + oDistance, sp->scrollingCurve.type(), orientation);
1415             pushSegment(ScrollTypeOvershoot, oDeltaTime * qreal(0.7), qreal(1.0), stopPos + oDistance, -oDistance, stopPos, sp->scrollingCurve.type(), orientation);
1416         }
1417         return;
1418     }
1419
1420     pushSegment(ScrollTypeFlick, deltaTime, qreal(1.0), startPos, deltaPos, endPos, sp->scrollingCurve.type(), orientation);
1421 }
1422
1423
1424 /*! \internal
1425     Prepares scrolling by sending a QScrollPrepareEvent to the receiver widget.
1426     Returns true if the scrolling was accepted and a target was returned.
1427 */
1428 bool QScrollerPrivate::prepareScrolling(const QPointF &position)
1429 {
1430     QScrollPrepareEvent spe(position);
1431     spe.ignore();
1432     sendEvent(target, &spe);
1433
1434     qScrollerDebug() << "QScrollPrepareEvent returned from" << target << "with" << spe.isAccepted() << "mcp:" << spe.contentPosRange() << "cp:" << spe.contentPos();
1435     if (spe.isAccepted()) {
1436         QPointF oldContentPos = contentPosition + overshootPosition;
1437         QPointF contentDelta = spe.contentPos() - oldContentPos;
1438
1439         viewportSize = spe.viewportSize();
1440         contentPosRange = spe.contentPosRange();
1441         if (contentPosRange.width() < 0)
1442             contentPosRange.setWidth(0);
1443         if (contentPosRange.height() < 0)
1444             contentPosRange.setHeight(0);
1445         contentPosition = clampToRect(spe.contentPos(), contentPosRange);
1446         overshootPosition = spe.contentPos() - contentPosition;
1447
1448         // - check if the content position was moved
1449         if (contentDelta != QPointF(0, 0)) {
1450             // need to correct all segments
1451             for (int i = 0; i < xSegments.count(); i++)
1452                 xSegments[i].startPos -= contentDelta.x();
1453
1454             for (int i = 0; i < ySegments.count(); i++)
1455                 ySegments[i].startPos -= contentDelta.y();
1456         }
1457
1458         if (QWidget *w = qobject_cast<QWidget *>(target))
1459             setDpiFromWidget(w);
1460 #ifndef QT_NO_GRAPHICSVIEW
1461         if (QGraphicsObject *go = qobject_cast<QGraphicsObject *>(target)) {
1462             //TODO: the first view isn't really correct - maybe use an additional field in the prepare event?
1463             if (go->scene() && !go->scene()->views().isEmpty())
1464                 setDpiFromWidget(go->scene()->views().first());
1465         }
1466 #endif
1467
1468         if (state == QScroller::Scrolling) {
1469             recalcScrollingSegments();
1470         }
1471         return true;
1472     }
1473
1474     return false;
1475 }
1476
1477 void QScrollerPrivate::handleDrag(const QPointF &position, qint64 timestamp)
1478 {
1479     const QScrollerPropertiesPrivate *sp = properties.d.data();
1480
1481     QPointF deltaPixel = position - lastPosition;
1482     qint64 deltaTime = timestamp - lastTimestamp;
1483
1484     if (sp->axisLockThreshold) {
1485         int dx = qAbs(deltaPixel.x());
1486         int dy = qAbs(deltaPixel.y());
1487         if (dx || dy) {
1488             bool vertical = (dy > dx);
1489             qreal alpha = qreal(vertical ? dx : dy) / qreal(vertical ? dy : dx);
1490             //qScrollerDebug() << "QScroller::handleDrag() -- axis lock:" << alpha << " / " << axisLockThreshold << "- isvertical:" << vertical << "- dx:" << dx << "- dy:" << dy;
1491             if (alpha <= sp->axisLockThreshold) {
1492                 if (vertical)
1493                     deltaPixel.setX(0);
1494                 else
1495                     deltaPixel.setY(0);
1496             }
1497         }
1498     }
1499
1500     // calculate velocity (if the user would release the mouse NOW)
1501     updateVelocity(deltaPixel, deltaTime);
1502
1503     // restrict velocity, if content is not scrollable
1504     QRectF max = contentPosRange;
1505     bool canScrollX = (max.width() > 0) || (sp->hOvershootPolicy == QScrollerProperties::OvershootAlwaysOn);
1506     bool canScrollY = (max.height() > 0) || (sp->vOvershootPolicy == QScrollerProperties::OvershootAlwaysOn);
1507
1508     if (!canScrollX) {
1509         deltaPixel.setX(0);
1510         releaseVelocity.setX(0);
1511     }
1512     if (!canScrollY) {
1513         deltaPixel.setY(0);
1514         releaseVelocity.setY(0);
1515     }
1516
1517 //    if (firstDrag) {
1518 //        // Do not delay the first drag
1519 //        setContentPositionHelper(q->contentPosition() - overshootDistance - deltaPixel);
1520 //        dragDistance = QPointF(0, 0);
1521 //    } else {
1522     dragDistance += deltaPixel;
1523 //    }
1524 //qScrollerDebug() << "######################" << deltaPixel << position.y() << lastPosition.y();
1525     if (canScrollX)
1526         lastPosition.setX(position.x());
1527     if (canScrollY)
1528         lastPosition.setY(position.y());
1529     lastTimestamp = timestamp;
1530 }
1531
1532 bool QScrollerPrivate::pressWhileInactive(const QPointF &position, qint64 timestamp)
1533 {
1534     if (prepareScrolling(position)) {
1535         const QScrollerPropertiesPrivate *sp = properties.d.data();
1536
1537         if (!contentPosRange.isNull() ||
1538             (sp->hOvershootPolicy == QScrollerProperties::OvershootAlwaysOn) ||
1539             (sp->vOvershootPolicy == QScrollerProperties::OvershootAlwaysOn)) {
1540
1541             lastPosition = pressPosition = position;
1542             lastTimestamp = pressTimestamp = timestamp;
1543             setState(QScroller::Pressed);
1544         }
1545     }
1546     return false;
1547 }
1548
1549 bool QScrollerPrivate::releaseWhilePressed(const QPointF &, qint64)
1550 {
1551     if (overshootPosition != QPointF(0.0, 0.0)) {
1552         setState(QScroller::Scrolling);
1553         return true;
1554     } else {
1555         setState(QScroller::Inactive);
1556         return false;
1557     }
1558 }
1559
1560 bool QScrollerPrivate::moveWhilePressed(const QPointF &position, qint64 timestamp)
1561 {
1562     Q_Q(QScroller);
1563     const QScrollerPropertiesPrivate *sp = properties.d.data();
1564     QPointF ppm = q->pixelPerMeter();
1565
1566     QPointF deltaPixel = position - pressPosition;
1567
1568     bool moveAborted = false;
1569     bool moveStarted = (((deltaPixel / ppm).manhattanLength()) > sp->dragStartDistance);
1570
1571     // check the direction of the mouse drag and abort if it's too much in the wrong direction.
1572     if (moveStarted) {
1573         QRectF max = contentPosRange;
1574         bool canScrollX = (max.width() > 0);
1575         bool canScrollY = (max.height() > 0);
1576
1577         if (sp->hOvershootPolicy == QScrollerProperties::OvershootAlwaysOn)
1578             canScrollX = true;
1579         if (sp->vOvershootPolicy == QScrollerProperties::OvershootAlwaysOn)
1580             canScrollY = true;
1581
1582         if (qAbs(deltaPixel.x() / ppm.x()) < qAbs(deltaPixel.y() / ppm.y())) {
1583             if (!canScrollY)
1584                 moveAborted = true;
1585         } else {
1586             if (!canScrollX)
1587                 moveAborted = true;
1588         }
1589     }
1590
1591     if (moveAborted) {
1592         setState(QScroller::Inactive);
1593         moveStarted = false;
1594
1595     } else if (moveStarted) {
1596         setState(QScroller::Dragging);
1597
1598         // subtract the dragStartDistance
1599         deltaPixel = deltaPixel - deltaPixel * (sp->dragStartDistance / deltaPixel.manhattanLength());
1600
1601         if (deltaPixel != QPointF(0, 0)) {
1602             // handleDrag updates lastPosition, lastTimestamp and velocity
1603             handleDrag(pressPosition + deltaPixel, timestamp);
1604         }
1605     }
1606     return moveStarted;
1607 }
1608
1609 bool QScrollerPrivate::moveWhileDragging(const QPointF &position, qint64 timestamp)
1610 {
1611     // handleDrag updates lastPosition, lastTimestamp and velocity
1612     handleDrag(position, timestamp);
1613     return true;
1614 }
1615
1616 void QScrollerPrivate::timerEventWhileDragging()
1617 {
1618     if (dragDistance != QPointF(0, 0)) {
1619         qScrollerDebug() << "QScroller::timerEventWhileDragging() -- dragDistance:" << dragDistance;
1620
1621         setContentPositionHelperDragging(-dragDistance);
1622         dragDistance = QPointF(0, 0);
1623     }
1624 }
1625
1626 bool QScrollerPrivate::releaseWhileDragging(const QPointF &position, qint64 timestamp)
1627 {
1628     Q_Q(QScroller);
1629     const QScrollerPropertiesPrivate *sp = properties.d.data();
1630
1631     // handleDrag updates lastPosition, lastTimestamp and velocity
1632     handleDrag(position, timestamp);
1633
1634     // check if we moved at all - this can happen if you stop a running
1635     // scroller with a press and release shortly afterwards
1636     QPointF deltaPixel = position - pressPosition;
1637     if (((deltaPixel / q->pixelPerMeter()).manhattanLength()) > sp->dragStartDistance) {
1638
1639         // handle accelerating flicks
1640         if ((oldVelocity != QPointF(0, 0)) && sp->acceleratingFlickMaximumTime &&
1641             ((timestamp - pressTimestamp) < qint64(sp->acceleratingFlickMaximumTime * 1000))) {
1642
1643             // - determine if the direction was changed
1644             int signX = 0, signY = 0;
1645             if (releaseVelocity.x())
1646                 signX = (releaseVelocity.x() > 0) == (oldVelocity.x() > 0) ? 1 : -1;
1647             if (releaseVelocity.y())
1648                 signY = (releaseVelocity.y() > 0) == (oldVelocity.y() > 0) ? 1 : -1;
1649
1650             if (signX > 0)
1651                 releaseVelocity.setX(qBound(-sp->maximumVelocity,
1652                                             oldVelocity.x() * sp->acceleratingFlickSpeedupFactor,
1653                                             sp->maximumVelocity));
1654             if (signY > 0)
1655                 releaseVelocity.setY(qBound(-sp->maximumVelocity,
1656                                             oldVelocity.y() * sp->acceleratingFlickSpeedupFactor,
1657                                             sp->maximumVelocity));
1658         }
1659     }
1660
1661     QPointF ppm = q->pixelPerMeter();
1662     createScrollingSegments(releaseVelocity.x(), contentPosition.x() + overshootPosition.x(), ppm.x(), Qt::Horizontal);
1663     createScrollingSegments(releaseVelocity.y(), contentPosition.y() + overshootPosition.y(), ppm.y(), Qt::Vertical);
1664
1665     qScrollerDebug() << "QScroller::releaseWhileDragging() -- velocity:" << releaseVelocity << "-- minimum velocity:" << sp->minimumVelocity << "overshoot" << overshootPosition;
1666
1667     if (xSegments.isEmpty() && ySegments.isEmpty())
1668         setState(QScroller::Inactive);
1669     else
1670         setState(QScroller::Scrolling);
1671
1672     return true;
1673 }
1674
1675 void QScrollerPrivate::timerEventWhileScrolling()
1676 {
1677     qScrollerDebug() << "QScroller::timerEventWhileScrolling()";
1678
1679     setContentPositionHelperScrolling();
1680     if (xSegments.isEmpty() && ySegments.isEmpty())
1681         setState(QScroller::Inactive);
1682 }
1683
1684 bool QScrollerPrivate::pressWhileScrolling(const QPointF &position, qint64 timestamp)
1685 {
1686     Q_Q(QScroller);
1687
1688     if ((q->velocity() <= properties.d->maximumClickThroughVelocity) &&
1689         (overshootPosition == QPointF(0.0, 0.0))) {
1690         setState(QScroller::Inactive);
1691         return false;
1692     } else {
1693         lastPosition = pressPosition = position;
1694         lastTimestamp = pressTimestamp = timestamp;
1695         setState(QScroller::Pressed);
1696         setState(QScroller::Dragging);
1697         return true;
1698     }
1699 }
1700
1701 /*! \internal
1702     This function handles all state changes of the scroller.
1703 */
1704 void QScrollerPrivate::setState(QScroller::State newstate)
1705 {
1706     Q_Q(QScroller);
1707     bool sendLastScroll = false;
1708
1709     if (state == newstate)
1710         return;
1711
1712     qScrollerDebug() << q << "QScroller::setState(" << stateName(newstate) << ")";
1713
1714     switch (newstate) {
1715     case QScroller::Inactive:
1716 #ifndef QT_NO_ANIMATION
1717         scrollTimer->stop();
1718 #endif
1719
1720         // send the last scroll event (but only after the current state change was finished)
1721         if (!firstScroll)
1722             sendLastScroll = true;
1723
1724         releaseVelocity = QPointF(0, 0);
1725         break;
1726
1727     case QScroller::Pressed:
1728 #ifndef QT_NO_ANIMATION
1729         scrollTimer->stop();
1730 #endif
1731
1732         oldVelocity = releaseVelocity;
1733         releaseVelocity = QPointF(0, 0);
1734         break;
1735
1736     case QScroller::Dragging:
1737         dragDistance = QPointF(0, 0);
1738 #ifndef QT_NO_ANIMATION
1739         if (state == QScroller::Pressed)
1740             scrollTimer->start();
1741 #endif
1742         break;
1743
1744     case QScroller::Scrolling:
1745 #ifndef QT_NO_ANIMATION
1746         scrollTimer->start();
1747 #endif
1748         break;
1749     }
1750
1751     qSwap(state, newstate);
1752
1753     if (sendLastScroll) {
1754         QScrollEvent se(contentPosition, overshootPosition, QScrollEvent::ScrollFinished);
1755         sendEvent(target, &se);
1756         firstScroll = true;
1757     }
1758     if (state == QScroller::Dragging || state == QScroller::Scrolling)
1759         qt_activeScrollers()->insert(q);
1760     else
1761         qt_activeScrollers()->remove(q);
1762     emit q->stateChanged(state);
1763 }
1764
1765
1766 /*! \internal
1767     Helps when setting the content position.
1768     It will try to move the content by the requested delta but stop in case
1769     when we are coming back from an overshoot or a scrollTo.
1770     It will also indicate a new overshooting condition by the overshootX and oversthootY flags.
1771
1772     In this cases it will reset the velocity variables and other flags.
1773
1774     Also keeps track of the current over-shooting value in overshootPosition.
1775
1776     \a deltaPos is the amount of pixels the current content position should be moved
1777 */
1778 void QScrollerPrivate::setContentPositionHelperDragging(const QPointF &deltaPos)
1779 {
1780     const QScrollerPropertiesPrivate *sp = properties.d.data();
1781
1782     if (sp->overshootDragResistanceFactor)
1783         overshootPosition /= sp->overshootDragResistanceFactor;
1784
1785     QPointF oldPos = contentPosition + overshootPosition;
1786     QPointF newPos = oldPos + deltaPos;
1787
1788     qScrollerDebug() << "QScroller::setContentPositionHelperDragging(" << deltaPos << " [pix])";
1789     qScrollerDebug() << "  --> overshoot:" << overshootPosition << "- old pos:" << oldPos << "- new pos:" << newPos;
1790
1791     QPointF oldClampedPos = clampToRect(oldPos, contentPosRange);
1792     QPointF newClampedPos = clampToRect(newPos, contentPosRange);
1793
1794     // --- handle overshooting and stop if the coordinate is going back inside the normal area
1795     bool alwaysOvershootX = (sp->hOvershootPolicy == QScrollerProperties::OvershootAlwaysOn);
1796     bool alwaysOvershootY = (sp->vOvershootPolicy == QScrollerProperties::OvershootAlwaysOn);
1797     bool noOvershootX = (sp->hOvershootPolicy == QScrollerProperties::OvershootAlwaysOff) ||
1798                         ((state == QScroller::Dragging) && !sp->overshootDragResistanceFactor) ||
1799                         !sp->overshootDragDistanceFactor;
1800     bool noOvershootY = (sp->vOvershootPolicy == QScrollerProperties::OvershootAlwaysOff) ||
1801                         ((state == QScroller::Dragging) && !sp->overshootDragResistanceFactor) ||
1802                         !sp->overshootDragDistanceFactor;
1803     bool canOvershootX = !noOvershootX && (alwaysOvershootX || contentPosRange.width());
1804     bool canOvershootY = !noOvershootY && (alwaysOvershootY || contentPosRange.height());
1805
1806     qreal oldOvershootX = (canOvershootX) ? oldPos.x() - oldClampedPos.x() : 0;
1807     qreal oldOvershootY = (canOvershootY) ? oldPos.y() - oldClampedPos.y() : 0;
1808
1809     qreal newOvershootX = (canOvershootX) ? newPos.x() - newClampedPos.x() : 0;
1810     qreal newOvershootY = (canOvershootY) ? newPos.y() - newClampedPos.y() : 0;
1811
1812     qreal maxOvershootX = viewportSize.width() * sp->overshootDragDistanceFactor;
1813     qreal maxOvershootY = viewportSize.height() * sp->overshootDragDistanceFactor;
1814
1815     qScrollerDebug() << "  --> noOs:" << noOvershootX << "drf:" << sp->overshootDragResistanceFactor << "mdf:" << sp->overshootScrollDistanceFactor << "ossP:"<<sp->hOvershootPolicy;
1816     qScrollerDebug() << "  --> canOS:" << canOvershootX << "newOS:" << newOvershootX << "maxOS:" << maxOvershootX;
1817
1818     if (sp->overshootDragResistanceFactor) {
1819         oldOvershootX *= sp->overshootDragResistanceFactor;
1820         oldOvershootY *= sp->overshootDragResistanceFactor;
1821         newOvershootX *= sp->overshootDragResistanceFactor;
1822         newOvershootY *= sp->overshootDragResistanceFactor;
1823     }
1824
1825     // -- stop at the maximum overshoot distance
1826
1827     newOvershootX = qBound(-maxOvershootX, newOvershootX, maxOvershootX);
1828     newOvershootY = qBound(-maxOvershootY, newOvershootY, maxOvershootY);
1829
1830     overshootPosition.setX(newOvershootX);
1831     overshootPosition.setY(newOvershootY);
1832     contentPosition = newClampedPos;
1833
1834     QScrollEvent se(contentPosition, overshootPosition, firstScroll ? QScrollEvent::ScrollStarted : QScrollEvent::ScrollUpdated);
1835     sendEvent(target, &se);
1836     firstScroll = false;
1837
1838     qScrollerDebug() << "  --> new position:" << newClampedPos << "- new overshoot:" << overshootPosition <<
1839                         "- overshoot x/y?:" << overshootPosition;
1840 }
1841
1842
1843 qreal QScrollerPrivate::nextSegmentPosition(QQueue<ScrollSegment> &segments, qint64 now, qreal oldPos)
1844 {
1845     qreal pos = oldPos;
1846
1847     // check the X segments for new positions
1848     while (!segments.isEmpty()) {
1849         const ScrollSegment s = segments.head();
1850
1851         if ((s.startTime + s.deltaTime * s.stopProgress) <= now) {
1852             segments.dequeue();
1853             pos = s.stopPos;
1854         } else if (s.startTime <= now) {
1855             qreal progress = qreal(now - s.startTime) / qreal(s.deltaTime);
1856             pos = s.startPos + s.deltaPos * s.curve.valueForProgress(progress);
1857             if (s.deltaPos > 0 ? pos > s.stopPos : pos < s.stopPos) {
1858                 segments.dequeue();
1859                 pos = s.stopPos;
1860             } else {
1861                 break;
1862             }
1863         } else {
1864             break;
1865         }
1866     }
1867     return pos;
1868 }
1869
1870 void QScrollerPrivate::setContentPositionHelperScrolling()
1871 {
1872     qint64 now = monotonicTimer.elapsed();
1873     QPointF newPos = contentPosition + overshootPosition;
1874
1875     newPos.setX(nextSegmentPosition(xSegments, now, newPos.x()));
1876     newPos.setY(nextSegmentPosition(ySegments, now, newPos.y()));
1877
1878     // -- set the position and handle overshoot
1879     qScrollerDebug() << "QScroller::setContentPositionHelperScrolling()";
1880     qScrollerDebug() << "  --> overshoot:" << overshootPosition << "- new pos:" << newPos;
1881
1882     QPointF newClampedPos = clampToRect(newPos, contentPosRange);
1883
1884     overshootPosition = newPos - newClampedPos;
1885     contentPosition = newClampedPos;
1886
1887     QScrollEvent se(contentPosition, overshootPosition, firstScroll ? QScrollEvent::ScrollStarted : QScrollEvent::ScrollUpdated);
1888     sendEvent(target, &se);
1889     firstScroll = false;
1890
1891     qScrollerDebug() << "  --> new position:" << newClampedPos << "- new overshoot:" << overshootPosition;
1892 }
1893
1894 /*! \internal
1895     Returns the next snap point in direction.
1896     If \a direction >0 it will return the next snap point that is larger than the current position.
1897     If \a direction <0 it will return the next snap point that is smaller than the current position.
1898     If \a direction ==0 it will return the nearest snap point (or the current position if we are already
1899     on a snap point.
1900     Returns the nearest snap position or NaN if no such point could be found.
1901  */
1902 qreal QScrollerPrivate::nextSnapPos(qreal p, int dir, Qt::Orientation orientation)
1903 {
1904     qreal bestSnapPos = Q_QNAN;
1905     qreal bestSnapPosDist = Q_INFINITY;
1906
1907     qreal minPos;
1908     qreal maxPos;
1909
1910     if (orientation == Qt::Horizontal) {
1911         minPos = contentPosRange.left();
1912         maxPos = contentPosRange.right();
1913     } else {
1914         minPos = contentPosRange.top();
1915         maxPos = contentPosRange.bottom();
1916     }
1917
1918     if (orientation == Qt::Horizontal) {
1919         // the snap points in the list
1920         foreach (qreal snapPos, snapPositionsX) {
1921             qreal snapPosDist = snapPos - p;
1922             if ((dir > 0 && snapPosDist < 0) ||
1923                 (dir < 0 && snapPosDist > 0))
1924                 continue; // wrong direction
1925             if (snapPos < minPos || snapPos > maxPos )
1926                 continue; // invalid
1927
1928             if (qIsNaN(bestSnapPos) ||
1929                 qAbs(snapPosDist) < bestSnapPosDist ) {
1930                 bestSnapPos = snapPos;
1931                 bestSnapPosDist = qAbs(snapPosDist);
1932             }
1933         }
1934
1935         // the snap point interval
1936         if (snapIntervalX > 0.0) {
1937             qreal first = minPos + snapFirstX;
1938             qreal snapPos;
1939             if (dir > 0)
1940                 snapPos = qCeil((p - first) / snapIntervalX) * snapIntervalX + first;
1941             else if (dir < 0)
1942                 snapPos = qFloor((p - first) / snapIntervalX) * snapIntervalX + first;
1943             else if (p <= first)
1944                 snapPos = first;
1945             else
1946             {
1947                 qreal last = qFloor((maxPos - first) / snapIntervalX) * snapIntervalX + first;
1948                 if (p >= last)
1949                     snapPos = last;
1950                 else
1951                     snapPos = qRound((p - first) / snapIntervalX) * snapIntervalX + first;
1952             }
1953
1954             if (snapPos >= first && snapPos <= maxPos ) {
1955                 qreal snapPosDist = snapPos - p;
1956
1957                 if (qIsNaN(bestSnapPos) ||
1958                     qAbs(snapPosDist) < bestSnapPosDist ) {
1959                     bestSnapPos = snapPos;
1960                     bestSnapPosDist = qAbs(snapPosDist);
1961                 }
1962             }
1963         }
1964
1965     } else { // (orientation == Qt::Vertical)
1966         // the snap points in the list
1967         foreach (qreal snapPos, snapPositionsY) {
1968             qreal snapPosDist = snapPos - p;
1969             if ((dir > 0 && snapPosDist < 0) ||
1970                 (dir < 0 && snapPosDist > 0))
1971                 continue; // wrong direction
1972             if (snapPos < minPos || snapPos > maxPos )
1973                 continue; // invalid
1974
1975             if (qIsNaN(bestSnapPos) ||
1976                 qAbs(snapPosDist) < bestSnapPosDist) {
1977                 bestSnapPos = snapPos;
1978                 bestSnapPosDist = qAbs(snapPosDist);
1979             }
1980         }
1981
1982         // the snap point interval
1983         if (snapIntervalY > 0.0) {
1984             qreal first = minPos + snapFirstY;
1985             qreal snapPos;
1986             if (dir > 0)
1987                 snapPos = qCeil((p - first) / snapIntervalY) * snapIntervalY + first;
1988             else if (dir < 0)
1989                 snapPos = qFloor((p - first) / snapIntervalY) * snapIntervalY + first;
1990             else if (p <= first)
1991                 snapPos = first;
1992             else
1993             {
1994                 qreal last = qFloor((maxPos - first) / snapIntervalY) * snapIntervalY + first;
1995                 if (p >= last)
1996                     snapPos = last;
1997                 else
1998                     snapPos = qRound((p - first) / snapIntervalY) * snapIntervalY + first;
1999             }
2000
2001             if (snapPos >= first && snapPos <= maxPos ) {
2002                 qreal snapPosDist = snapPos - p;
2003
2004                 if (qIsNaN(bestSnapPos) ||
2005                     qAbs(snapPosDist) < bestSnapPosDist) {
2006                     bestSnapPos = snapPos;
2007                     bestSnapPosDist = qAbs(snapPosDist);
2008                 }
2009             }
2010         }
2011     }
2012
2013     return bestSnapPos;
2014 }
2015
2016 /*!
2017     \enum QScroller::State
2018
2019     This enum contains the different QScroller states.
2020
2021     \value Inactive The scroller is not scrolling and nothing is pressed.
2022     \value Pressed A touch event was received or the mouse button was pressed but the scroll area is currently not dragged.
2023     \value Dragging The scroll area is currently following the touch point or mouse.
2024     \value Scrolling The scroll area is moving on it's own.
2025 */
2026
2027 /*!
2028     \enum QScroller::ScrollerGestureType
2029
2030     This enum contains the different gesture types that are supported by the QScroller gesture recognizer.
2031
2032     \value TouchGesture The gesture recognizer will only trigger on touch
2033         events.  Specifically it will react on single touch points when using a
2034         touch screen and dual touch points when using a touchpad.
2035     \value LeftMouseButtonGesture The gesture recognizer will only trigger on left mouse button events.
2036     \value MiddleMouseButtonGesture The gesture recognizer will only trigger on middle mouse button events.
2037     \value RightMouseButtonGesture The gesture recognizer will only trigger on right mouse button events.
2038 */
2039
2040 /*!
2041     \enum QScroller::Input
2042
2043     This enum contains an input device agnostic view of input events that are relevant for QScroller.
2044
2045     \value InputPress   The user pressed the input device (e.g.  QEvent::MouseButtonPress,
2046                         QEvent::GraphicsSceneMousePress, QEvent::TouchBegin)
2047
2048     \value InputMove    The user moved the input device (e.g.  QEvent::MouseMove,
2049                         QEvent::GraphicsSceneMouseMove, QEvent::TouchUpdate)
2050
2051     \value InputRelease The user released the input device (e.g.  QEvent::MouseButtonRelease,
2052                         QEvent::GraphicsSceneMouseRelease, QEvent::TouchEnd)
2053
2054 */
2055
2056 QT_END_NAMESPACE