Initial import from qtquick2.
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsgpathview.cpp
1 // Commit: ac704e9f682378a5ec56e3f5c195dcf2f2dfa1ac
2 /****************************************************************************
3 **
4 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
5 ** All rights reserved.
6 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 **
8 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 **
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** No Commercial Usage
12 ** This file contains pre-release code and may not be distributed.
13 ** You may use this file in accordance with the terms and conditions
14 ** contained in the Technology Preview License Agreement accompanying
15 ** this package.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 **
39 ** $QT_END_LICENSE$
40 **
41 ****************************************************************************/
42
43 #include "qsgpathview_p.h"
44 #include "qsgpathview_p_p.h"
45 #include "qsgcanvas.h"
46
47 #include <private/qdeclarativestate_p.h>
48 #include <private/qdeclarativeopenmetaobject_p.h>
49 #include <private/qlistmodelinterface_p.h>
50
51 #include <QtGui/qevent.h>
52 #include <QtGui/qgraphicssceneevent.h>
53 #include <QtGui/qapplication.h>
54 #include <QtCore/qmath.h>
55 #include <math.h>
56
57 QT_BEGIN_NAMESPACE
58
59 inline qreal qmlMod(qreal x, qreal y)
60 {
61 #ifdef QT_USE_MATH_H_FLOATS
62     if(sizeof(qreal) == sizeof(float))
63         return fmodf(float(x), float(y));
64     else
65 #endif
66         return fmod(x, y);
67 }
68
69 static QDeclarativeOpenMetaObjectType *qPathViewAttachedType = 0;
70
71 QSGPathViewAttached::QSGPathViewAttached(QObject *parent)
72 : QObject(parent), m_percent(-1), m_view(0), m_onPath(false), m_isCurrent(false)
73 {
74     if (qPathViewAttachedType) {
75         m_metaobject = new QDeclarativeOpenMetaObject(this, qPathViewAttachedType);
76         m_metaobject->setCached(true);
77     } else {
78         m_metaobject = new QDeclarativeOpenMetaObject(this);
79     }
80 }
81
82 QSGPathViewAttached::~QSGPathViewAttached()
83 {
84 }
85
86 QVariant QSGPathViewAttached::value(const QByteArray &name) const
87 {
88     return m_metaobject->value(name);
89 }
90 void QSGPathViewAttached::setValue(const QByteArray &name, const QVariant &val)
91 {
92     m_metaobject->setValue(name, val);
93 }
94
95
96 void QSGPathViewPrivate::init()
97 {
98     Q_Q(QSGPathView);
99     offset = 0;
100     q->setAcceptedMouseButtons(Qt::LeftButton);
101     q->setFlag(QSGItem::ItemIsFocusScope);
102     q->setFiltersChildMouseEvents(true);
103     q->connect(&tl, SIGNAL(updated()), q, SLOT(ticked()));
104     lastPosTime.invalidate();
105     static int timelineCompletedIdx = -1;
106     static int movementEndingIdx = -1;
107     if (timelineCompletedIdx == -1) {
108         timelineCompletedIdx = QDeclarativeTimeLine::staticMetaObject.indexOfSignal("completed()");
109         movementEndingIdx = QSGPathView::staticMetaObject.indexOfSlot("movementEnding()");
110     }
111     QMetaObject::connect(&tl, timelineCompletedIdx,
112                          q, movementEndingIdx, Qt::DirectConnection);
113 }
114
115 QSGItem *QSGPathViewPrivate::getItem(int modelIndex)
116 {
117     Q_Q(QSGPathView);
118     requestedIndex = modelIndex;
119     QSGItem *item = model->item(modelIndex, false);
120     if (item) {
121         if (!attType) {
122             // pre-create one metatype to share with all attached objects
123             attType = new QDeclarativeOpenMetaObjectType(&QSGPathViewAttached::staticMetaObject, qmlEngine(q));
124             foreach(const QString &attr, path->attributes())
125                 attType->createProperty(attr.toUtf8());
126         }
127         qPathViewAttachedType = attType;
128         QSGPathViewAttached *att = static_cast<QSGPathViewAttached *>(qmlAttachedPropertiesObject<QSGPathView>(item));
129         qPathViewAttachedType = 0;
130         if (att) {
131             att->m_view = q;
132             att->setOnPath(true);
133         }
134         item->setParentItem(q);
135         QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
136         itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
137     }
138     requestedIndex = -1;
139     return item;
140 }
141
142 void QSGPathViewPrivate::releaseItem(QSGItem *item)
143 {
144     if (!item || !model)
145         return;
146     QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
147     itemPrivate->removeItemChangeListener(this, QSGItemPrivate::Geometry);
148     if (model->release(item) == 0) {
149         // item was not destroyed, and we no longer reference it.
150         if (QSGPathViewAttached *att = attached(item))
151             att->setOnPath(false);
152     }
153 }
154
155 QSGPathViewAttached *QSGPathViewPrivate::attached(QSGItem *item)
156 {
157     return static_cast<QSGPathViewAttached *>(qmlAttachedPropertiesObject<QSGPathView>(item, false));
158 }
159
160 void QSGPathViewPrivate::clear()
161 {
162     for (int i=0; i<items.count(); i++){
163         QSGItem *p = items[i];
164         releaseItem(p);
165     }
166     items.clear();
167 }
168
169 void QSGPathViewPrivate::updateMappedRange()
170 {
171     if (model && pathItems != -1 && pathItems < modelCount)
172         mappedRange = qreal(pathItems)/modelCount;
173     else
174         mappedRange = 1.0;
175 }
176
177 qreal QSGPathViewPrivate::positionOfIndex(qreal index) const
178 {
179     qreal pos = -1.0;
180
181     if (model && index >= 0 && index < modelCount) {
182         qreal start = 0.0;
183         if (haveHighlightRange && highlightRangeMode != QSGPathView::NoHighlightRange)
184             start = highlightRangeStart;
185         qreal globalPos = index + offset;
186         globalPos = qmlMod(globalPos, qreal(modelCount)) / modelCount;
187         if (pathItems != -1 && pathItems < modelCount) {
188             globalPos += start * mappedRange;
189             globalPos = qmlMod(globalPos, 1.0);
190             if (globalPos < mappedRange)
191                 pos = globalPos / mappedRange;
192         } else {
193             pos = qmlMod(globalPos + start, 1.0);
194         }
195     }
196
197     return pos;
198 }
199
200 void QSGPathViewPrivate::createHighlight()
201 {
202     Q_Q(QSGPathView);
203     if (!q->isComponentComplete())
204         return;
205
206     bool changed = false;
207     if (highlightItem) {
208         delete highlightItem;
209         highlightItem = 0;
210         changed = true;
211     }
212
213     QSGItem *item = 0;
214     if (highlightComponent) {
215         QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q));
216         QObject *nobj = highlightComponent->create(highlightContext);
217         if (nobj) {
218             QDeclarative_setParent_noEvent(highlightContext, nobj);
219             item = qobject_cast<QSGItem *>(nobj);
220             if (!item)
221                 delete nobj;
222         } else {
223             delete highlightContext;
224         }
225     } else {
226         item = new QSGItem;
227     }
228     if (item) {
229         QDeclarative_setParent_noEvent(item, q);
230         item->setParentItem(q);
231         highlightItem = item;
232         changed = true;
233     }
234     if (changed)
235         emit q->highlightItemChanged();
236 }
237
238 void QSGPathViewPrivate::updateHighlight()
239 {
240     Q_Q(QSGPathView);
241     if (!q->isComponentComplete() || !isValid())
242         return;
243     if (highlightItem) {
244         if (haveHighlightRange && highlightRangeMode == QSGPathView::StrictlyEnforceRange) {
245             updateItem(highlightItem, highlightRangeStart);
246         } else {
247             qreal target = currentIndex;
248
249             offsetAdj = 0.0;
250             tl.reset(moveHighlight);
251             moveHighlight.setValue(highlightPosition);
252
253             const int duration = highlightMoveDuration;
254
255             if (target - highlightPosition > modelCount/2) {
256                 highlightUp = false;
257                 qreal distance = modelCount - target + highlightPosition;
258                 tl.move(moveHighlight, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance));
259                 tl.set(moveHighlight, modelCount-0.01);
260                 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (modelCount-target) / distance));
261             } else if (target - highlightPosition <= -modelCount/2) {
262                 highlightUp = true;
263                 qreal distance = modelCount - highlightPosition + target;
264                 tl.move(moveHighlight, modelCount-0.01, QEasingCurve(QEasingCurve::InQuad), int(duration * (modelCount-highlightPosition) / distance));
265                 tl.set(moveHighlight, 0.0);
266                 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance));
267             } else {
268                 highlightUp = highlightPosition - target < 0;
269                 tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration);
270             }
271         }
272     }
273 }
274
275 void QSGPathViewPrivate::setHighlightPosition(qreal pos)
276 {
277     if (pos != highlightPosition) {
278         qreal start = 0.0;
279         qreal end = 1.0;
280         if (haveHighlightRange && highlightRangeMode != QSGPathView::NoHighlightRange) {
281             start = highlightRangeStart;
282             end = highlightRangeEnd;
283         }
284
285         qreal range = qreal(modelCount);
286         // calc normalized position of highlight relative to offset
287         qreal relativeHighlight = qmlMod(pos + offset, range) / range;
288
289         if (!highlightUp && relativeHighlight > end * mappedRange) {
290             qreal diff = 1.0 - relativeHighlight;
291             setOffset(offset + diff * range);
292         } else if (highlightUp && relativeHighlight >= (end - start) * mappedRange) {
293             qreal diff = relativeHighlight - (end - start) * mappedRange;
294             setOffset(offset - diff * range - 0.00001);
295         }
296
297         highlightPosition = pos;
298         qreal pathPos = positionOfIndex(pos);
299         updateItem(highlightItem, pathPos);
300         if (QSGPathViewAttached *att = attached(highlightItem))
301             att->setOnPath(pathPos != -1.0);
302     }
303 }
304
305 void QSGPathView::pathUpdated()
306 {
307     Q_D(QSGPathView);
308     QList<QSGItem*>::iterator it = d->items.begin();
309     while (it != d->items.end()) {
310         QSGItem *item = *it;
311         if (QSGPathViewAttached *att = d->attached(item))
312             att->m_percent = -1;
313         ++it;
314     }
315     refill();
316 }
317
318 void QSGPathViewPrivate::updateItem(QSGItem *item, qreal percent)
319 {
320     if (QSGPathViewAttached *att = attached(item)) {
321         if (qFuzzyCompare(att->m_percent, percent))
322             return;
323         att->m_percent = percent;
324         foreach(const QString &attr, path->attributes())
325             att->setValue(attr.toUtf8(), path->attributeAt(attr, percent));
326     }
327     QPointF pf = path->pointAt(percent);
328     item->setX(qRound(pf.x() - item->width()/2));
329     item->setY(qRound(pf.y() - item->height()/2));
330 }
331
332 void QSGPathViewPrivate::regenerate()
333 {
334     Q_Q(QSGPathView);
335     if (!q->isComponentComplete())
336         return;
337
338     clear();
339
340     if (!isValid())
341         return;
342
343     firstIndex = -1;
344     updateMappedRange();
345     q->refill();
346 }
347
348 QSGPathView::QSGPathView(QSGItem *parent)
349   : QSGItem(*(new QSGPathViewPrivate), parent)
350 {
351     Q_D(QSGPathView);
352     d->init();
353 }
354
355 QSGPathView::~QSGPathView()
356 {
357     Q_D(QSGPathView);
358     d->clear();
359     if (d->attType)
360         d->attType->release();
361     if (d->ownModel)
362         delete d->model;
363 }
364
365 QVariant QSGPathView::model() const
366 {
367     Q_D(const QSGPathView);
368     return d->modelVariant;
369 }
370
371 void QSGPathView::setModel(const QVariant &model)
372 {
373     Q_D(QSGPathView);
374     if (d->modelVariant == model)
375         return;
376
377     if (d->model) {
378         disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
379         disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
380         disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
381         disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
382         disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
383         for (int i=0; i<d->items.count(); i++){
384             QSGItem *p = d->items[i];
385             d->model->release(p);
386         }
387         d->items.clear();
388     }
389
390     d->modelVariant = model;
391     QObject *object = qvariant_cast<QObject*>(model);
392     QSGVisualModel *vim = 0;
393     if (object && (vim = qobject_cast<QSGVisualModel *>(object))) {
394         if (d->ownModel) {
395             delete d->model;
396             d->ownModel = false;
397         }
398         d->model = vim;
399     } else {
400         if (!d->ownModel) {
401             d->model = new QSGVisualDataModel(qmlContext(this), this);
402             d->ownModel = true;
403         }
404         if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
405             dataModel->setModel(model);
406     }
407     d->modelCount = 0;
408     if (d->model) {
409         connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
410         connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
411         connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
412         connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
413         connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
414         d->modelCount = d->model->count();
415         if (d->model->count())
416             d->offset = qmlMod(d->offset, qreal(d->model->count()));
417         if (d->offset < 0)
418             d->offset = d->model->count() + d->offset;
419 }
420     d->regenerate();
421     d->fixOffset();
422     emit countChanged();
423     emit modelChanged();
424 }
425
426 int QSGPathView::count() const
427 {
428     Q_D(const QSGPathView);
429     return d->model ? d->modelCount : 0;
430 }
431
432 QDeclarativePath *QSGPathView::path() const
433 {
434     Q_D(const QSGPathView);
435     return d->path;
436 }
437
438 void QSGPathView::setPath(QDeclarativePath *path)
439 {
440     Q_D(QSGPathView);
441     if (d->path == path)
442         return;
443     if (d->path)
444         disconnect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated()));
445     d->path = path;
446     connect(d->path, SIGNAL(changed()), this, SLOT(pathUpdated()));
447     if (d->isValid() && isComponentComplete()) {
448         d->clear();
449         if (d->attType) {
450             d->attType->release();
451             d->attType = 0;
452         }
453         d->regenerate();
454     }
455     emit pathChanged();
456 }
457
458 int QSGPathView::currentIndex() const
459 {
460     Q_D(const QSGPathView);
461     return d->currentIndex;
462 }
463
464 void QSGPathView::setCurrentIndex(int idx)
465 {
466     Q_D(QSGPathView);
467     if (d->model && d->modelCount)
468         idx = qAbs(idx % d->modelCount);
469     if (d->model && idx != d->currentIndex) {
470         if (d->modelCount) {
471             int itemIndex = (d->currentIndex - d->firstIndex + d->modelCount) % d->modelCount;
472             if (itemIndex < d->items.count()) {
473                 if (QSGItem *item = d->items.at(itemIndex)) {
474                     if (QSGPathViewAttached *att = d->attached(item))
475                         att->setIsCurrentItem(false);
476                 }
477             }
478         }
479         d->currentItem = 0;
480         d->moveReason = QSGPathViewPrivate::SetIndex;
481         d->currentIndex = idx;
482         if (d->modelCount) {
483             if (d->haveHighlightRange && d->highlightRangeMode == QSGPathView::StrictlyEnforceRange)
484                 d->snapToCurrent();
485             int itemIndex = (idx - d->firstIndex + d->modelCount) % d->modelCount;
486             if (itemIndex < d->items.count()) {
487                 d->currentItem = d->items.at(itemIndex);
488                 d->currentItem->setFocus(true);
489                 if (QSGPathViewAttached *att = d->attached(d->currentItem))
490                     att->setIsCurrentItem(true);
491             }
492             d->currentItemOffset = d->positionOfIndex(d->currentIndex);
493             d->updateHighlight();
494         }
495         emit currentIndexChanged();
496     }
497 }
498
499 void QSGPathView::incrementCurrentIndex()
500 {
501     Q_D(QSGPathView);
502     d->moveDirection = QSGPathViewPrivate::Positive;
503     setCurrentIndex(currentIndex()+1);
504 }
505
506 void QSGPathView::decrementCurrentIndex()
507 {
508     Q_D(QSGPathView);
509     if (d->model && d->modelCount) {
510         int idx = currentIndex()-1;
511         if (idx < 0)
512             idx = d->modelCount - 1;
513         d->moveDirection = QSGPathViewPrivate::Negative;
514         setCurrentIndex(idx);
515     }
516 }
517
518 qreal QSGPathView::offset() const
519 {
520     Q_D(const QSGPathView);
521     return d->offset;
522 }
523
524 void QSGPathView::setOffset(qreal offset)
525 {
526     Q_D(QSGPathView);
527     d->setOffset(offset);
528     d->updateCurrent();
529 }
530
531 void QSGPathViewPrivate::setOffset(qreal o)
532 {
533     Q_Q(QSGPathView);
534     if (offset != o) {
535         if (isValid() && q->isComponentComplete()) {
536             offset = qmlMod(o, qreal(modelCount));
537             if (offset < 0)
538                 offset += qreal(modelCount);
539             q->refill();
540         } else {
541             offset = o;
542         }
543         emit q->offsetChanged();
544     }
545 }
546
547 void QSGPathViewPrivate::setAdjustedOffset(qreal o)
548 {
549     setOffset(o+offsetAdj);
550 }
551
552 QDeclarativeComponent *QSGPathView::highlight() const
553 {
554     Q_D(const QSGPathView);
555     return d->highlightComponent;
556 }
557
558 void QSGPathView::setHighlight(QDeclarativeComponent *highlight)
559 {
560     Q_D(QSGPathView);
561     if (highlight != d->highlightComponent) {
562         d->highlightComponent = highlight;
563         d->createHighlight();
564         d->updateHighlight();
565         emit highlightChanged();
566     }
567 }
568
569 QSGItem *QSGPathView::highlightItem()
570 {
571     Q_D(const QSGPathView);
572     return d->highlightItem;
573 }
574
575 qreal QSGPathView::preferredHighlightBegin() const
576 {
577     Q_D(const QSGPathView);
578     return d->highlightRangeStart;
579 }
580
581 void QSGPathView::setPreferredHighlightBegin(qreal start)
582 {
583     Q_D(QSGPathView);
584     if (d->highlightRangeStart == start || start < 0 || start > 1.0)
585         return;
586     d->highlightRangeStart = start;
587     d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
588     refill();
589     emit preferredHighlightBeginChanged();
590 }
591
592 qreal QSGPathView::preferredHighlightEnd() const
593 {
594     Q_D(const QSGPathView);
595     return d->highlightRangeEnd;
596 }
597
598 void QSGPathView::setPreferredHighlightEnd(qreal end)
599 {
600     Q_D(QSGPathView);
601     if (d->highlightRangeEnd == end || end < 0 || end > 1.0)
602         return;
603     d->highlightRangeEnd = end;
604     d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
605     refill();
606     emit preferredHighlightEndChanged();
607 }
608
609 QSGPathView::HighlightRangeMode QSGPathView::highlightRangeMode() const
610 {
611     Q_D(const QSGPathView);
612     return d->highlightRangeMode;
613 }
614
615 void QSGPathView::setHighlightRangeMode(HighlightRangeMode mode)
616 {
617     Q_D(QSGPathView);
618     if (d->highlightRangeMode == mode)
619         return;
620     d->highlightRangeMode = mode;
621     d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
622     emit highlightRangeModeChanged();
623 }
624
625 int QSGPathView::highlightMoveDuration() const
626 {
627     Q_D(const QSGPathView);
628     return d->highlightMoveDuration;
629 }
630
631 void QSGPathView::setHighlightMoveDuration(int duration)
632 {
633     Q_D(QSGPathView);
634     if (d->highlightMoveDuration == duration)
635         return;
636     d->highlightMoveDuration = duration;
637     emit highlightMoveDurationChanged();
638 }
639
640 qreal QSGPathView::dragMargin() const
641 {
642     Q_D(const QSGPathView);
643     return d->dragMargin;
644 }
645
646 void QSGPathView::setDragMargin(qreal dragMargin)
647 {
648     Q_D(QSGPathView);
649     if (d->dragMargin == dragMargin)
650         return;
651     d->dragMargin = dragMargin;
652     emit dragMarginChanged();
653 }
654
655 qreal QSGPathView::flickDeceleration() const
656 {
657     Q_D(const QSGPathView);
658     return d->deceleration;
659 }
660
661 void QSGPathView::setFlickDeceleration(qreal dec)
662 {
663     Q_D(QSGPathView);
664     if (d->deceleration == dec)
665         return;
666     d->deceleration = dec;
667     emit flickDecelerationChanged();
668 }
669
670 bool QSGPathView::isInteractive() const
671 {
672     Q_D(const QSGPathView);
673     return d->interactive;
674 }
675
676 void QSGPathView::setInteractive(bool interactive)
677 {
678     Q_D(QSGPathView);
679     if (interactive != d->interactive) {
680         d->interactive = interactive;
681         if (!interactive)
682             d->tl.clear();
683         emit interactiveChanged();
684     }
685 }
686
687 bool QSGPathView::isMoving() const
688 {
689     Q_D(const QSGPathView);
690     return d->moving;
691 }
692
693 bool QSGPathView::isFlicking() const
694 {
695     Q_D(const QSGPathView);
696     return d->flicking;
697 }
698
699 QDeclarativeComponent *QSGPathView::delegate() const
700 {
701     Q_D(const QSGPathView);
702      if (d->model) {
703         if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
704             return dataModel->delegate();
705     }
706
707     return 0;
708 }
709
710 void QSGPathView::setDelegate(QDeclarativeComponent *delegate)
711 {
712     Q_D(QSGPathView);
713     if (delegate == this->delegate())
714         return;
715     if (!d->ownModel) {
716         d->model = new QSGVisualDataModel(qmlContext(this));
717         d->ownModel = true;
718     }
719     if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model)) {
720         dataModel->setDelegate(delegate);
721         d->modelCount = dataModel->count();
722         d->regenerate();
723         emit delegateChanged();
724     }
725 }
726
727 int QSGPathView::pathItemCount() const
728 {
729     Q_D(const QSGPathView);
730     return d->pathItems;
731 }
732
733 void QSGPathView::setPathItemCount(int i)
734 {
735     Q_D(QSGPathView);
736     if (i == d->pathItems)
737         return;
738     if (i < 1)
739         i = 1;
740     d->pathItems = i;
741     d->updateMappedRange();
742     if (d->isValid() && isComponentComplete()) {
743         d->regenerate();
744     }
745     emit pathItemCountChanged();
746 }
747
748 QPointF QSGPathViewPrivate::pointNear(const QPointF &point, qreal *nearPercent) const
749 {
750     //XXX maybe do recursively at increasing resolution.
751     qreal mindist = 1e10; // big number
752     QPointF nearPoint = path->pointAt(0);
753     qreal nearPc = 0;
754     for (qreal i=1; i < 1000; i++) {
755         QPointF pt = path->pointAt(i/1000.0);
756         QPointF diff = pt - point;
757         qreal dist = diff.x()*diff.x() + diff.y()*diff.y();
758         if (dist < mindist) {
759             nearPoint = pt;
760             nearPc = i;
761             mindist = dist;
762         }
763     }
764
765     if (nearPercent)
766         *nearPercent = nearPc / 1000.0;
767
768     return nearPoint;
769 }
770
771 void QSGPathView::mousePressEvent(QGraphicsSceneMouseEvent *event)
772 {
773     Q_D(QSGPathView);
774     if (d->interactive) {
775         d->handleMousePressEvent(event);
776         event->accept();
777     } else {
778         QSGItem::mousePressEvent(event);
779     }
780 }
781
782 void QSGPathViewPrivate::handleMousePressEvent(QGraphicsSceneMouseEvent *event)
783 {
784     Q_Q(QSGPathView);
785     if (!interactive || !items.count())
786         return;
787     QPointF scenePoint = q->mapToScene(event->pos());
788     int idx = 0;
789     for (; idx < items.count(); ++idx) {
790         QRectF rect = items.at(idx)->boundingRect();
791         rect = items.at(idx)->mapRectToScene(rect);
792         if (rect.contains(scenePoint))
793             break;
794     }
795     if (idx == items.count() && dragMargin == 0.)  // didn't click on an item
796         return;
797
798     startPoint = pointNear(event->pos(), &startPc);
799     if (idx == items.count()) {
800         qreal distance = qAbs(event->pos().x() - startPoint.x()) + qAbs(event->pos().y() - startPoint.y());
801         if (distance > dragMargin)
802             return;
803     }
804
805     if (tl.isActive() && flicking)
806         stealMouse = true; // If we've been flicked then steal the click.
807     else
808         stealMouse = false;
809
810     lastElapsed = 0;
811     lastDist = 0;
812     QSGItemPrivate::start(lastPosTime);
813     tl.clear();
814 }
815
816 void QSGPathView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
817 {
818     Q_D(QSGPathView);
819     if (d->interactive) {
820         d->handleMouseMoveEvent(event);
821         if (d->stealMouse)
822             setKeepMouseGrab(true);
823         event->accept();
824     } else {
825         QSGItem::mouseMoveEvent(event);
826     }
827 }
828
829 void QSGPathViewPrivate::handleMouseMoveEvent(QGraphicsSceneMouseEvent *event)
830 {
831     Q_Q(QSGPathView);
832     if (!interactive || !lastPosTime.isValid())
833         return;
834
835     qreal newPc;
836     QPointF pathPoint = pointNear(event->pos(), &newPc);
837     if (!stealMouse) {
838         QPointF delta = pathPoint - startPoint;
839         if (qAbs(delta.x()) > QApplication::startDragDistance() || qAbs(delta.y()) > QApplication::startDragDistance()) {
840             stealMouse = true;
841             startPc = newPc;
842         }
843     }
844
845     if (stealMouse) {
846         moveReason = QSGPathViewPrivate::Mouse;
847         qreal diff = (newPc - startPc)*modelCount*mappedRange;
848         if (diff) {
849             q->setOffset(offset + diff);
850
851             if (diff > modelCount/2)
852                 diff -= modelCount;
853             else if (diff < -modelCount/2)
854                 diff += modelCount;
855
856             lastElapsed = QSGItemPrivate::restart(lastPosTime);
857             lastDist = diff;
858             startPc = newPc;
859         }
860         if (!moving) {
861             moving = true;
862             emit q->movingChanged();
863             emit q->movementStarted();
864         }
865     }
866 }
867
868 void QSGPathView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
869 {
870     Q_D(QSGPathView);
871     if (d->interactive) {
872         d->handleMouseReleaseEvent(event);
873         event->accept();
874         ungrabMouse();
875     } else {
876         QSGItem::mouseReleaseEvent(event);
877     }
878 }
879
880 void QSGPathViewPrivate::handleMouseReleaseEvent(QGraphicsSceneMouseEvent *)
881 {
882     Q_Q(QSGPathView);
883     stealMouse = false;
884     q->setKeepMouseGrab(false);
885     if (!interactive || !lastPosTime.isValid())
886         return;
887
888     qreal elapsed = qreal(lastElapsed + QSGItemPrivate::elapsed(lastPosTime)) / 1000.;
889     qreal velocity = elapsed > 0. ? lastDist / elapsed : 0;
890     if (model && modelCount && qAbs(velocity) > 1.) {
891         qreal count = pathItems == -1 ? modelCount : pathItems;
892         if (qAbs(velocity) > count * 2) // limit velocity
893             velocity = (velocity > 0 ? count : -count) * 2;
894         // Calculate the distance to be travelled
895         qreal v2 = velocity*velocity;
896         qreal accel = deceleration/10;
897         // + 0.25 to encourage moving at least one item in the flick direction
898         qreal dist = qMin(qreal(modelCount-1), qreal(v2 / (accel * 2.0) + 0.25));
899         if (haveHighlightRange && highlightRangeMode == QSGPathView::StrictlyEnforceRange) {
900             // round to nearest item.
901             if (velocity > 0.)
902                 dist = qRound(dist + offset) - offset;
903             else
904                 dist = qRound(dist - offset) + offset;
905             // Calculate accel required to stop on item boundary
906             if (dist <= 0.) {
907                 dist = 0.;
908                 accel = 0.;
909             } else {
910                 accel = v2 / (2.0f * qAbs(dist));
911             }
912         }
913         offsetAdj = 0.0;
914         moveOffset.setValue(offset);
915         tl.accel(moveOffset, velocity, accel, dist);
916         tl.callback(QDeclarativeTimeLineCallback(&moveOffset, fixOffsetCallback, this));
917         if (!flicking) {
918             flicking = true;
919             emit q->flickingChanged();
920             emit q->flickStarted();
921         }
922     } else {
923         fixOffset();
924     }
925
926     lastPosTime.invalidate();
927     if (!tl.isActive())
928         q->movementEnding();
929 }
930
931 bool QSGPathView::sendMouseEvent(QGraphicsSceneMouseEvent *event)
932 {
933     Q_D(QSGPathView);
934     QGraphicsSceneMouseEvent mouseEvent(event->type());
935     QRectF myRect = mapRectToScene(QRectF(0, 0, width(), height()));
936     QSGCanvas *c = canvas();
937     QSGItem *grabber = c ? c->mouseGrabberItem() : 0;
938     bool stealThisEvent = d->stealMouse;
939     if ((stealThisEvent || myRect.contains(event->scenePos().toPoint())) && (!grabber || !grabber->keepMouseGrab())) {
940         mouseEvent.setAccepted(false);
941         for (int i = 0x1; i <= 0x10; i <<= 1) {
942             if (event->buttons() & i) {
943                 Qt::MouseButton button = Qt::MouseButton(i);
944                 mouseEvent.setButtonDownPos(button, mapFromScene(event->buttonDownPos(button)));
945             }
946         }
947         mouseEvent.setScenePos(event->scenePos());
948         mouseEvent.setLastScenePos(event->lastScenePos());
949         mouseEvent.setPos(mapFromScene(event->scenePos()));
950         mouseEvent.setLastPos(mapFromScene(event->lastScenePos()));
951
952         switch(mouseEvent.type()) {
953         case QEvent::GraphicsSceneMouseMove:
954             d->handleMouseMoveEvent(&mouseEvent);
955             break;
956         case QEvent::GraphicsSceneMousePress:
957             d->handleMousePressEvent(&mouseEvent);
958             stealThisEvent = d->stealMouse;   // Update stealThisEvent in case changed by function call above
959             break;
960         case QEvent::GraphicsSceneMouseRelease:
961             d->handleMouseReleaseEvent(&mouseEvent);
962             break;
963         default:
964             break;
965         }
966         grabber = c->mouseGrabberItem();
967         if (grabber && stealThisEvent && !grabber->keepMouseGrab() && grabber != this)
968             grabMouse();
969
970         return d->stealMouse;
971     } else if (d->lastPosTime.isValid()) {
972         d->lastPosTime.invalidate();
973     }
974     if (mouseEvent.type() == QEvent::GraphicsSceneMouseRelease)
975         d->stealMouse = false;
976     return false;
977 }
978
979 bool QSGPathView::childMouseEventFilter(QSGItem *i, QEvent *e)
980 {
981     Q_D(QSGPathView);
982     if (!isVisible() || !d->interactive)
983         return QSGItem::childMouseEventFilter(i, e);
984
985     switch (e->type()) {
986     case QEvent::GraphicsSceneMousePress:
987     case QEvent::GraphicsSceneMouseMove:
988     case QEvent::GraphicsSceneMouseRelease:
989         return sendMouseEvent(static_cast<QGraphicsSceneMouseEvent *>(e));
990     default:
991         break;
992     }
993
994     return QSGItem::childMouseEventFilter(i, e);
995 }
996
997 void QSGPathView::updatePolish()
998 {
999     QSGItem::updatePolish();
1000     refill();
1001 }
1002
1003 void QSGPathView::componentComplete()
1004 {
1005     Q_D(QSGPathView);
1006     QSGItem::componentComplete();
1007     d->createHighlight();
1008     // It is possible that a refill has already happended to to Path
1009     // bindings being handled in the componentComplete().  If so
1010     // don't do it again.
1011     if (d->items.count() == 0 && d->model) {
1012         d->modelCount = d->model->count();
1013         d->regenerate();
1014     }
1015     d->updateHighlight();
1016 }
1017
1018 void QSGPathView::refill()
1019 {
1020     Q_D(QSGPathView);
1021     if (!d->isValid() || !isComponentComplete())
1022         return;
1023
1024     d->layoutScheduled = false;
1025     bool currentVisible = false;
1026
1027     // first move existing items and remove items off path
1028     int idx = d->firstIndex;
1029     QList<QSGItem*>::iterator it = d->items.begin();
1030     while (it != d->items.end()) {
1031         qreal pos = d->positionOfIndex(idx);
1032         QSGItem *item = *it;
1033         if (pos >= 0.0) {
1034             d->updateItem(item, pos);
1035             if (idx == d->currentIndex) {
1036                 currentVisible = true;
1037                 d->currentItemOffset = pos;
1038             }
1039             ++it;
1040         } else {
1041 //            qDebug() << "release";
1042             d->updateItem(item, 1.0);
1043             d->releaseItem(item);
1044             if (it == d->items.begin()) {
1045                 if (++d->firstIndex >= d->modelCount)
1046                     d->firstIndex = 0;
1047             }
1048             it = d->items.erase(it);
1049         }
1050         ++idx;
1051         if (idx >= d->modelCount)
1052             idx = 0;
1053     }
1054     if (!d->items.count())
1055         d->firstIndex = -1;
1056
1057     if (d->modelCount) {
1058         // add items to beginning and end
1059         int count = d->pathItems == -1 ? d->modelCount : qMin(d->pathItems, d->modelCount);
1060         if (d->items.count() < count) {
1061             int idx = qRound(d->modelCount - d->offset) % d->modelCount;
1062             qreal startPos = 0.0;
1063             if (d->haveHighlightRange && d->highlightRangeMode != QSGPathView::NoHighlightRange)
1064                 startPos = d->highlightRangeStart;
1065             if (d->firstIndex >= 0) {
1066                 startPos = d->positionOfIndex(d->firstIndex);
1067                 idx = (d->firstIndex + d->items.count()) % d->modelCount;
1068             }
1069             qreal pos = d->positionOfIndex(idx);
1070             while ((pos > startPos || !d->items.count()) && d->items.count() < count) {
1071     //            qDebug() << "append" << idx;
1072                 QSGItem *item = d->getItem(idx);
1073                 if (d->model->completePending())
1074                     item->setZ(idx+1);
1075                 if (d->currentIndex == idx) {
1076                     item->setFocus(true);
1077                     if (QSGPathViewAttached *att = d->attached(item))
1078                         att->setIsCurrentItem(true);
1079                     currentVisible = true;
1080                     d->currentItemOffset = pos;
1081                     d->currentItem = item;
1082                 }
1083                 if (d->items.count() == 0)
1084                     d->firstIndex = idx;
1085                 d->items.append(item);
1086                 d->updateItem(item, pos);
1087                 if (d->model->completePending())
1088                     d->model->completeItem();
1089                 ++idx;
1090                 if (idx >= d->modelCount)
1091                     idx = 0;
1092                 pos = d->positionOfIndex(idx);
1093             }
1094
1095             idx = d->firstIndex - 1;
1096             if (idx < 0)
1097                 idx = d->modelCount - 1;
1098             pos = d->positionOfIndex(idx);
1099             while (pos >= 0.0 && pos < startPos) {
1100     //            qDebug() << "prepend" << idx;
1101                 QSGItem *item = d->getItem(idx);
1102                 if (d->model->completePending())
1103                     item->setZ(idx+1);
1104                 if (d->currentIndex == idx) {
1105                     item->setFocus(true);
1106                     if (QSGPathViewAttached *att = d->attached(item))
1107                         att->setIsCurrentItem(true);
1108                     currentVisible = true;
1109                     d->currentItemOffset = pos;
1110                     d->currentItem = item;
1111                 }
1112                 d->items.prepend(item);
1113                 d->updateItem(item, pos);
1114                 if (d->model->completePending())
1115                     d->model->completeItem();
1116                 d->firstIndex = idx;
1117                 idx = d->firstIndex - 1;
1118                 if (idx < 0)
1119                     idx = d->modelCount - 1;
1120                 pos = d->positionOfIndex(idx);
1121             }
1122         }
1123     }
1124
1125     if (!currentVisible)
1126         d->currentItemOffset = 1.0;
1127
1128     if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QSGPathView::StrictlyEnforceRange) {
1129         d->updateItem(d->highlightItem, d->highlightRangeStart);
1130         if (QSGPathViewAttached *att = d->attached(d->highlightItem))
1131             att->setOnPath(true);
1132     } else if (d->highlightItem && d->moveReason != QSGPathViewPrivate::SetIndex) {
1133         d->updateItem(d->highlightItem, d->currentItemOffset);
1134         if (QSGPathViewAttached *att = d->attached(d->highlightItem))
1135             att->setOnPath(currentVisible);
1136     }
1137     while (d->itemCache.count())
1138         d->releaseItem(d->itemCache.takeLast());
1139 }
1140
1141 void QSGPathView::itemsInserted(int modelIndex, int count)
1142 {
1143     //XXX support animated insertion
1144     Q_D(QSGPathView);
1145     if (!d->isValid() || !isComponentComplete())
1146         return;
1147
1148     if (d->modelCount) {
1149         d->itemCache += d->items;
1150         d->items.clear();
1151         if (modelIndex <= d->currentIndex) {
1152             d->currentIndex += count;
1153             emit currentIndexChanged();
1154         } else if (d->offset != 0) {
1155             d->offset += count;
1156             d->offsetAdj += count;
1157         }
1158     }
1159
1160     d->modelCount += count;
1161     if (d->flicking || d->moving) {
1162         d->regenerate();
1163         d->updateCurrent();
1164     } else {
1165         d->firstIndex = -1;
1166         d->updateMappedRange();
1167         d->scheduleLayout();
1168     }
1169     emit countChanged();
1170 }
1171
1172 void QSGPathView::itemsRemoved(int modelIndex, int count)
1173 {
1174     //XXX support animated removal
1175     Q_D(QSGPathView);
1176     if (!d->model || !d->modelCount || !d->model->isValid() || !d->path || !isComponentComplete())
1177         return;
1178
1179     // fix current
1180     bool currentChanged = false;
1181     if (d->currentIndex >= modelIndex + count) {
1182         d->currentIndex -= count;
1183         currentChanged = true;
1184     } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
1185         // current item has been removed.
1186         d->currentIndex = qMin(modelIndex, d->modelCount-count-1);
1187         if (d->currentItem) {
1188             if (QSGPathViewAttached *att = d->attached(d->currentItem))
1189                 att->setIsCurrentItem(true);
1190         }
1191         currentChanged = true;
1192     }
1193
1194     d->itemCache += d->items;
1195     d->items.clear();
1196
1197     bool changedOffset = false;
1198     if (modelIndex > d->currentIndex) {
1199         if (d->offset >= count) {
1200             changedOffset = true;
1201             d->offset -= count;
1202             d->offsetAdj -= count;
1203         }
1204     }
1205
1206     d->modelCount -= count;
1207     if (!d->modelCount) {
1208         while (d->itemCache.count())
1209             d->releaseItem(d->itemCache.takeLast());
1210         d->offset = 0;
1211         changedOffset = true;
1212         d->tl.reset(d->moveOffset);
1213     } else {
1214         d->regenerate();
1215         d->updateCurrent();
1216     }
1217     if (changedOffset)
1218         emit offsetChanged();
1219     if (currentChanged)
1220         emit currentIndexChanged();
1221     emit countChanged();
1222 }
1223
1224 void QSGPathView::itemsMoved(int /*from*/, int /*to*/, int /*count*/)
1225 {
1226     Q_D(QSGPathView);
1227     if (!d->isValid() || !isComponentComplete())
1228         return;
1229
1230     QList<QSGItem *> removedItems = d->items;
1231     d->items.clear();
1232     d->regenerate();
1233     while (removedItems.count())
1234         d->releaseItem(removedItems.takeLast());
1235
1236     // Fix current index
1237     if (d->currentIndex >= 0 && d->currentItem) {
1238         int oldCurrent = d->currentIndex;
1239         d->currentIndex = d->model->indexOf(d->currentItem, this);
1240         if (oldCurrent != d->currentIndex)
1241             emit currentIndexChanged();
1242     }
1243     d->updateCurrent();
1244 }
1245
1246 void QSGPathView::modelReset()
1247 {
1248     Q_D(QSGPathView);
1249     d->modelCount = d->model->count();
1250     d->regenerate();
1251     emit countChanged();
1252 }
1253
1254 void QSGPathView::createdItem(int index, QSGItem *item)
1255 {
1256     Q_D(QSGPathView);
1257     if (d->requestedIndex != index) {
1258         if (!d->attType) {
1259             // pre-create one metatype to share with all attached objects
1260             d->attType = new QDeclarativeOpenMetaObjectType(&QSGPathViewAttached::staticMetaObject, qmlEngine(this));
1261             foreach(const QString &attr, d->path->attributes())
1262                 d->attType->createProperty(attr.toUtf8());
1263         }
1264         qPathViewAttachedType = d->attType;
1265         QSGPathViewAttached *att = static_cast<QSGPathViewAttached *>(qmlAttachedPropertiesObject<QSGPathView>(item));
1266         qPathViewAttachedType = 0;
1267         if (att) {
1268             att->m_view = this;
1269             att->setOnPath(false);
1270         }
1271         item->setParentItem(this);
1272         d->updateItem(item, index < d->firstIndex ? 0.0 : 1.0);
1273     }
1274 }
1275
1276 void QSGPathView::destroyingItem(QSGItem *item)
1277 {
1278     Q_UNUSED(item);
1279 }
1280
1281 void QSGPathView::ticked()
1282 {
1283     Q_D(QSGPathView);
1284     d->updateCurrent();
1285 }
1286
1287 void QSGPathView::movementEnding()
1288 {
1289     Q_D(QSGPathView);
1290     if (d->flicking) {
1291         d->flicking = false;
1292         emit flickingChanged();
1293         emit flickEnded();
1294     }
1295     if (d->moving && !d->stealMouse) {
1296         d->moving = false;
1297         emit movingChanged();
1298         emit movementEnded();
1299     }
1300 }
1301
1302 // find the item closest to the snap position
1303 int QSGPathViewPrivate::calcCurrentIndex()
1304 {
1305     int current = -1;
1306     if (modelCount && model && items.count()) {
1307         offset = qmlMod(offset, modelCount);
1308         if (offset < 0)
1309             offset += modelCount;
1310         current = qRound(qAbs(qmlMod(modelCount - offset, modelCount)));
1311         current = current % modelCount;
1312     }
1313
1314     return current;
1315 }
1316
1317 void QSGPathViewPrivate::updateCurrent()
1318 {
1319     Q_Q(QSGPathView);
1320     if (moveReason != Mouse)
1321         return;
1322     if (!modelCount || !haveHighlightRange || highlightRangeMode != QSGPathView::StrictlyEnforceRange)
1323         return;
1324
1325     int idx = calcCurrentIndex();
1326     if (model && idx != currentIndex) {
1327         int itemIndex = (currentIndex - firstIndex + modelCount) % modelCount;
1328         if (itemIndex < items.count()) {
1329             if (QSGItem *item = items.at(itemIndex)) {
1330                 if (QSGPathViewAttached *att = attached(item))
1331                     att->setIsCurrentItem(false);
1332             }
1333         }
1334         currentIndex = idx;
1335         currentItem = 0;
1336         itemIndex = (idx - firstIndex + modelCount) % modelCount;
1337         if (itemIndex < items.count()) {
1338             currentItem = items.at(itemIndex);
1339             currentItem->setFocus(true);
1340             if (QSGPathViewAttached *att = attached(currentItem))
1341                 att->setIsCurrentItem(true);
1342         }
1343         emit q->currentIndexChanged();
1344     }
1345 }
1346
1347 void QSGPathViewPrivate::fixOffsetCallback(void *d)
1348 {
1349     ((QSGPathViewPrivate *)d)->fixOffset();
1350 }
1351
1352 void QSGPathViewPrivate::fixOffset()
1353 {
1354     Q_Q(QSGPathView);
1355     if (model && items.count()) {
1356         if (haveHighlightRange && highlightRangeMode == QSGPathView::StrictlyEnforceRange) {
1357             int curr = calcCurrentIndex();
1358             if (curr != currentIndex)
1359                 q->setCurrentIndex(curr);
1360             else
1361                 snapToCurrent();
1362         }
1363     }
1364 }
1365
1366 void QSGPathViewPrivate::snapToCurrent()
1367 {
1368     if (!model || modelCount <= 0)
1369         return;
1370
1371     qreal targetOffset = qmlMod(modelCount - currentIndex, modelCount);
1372
1373     moveReason = Other;
1374     offsetAdj = 0.0;
1375     tl.reset(moveOffset);
1376     moveOffset.setValue(offset);
1377
1378     const int duration = highlightMoveDuration;
1379
1380     if (moveDirection == Positive || (moveDirection == Shortest && targetOffset - offset > modelCount/2)) {
1381         qreal distance = modelCount - targetOffset + offset;
1382         if (targetOffset > moveOffset) {
1383             tl.move(moveOffset, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * offset / distance));
1384             tl.set(moveOffset, modelCount);
1385             tl.move(moveOffset, targetOffset, QEasingCurve(offset == 0.0 ? QEasingCurve::InOutQuad : QEasingCurve::OutQuad), int(duration * (modelCount-targetOffset) / distance));
1386         } else {
1387             tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1388         }
1389     } else if (moveDirection == Negative || targetOffset - offset <= -modelCount/2) {
1390         qreal distance = modelCount - offset + targetOffset;
1391         if (targetOffset < moveOffset) {
1392             tl.move(moveOffset, modelCount, QEasingCurve(targetOffset == 0 ? QEasingCurve::InOutQuad : QEasingCurve::InQuad), int(duration * (modelCount-offset) / distance));
1393             tl.set(moveOffset, 0.0);
1394             tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::OutQuad), int(duration * targetOffset / distance));
1395         } else {
1396             tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1397         }
1398     } else {
1399         tl.move(moveOffset, targetOffset, QEasingCurve(QEasingCurve::InOutQuad), duration);
1400     }
1401     moveDirection = Shortest;
1402 }
1403
1404 QSGPathViewAttached *QSGPathView::qmlAttachedProperties(QObject *obj)
1405 {
1406     return new QSGPathViewAttached(obj);
1407 }
1408
1409 QT_END_NAMESPACE
1410