c8e8817f952044a56bfc9b84b76407a6578dabb0
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsglistview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsglistview_p.h"
43 #include "qsgitemview_p_p.h"
44 #include "qsgvisualitemmodel_p.h"
45
46 #include <QtDeclarative/qdeclarativeexpression.h>
47 #include <QtDeclarative/qdeclarativeengine.h>
48 #include <QtDeclarative/qdeclarativeinfo.h>
49 #include <QtGui/qevent.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qcoreapplication.h>
52
53 #include <private/qdeclarativesmoothedanimation_p_p.h>
54 #include <private/qlistmodelinterface_p.h>
55
56 QT_BEGIN_NAMESPACE
57
58 void QSGViewSection::setProperty(const QString &property)
59 {
60     if (property != m_property) {
61         m_property = property;
62         emit propertyChanged();
63     }
64 }
65
66 void QSGViewSection::setCriteria(QSGViewSection::SectionCriteria criteria)
67 {
68     if (criteria != m_criteria) {
69         m_criteria = criteria;
70         emit criteriaChanged();
71     }
72 }
73
74 void QSGViewSection::setDelegate(QDeclarativeComponent *delegate)
75 {
76     if (delegate != m_delegate) {
77         m_delegate = delegate;
78         emit delegateChanged();
79     }
80 }
81
82 QString QSGViewSection::sectionString(const QString &value)
83 {
84     if (m_criteria == FirstCharacter)
85         return value.isEmpty() ? QString() : value.at(0);
86     else
87         return value;
88 }
89
90 //----------------------------------------------------------------------------
91
92 class FxListItemSG : public FxViewItem
93 {
94 public:
95     FxListItemSG(QSGItem *i, QSGListView *v, bool own) : FxViewItem(i, own), section(0), view(v) {
96         attached = static_cast<QSGListViewAttached*>(qmlAttachedPropertiesObject<QSGListView>(item));
97         if (attached)
98             static_cast<QSGListViewAttached*>(attached)->setView(view);
99     }
100
101     ~FxListItemSG() {}
102
103     qreal position() const {
104         if (section) {
105             if (view->orientation() == QSGListView::Vertical)
106                 return section->y();
107             else
108                 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x());
109         } else {
110             return itemPosition();
111         }
112     }
113     qreal itemPosition() const {
114         if (view->orientation() == QSGListView::Vertical)
115             return item->y();
116         else
117             return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x());
118     }
119     qreal size() const {
120         if (section)
121             return (view->orientation() == QSGListView::Vertical ? item->height()+section->height() : item->width()+section->width());
122         else
123             return (view->orientation() == QSGListView::Vertical ? item->height() : item->width());
124     }
125     qreal itemSize() const {
126         return (view->orientation() == QSGListView::Vertical ? item->height() : item->width());
127     }
128     qreal sectionSize() const {
129         if (section)
130             return (view->orientation() == QSGListView::Vertical ? section->height() : section->width());
131         return 0.0;
132     }
133     qreal endPosition() const {
134         if (view->orientation() == QSGListView::Vertical) {
135             return item->y() + item->height();
136         } else {
137             return (view->effectiveLayoutDirection() == Qt::RightToLeft
138                     ? -item->x()
139                     : item->x() + item->width());
140         }
141     }
142     void setPosition(qreal pos) {
143         if (view->orientation() == QSGListView::Vertical) {
144             if (section) {
145                 section->setY(pos);
146                 pos += section->height();
147             }
148             item->setY(pos);
149         } else {
150             if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
151                 if (section) {
152                     section->setX(-section->width()-pos);
153                     pos += section->width();
154                 }
155                 item->setX(-item->width()-pos);
156             } else {
157                 if (section) {
158                     section->setX(pos);
159                     pos += section->width();
160                 }
161                 item->setX(pos);
162             }
163         }
164     }
165     void setSize(qreal size) {
166         if (view->orientation() == QSGListView::Vertical)
167             item->setHeight(size);
168         else
169             item->setWidth(size);
170     }
171     bool contains(qreal x, qreal y) const {
172         return (x >= item->x() && x < item->x() + item->width() &&
173                 y >= item->y() && y < item->y() + item->height());
174     }
175
176     QSGItem *section;
177     QSGListView *view;
178 };
179
180 //----------------------------------------------------------------------------
181
182 class QSGListViewPrivate : public QSGItemViewPrivate
183 {
184     Q_DECLARE_PUBLIC(QSGListView)
185 public:
186     virtual Qt::Orientation layoutOrientation() const;
187     virtual bool isContentFlowReversed() const;
188     bool isRightToLeft() const;
189
190     virtual qreal positionAt(int index) const;
191     virtual qreal endPositionAt(int index) const;
192     virtual qreal originPosition() const;
193     virtual qreal lastPosition() const;
194
195     FxViewItem *nextVisibleItem() const;
196     FxViewItem *itemBefore(int modelIndex) const;
197     QString sectionAt(int modelIndex);
198     qreal snapPosAt(qreal pos);
199     FxViewItem *snapItemAt(qreal pos);
200
201     virtual void init();
202     virtual void clear();
203
204     virtual bool addVisibleItems(int fillFrom, int fillTo, bool doBuffer);
205     virtual bool removeNonVisibleItems(int bufferFrom, int bufferTo);
206     virtual void visibleItemsChanged();
207
208     virtual FxViewItem *newViewItem(int index, QSGItem *item);
209     virtual void initializeViewItem(FxViewItem *item);
210     virtual void releaseItem(FxViewItem *item);
211     virtual void repositionPackageItemAt(QSGItem *item, int index);
212
213     virtual void createHighlight();
214     virtual void updateHighlight();
215     virtual void resetHighlightPosition();
216
217     virtual void setPosition(qreal pos);
218     virtual void layoutVisibleItems();
219
220     virtual void updateSections();
221     void createSection(FxListItemSG *);
222     void updateCurrentSection();
223
224     virtual qreal headerSize() const;
225     virtual qreal footerSize() const;
226     virtual void updateHeader();
227     virtual void updateFooter();
228
229     virtual void changedVisibleIndex(int newIndex);
230     virtual void initializeCurrentItem();
231
232     void updateAverage();
233
234     void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
235     virtual void fixupPosition();
236     virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
237     virtual void flick(QSGItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
238                         QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
239
240     QSGListView::Orientation orient;
241     qreal visiblePos;
242     qreal averageSize;
243     qreal spacing;
244     QSGListView::SnapMode snapMode;
245
246     QSmoothedAnimation *highlightPosAnimator;
247     QSmoothedAnimation *highlightSizeAnimator;
248     qreal highlightMoveSpeed;
249     qreal highlightResizeSpeed;
250     int highlightResizeDuration;
251
252     QSGViewSection *sectionCriteria;
253     QString currentSection;
254     static const int sectionCacheSize = 4;
255     QSGItem *sectionCache[sectionCacheSize];
256
257     qreal overshootDist;
258     bool correctFlick : 1;
259     bool inFlickCorrection : 1;
260
261     QSGListViewPrivate()
262         : orient(QSGListView::Vertical)
263         , visiblePos(0)
264         , averageSize(100.0), spacing(0.0)
265         , snapMode(QSGListView::NoSnap)
266         , highlightPosAnimator(0), highlightSizeAnimator(0)
267         , highlightMoveSpeed(400), highlightResizeSpeed(400), highlightResizeDuration(-1)
268         , sectionCriteria(0)
269         , overshootDist(0.0), correctFlick(false), inFlickCorrection(false)
270     {}
271 };
272
273 bool QSGListViewPrivate::isContentFlowReversed() const
274 {
275     return isRightToLeft();
276 }
277
278 Qt::Orientation QSGListViewPrivate::layoutOrientation() const
279 {
280     return static_cast<Qt::Orientation>(orient);
281 }
282
283 bool QSGListViewPrivate::isRightToLeft() const
284 {
285     Q_Q(const QSGListView);
286     return orient == QSGListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
287 }
288
289 FxViewItem *QSGListViewPrivate::nextVisibleItem() const
290 {
291     const qreal pos = isRightToLeft() ? -position()-size() : position();
292     bool foundFirst = false;
293     for (int i = 0; i < visibleItems.count(); ++i) {
294         FxViewItem *item = visibleItems.at(i);
295         if (item->index != -1) {
296             if (foundFirst)
297                 return item;
298             else if (item->position() < pos && item->endPosition() >= pos)
299                 foundFirst = true;
300         }
301     }
302     return 0;
303 }
304
305 // Returns the item before modelIndex, if created.
306 // May return an item marked for removal.
307 FxViewItem *QSGListViewPrivate::itemBefore(int modelIndex) const
308 {
309     if (modelIndex < visibleIndex)
310         return 0;
311     int idx = 1;
312     int lastIndex = -1;
313     while (idx < visibleItems.count()) {
314         FxViewItem *item = visibleItems.at(idx);
315         if (item->index != -1)
316             lastIndex = item->index;
317         if (item->index == modelIndex)
318             return visibleItems.at(idx-1);
319         ++idx;
320     }
321     if (lastIndex == modelIndex-1)
322         return visibleItems.last();
323     return 0;
324 }
325
326 void QSGListViewPrivate::setPosition(qreal pos)
327 {
328     Q_Q(QSGListView);
329     if (orient == QSGListView::Vertical) {
330         q->QSGFlickable::setContentY(pos);
331     } else {
332         if (isRightToLeft())
333             q->QSGFlickable::setContentX(-pos-size());
334         else
335             q->QSGFlickable::setContentX(pos);
336     }
337 }
338
339 qreal QSGListViewPrivate::originPosition() const
340 {
341     qreal pos = 0;
342     if (!visibleItems.isEmpty()) {
343         pos = (*visibleItems.constBegin())->position();
344         if (visibleIndex > 0)
345             pos -= visibleIndex * (averageSize + spacing);
346     }
347     return pos;
348 }
349
350 qreal QSGListViewPrivate::lastPosition() const
351 {
352     qreal pos = 0;
353     if (!visibleItems.isEmpty()) {
354         int invisibleCount = visibleItems.count() - visibleIndex;
355         for (int i = visibleItems.count()-1; i >= 0; --i) {
356             if (visibleItems.at(i)->index != -1) {
357                 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
358                 break;
359             }
360         }
361         pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
362     } else if (model && model->count()) {
363         pos = (model->count() * averageSize + (model->count()-1) * spacing);
364     }
365     return pos;
366 }
367
368 qreal QSGListViewPrivate::positionAt(int modelIndex) const
369 {
370     if (FxViewItem *item = visibleItem(modelIndex))
371         return item->position();
372     if (!visibleItems.isEmpty()) {
373         if (modelIndex < visibleIndex) {
374             int count = visibleIndex - modelIndex;
375             qreal cs = 0;
376             if (modelIndex == currentIndex && currentItem) {
377                 cs = currentItem->size() + spacing;
378                 --count;
379             }
380             return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
381         } else {
382             int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
383             return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing);
384         }
385     }
386     return 0;
387 }
388
389 qreal QSGListViewPrivate::endPositionAt(int modelIndex) const
390 {
391     if (FxViewItem *item = visibleItem(modelIndex))
392         return item->endPosition();
393     if (!visibleItems.isEmpty()) {
394         if (modelIndex < visibleIndex) {
395             int count = visibleIndex - modelIndex;
396             return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing;
397         } else {
398             int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
399             return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
400         }
401     }
402     return 0;
403 }
404
405 QString QSGListViewPrivate::sectionAt(int modelIndex)
406 {
407     if (FxViewItem *item = visibleItem(modelIndex))
408         return item->attached->section();
409
410     QString section;
411     if (sectionCriteria) {
412         QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
413         section = sectionCriteria->sectionString(propValue);
414     }
415
416     return section;
417 }
418
419 qreal QSGListViewPrivate::snapPosAt(qreal pos)
420 {
421     if (FxViewItem *snapItem = snapItemAt(pos))
422         return snapItem->position();
423     if (visibleItems.count()) {
424         qreal firstPos = (*visibleItems.constBegin())->position();
425         qreal endPos = (*(--visibleItems.constEnd()))->position();
426         if (pos < firstPos) {
427             return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
428         } else if (pos > endPos)
429             return endPos + qRound((pos - endPos) / averageSize) * averageSize;
430     }
431     return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
432 }
433
434 FxViewItem *QSGListViewPrivate::snapItemAt(qreal pos)
435 {
436     FxViewItem *snapItem = 0;
437     qreal prevItemSize = 0;
438     for (int i = 0; i < visibleItems.count(); ++i) {
439         FxViewItem *item = visibleItems.at(i);
440         if (item->index == -1)
441             continue;
442         qreal itemTop = item->position();
443         if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
444             return item;
445         if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
446             snapItem = item;
447         prevItemSize = item->size();
448     }
449     return snapItem;
450 }
451
452 void QSGListViewPrivate::changedVisibleIndex(int newIndex)
453 {
454     visiblePos = positionAt(newIndex);
455     visibleIndex = newIndex;
456 }
457
458 void QSGListViewPrivate::init()
459 {
460     QSGItemViewPrivate::init();
461     ::memset(sectionCache, 0, sizeof(QSGItem*) * sectionCacheSize);
462 }
463
464 void QSGListViewPrivate::clear()
465 {
466     for (int i = 0; i < sectionCacheSize; ++i) {
467         delete sectionCache[i];
468         sectionCache[i] = 0;
469     }
470     visiblePos = 0;
471     QSGItemViewPrivate::clear();
472 }
473
474 FxViewItem *QSGListViewPrivate::newViewItem(int modelIndex, QSGItem *item)
475 {
476     Q_Q(QSGListView);
477
478     FxListItemSG *listItem = new FxListItemSG(item, q, false);
479     listItem->index = modelIndex;
480
481     // initialise attached properties
482     if (sectionCriteria) {
483         QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
484         listItem->attached->m_section = sectionCriteria->sectionString(propValue);
485         if (modelIndex > 0) {
486             if (FxViewItem *item = itemBefore(modelIndex))
487                 listItem->attached->m_prevSection = item->attached->section();
488             else
489                 listItem->attached->m_prevSection = sectionAt(modelIndex-1);
490         }
491         if (modelIndex < model->count()-1) {
492             if (FxViewItem *item = visibleItem(modelIndex+1))
493                 listItem->attached->m_nextSection = static_cast<QSGListViewAttached*>(item->attached)->section();
494             else
495                 listItem->attached->m_nextSection = sectionAt(modelIndex+1);
496         }
497     }
498
499     return listItem;
500 }
501
502 void QSGListViewPrivate::initializeViewItem(FxViewItem *item)
503 {
504     QSGItemViewPrivate::initializeViewItem(item);
505
506     QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item->item);
507     itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
508
509     if (sectionCriteria && sectionCriteria->delegate()) {
510         if (item->attached->m_prevSection != item->attached->m_section)
511             createSection(static_cast<FxListItemSG*>(item));
512     }
513 }
514
515 void QSGListViewPrivate::releaseItem(FxViewItem *item)
516 {
517     if (item) {
518         FxListItemSG* listItem = static_cast<FxListItemSG*>(item);
519         if (listItem->section) {
520             int i = 0;
521             do {
522                 if (!sectionCache[i]) {
523                     sectionCache[i] = listItem->section;
524                     sectionCache[i]->setVisible(false);
525                     listItem->section = 0;
526                     break;
527                 }
528                 ++i;
529             } while (i < sectionCacheSize);
530             delete listItem->section;
531         }
532     }
533     QSGItemViewPrivate::releaseItem(item);
534 }
535
536 bool QSGListViewPrivate::addVisibleItems(int fillFrom, int fillTo, bool doBuffer)
537 {
538     qreal itemEnd = visiblePos;
539     if (visibleItems.count()) {
540         visiblePos = (*visibleItems.constBegin())->position();
541         itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
542     }
543
544     int modelIndex = findLastVisibleIndex();
545     bool haveValidItems = modelIndex >= 0;
546     modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
547
548     if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing
549         || fillTo < visiblePos - averageSize - spacing)) {
550         // We've jumped more than a page.  Estimate which items are now
551         // visible and fill from there.
552         int count = (fillFrom - itemEnd) / (averageSize + spacing);
553         for (int i = 0; i < visibleItems.count(); ++i)
554             releaseItem(visibleItems.at(i));
555         visibleItems.clear();
556         modelIndex += count;
557         if (modelIndex >= model->count()) {
558             count -= modelIndex - model->count() + 1;
559             modelIndex = model->count() - 1;
560         } else if (modelIndex < 0) {
561             count -= modelIndex;
562             modelIndex = 0;
563         }
564         visibleIndex = modelIndex;
565         visiblePos = itemEnd + count * (averageSize + spacing);
566         itemEnd = visiblePos;
567     }
568
569     bool changed = false;
570     FxListItemSG *item = 0;
571     qreal pos = itemEnd;
572     while (modelIndex < model->count() && pos <= fillTo) {
573 //        qDebug() << "refill: append item" << modelIndex << "pos" << pos;
574         if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex))))
575             break;
576         item->setPosition(pos);
577         pos += item->size() + spacing;
578         visibleItems.append(item);
579         ++modelIndex;
580         changed = true;
581         if (doBuffer) // never buffer more than one item per frame
582             break;
583     }
584     while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos >= fillFrom) {
585 //        qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos;
586         if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1))))
587             break;
588         --visibleIndex;
589         visiblePos -= item->size() + spacing;
590         item->setPosition(visiblePos);
591         visibleItems.prepend(item);
592         changed = true;
593         if (doBuffer) // never buffer more than one item per frame
594             break;
595     }
596
597     return changed;
598 }
599
600 bool QSGListViewPrivate::removeNonVisibleItems(int bufferFrom, int bufferTo)
601 {
602     FxViewItem *item = 0;
603     bool changed = false;
604
605     while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() <= bufferFrom) {
606         if (item->attached->delayRemove())
607             break;
608         if (item->size() == 0)
609             break;
610 //            qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
611         if (item->index != -1)
612             visibleIndex++;
613         visibleItems.removeFirst();
614         releaseItem(item);
615         changed = true;
616     }
617     while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
618         if (item->attached->delayRemove())
619             break;
620 //            qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
621         visibleItems.removeLast();
622         releaseItem(item);
623         changed = true;
624     }
625
626     return changed;
627 }
628
629 void QSGListViewPrivate::visibleItemsChanged()
630 {
631     if (visibleItems.count())
632         visiblePos = (*visibleItems.constBegin())->position();
633     updateAverage();
634     if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
635         static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
636         updateHighlight();
637     }
638     if (sectionCriteria)
639         updateCurrentSection();
640     updateHeader();
641     updateFooter();
642     updateViewport();
643     updateUnrequestedPositions();
644 }
645
646 void QSGListViewPrivate::layoutVisibleItems()
647 {
648     if (!visibleItems.isEmpty()) {
649         bool fixedCurrent = currentItem && (*visibleItems.constBegin())->item == currentItem->item;
650         qreal sum = (*visibleItems.constBegin())->size();
651         qreal pos = (*visibleItems.constBegin())->position() + (*visibleItems.constBegin())->size() + spacing;
652         for (int i=1; i < visibleItems.count(); ++i) {
653             FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
654             item->setPosition(pos);
655             pos += item->size() + spacing;
656             sum += item->size();
657             fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
658         }
659         averageSize = qRound(sum / visibleItems.count());
660
661         // move current item if it is not a visible item.
662         if (currentIndex >= 0 && currentItem && !fixedCurrent) {
663             static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
664         }
665     }
666 }
667
668 void QSGListViewPrivate::repositionPackageItemAt(QSGItem *item, int index)
669 {
670     Q_Q(QSGListView);
671     qreal pos = position();
672     if (orient == QSGListView::Vertical) {
673         if (item->y() + item->height() > pos && item->y() < pos + q->height())
674             item->setY(positionAt(index));
675     } else {
676         if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
677             if (isRightToLeft())
678                 item->setX(-positionAt(index)-item->width());
679             else
680                 item->setX(positionAt(index));
681         }
682     }
683 }
684
685 void QSGListViewPrivate::createHighlight()
686 {
687     Q_Q(QSGListView);
688     bool changed = false;
689     if (highlight) {
690         if (trackedItem == highlight)
691             trackedItem = 0;
692         delete highlight;
693         highlight = 0;
694
695         delete highlightPosAnimator;
696         delete highlightSizeAnimator;
697         highlightPosAnimator = 0;
698         highlightSizeAnimator = 0;
699
700         changed = true;
701     }
702
703     if (currentItem) {
704         QSGItem *item = createHighlightItem();
705         if (item) {
706             FxListItemSG *newHighlight = new FxListItemSG(item, q, true);
707
708             if (autoHighlight) {
709                 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
710                 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
711             }
712             const QLatin1String posProp(orient == QSGListView::Vertical ? "y" : "x");
713             highlightPosAnimator = new QSmoothedAnimation(q);
714             highlightPosAnimator->target = QDeclarativeProperty(item, posProp);
715             highlightPosAnimator->velocity = highlightMoveSpeed;
716             highlightPosAnimator->userDuration = highlightMoveDuration;
717
718             const QLatin1String sizeProp(orient == QSGListView::Vertical ? "height" : "width");
719             highlightSizeAnimator = new QSmoothedAnimation(q);
720             highlightSizeAnimator->velocity = highlightResizeSpeed;
721             highlightSizeAnimator->userDuration = highlightResizeDuration;
722             highlightSizeAnimator->target = QDeclarativeProperty(item, sizeProp);
723
724             highlight = newHighlight;
725             changed = true;
726         }
727     }
728     if (changed)
729         emit q->highlightItemChanged();
730 }
731
732 void QSGListViewPrivate::updateHighlight()
733 {
734     if ((!currentItem && highlight) || (currentItem && !highlight))
735         createHighlight();
736     bool strictHighlight = haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange;
737     if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
738         // auto-update highlight
739         FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
740         highlightPosAnimator->to = isRightToLeft()
741                 ? -listItem->itemPosition()-listItem->itemSize()
742                 : listItem->itemPosition();
743         highlightSizeAnimator->to = listItem->itemSize();
744         if (orient == QSGListView::Vertical) {
745             if (highlight->item->width() == 0)
746                 highlight->item->setWidth(currentItem->item->width());
747         } else {
748             if (highlight->item->height() == 0)
749                 highlight->item->setHeight(currentItem->item->height());
750         }
751
752         highlightPosAnimator->restart();
753         highlightSizeAnimator->restart();
754     }
755     updateTrackedItem();
756 }
757
758 void QSGListViewPrivate::resetHighlightPosition()
759 {
760     if (highlight && currentItem)
761         static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
762 }
763
764 void QSGListViewPrivate::createSection(FxListItemSG *listItem)
765 {
766     Q_Q(QSGListView);
767     if (!sectionCriteria || !sectionCriteria->delegate())
768         return;
769     if (listItem->attached->m_prevSection != listItem->attached->m_section) {
770         if (!listItem->section) {
771             qreal pos = listItem->position();
772             int i = sectionCacheSize-1;
773             while (i >= 0 && !sectionCache[i])
774                 --i;
775             if (i >= 0) {
776                 listItem->section = sectionCache[i];
777                 sectionCache[i] = 0;
778                 listItem->section->setVisible(true);
779                 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
780                 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
781             } else {
782                 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
783                 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
784                 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
785                 if (nobj) {
786                     QDeclarative_setParent_noEvent(context, nobj);
787                     listItem->section = qobject_cast<QSGItem *>(nobj);
788                     if (!listItem->section) {
789                         delete nobj;
790                     } else {
791                         listItem->section->setZ(1);
792                         QDeclarative_setParent_noEvent(listItem->section, q->contentItem());
793                         listItem->section->setParentItem(q->contentItem());
794                     }
795                 } else {
796                     delete context;
797                 }
798                 sectionCriteria->delegate()->completeCreate();
799             }
800             listItem->setPosition(pos);
801         } else {
802             QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
803             context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
804         }
805     } else if (listItem->section) {
806         qreal pos = listItem->position();
807         int i = 0;
808         do {
809             if (!sectionCache[i]) {
810                 sectionCache[i] = listItem->section;
811                 sectionCache[i]->setVisible(false);
812                 listItem->section = 0;
813                 return;
814             }
815             ++i;
816         } while (i < sectionCacheSize);
817         delete listItem->section;
818         listItem->section = 0;
819         listItem->setPosition(pos);
820     }
821 }
822
823 void QSGListViewPrivate::updateSections()
824 {
825     QSGItemViewPrivate::updateSections();
826
827     if (sectionCriteria && !visibleItems.isEmpty()) {
828         QString prevSection;
829         if (visibleIndex > 0)
830             prevSection = sectionAt(visibleIndex-1);
831         QSGListViewAttached *prevAtt = 0;
832         int idx = -1;
833         for (int i = 0; i < visibleItems.count(); ++i) {
834             QSGListViewAttached *attached = static_cast<QSGListViewAttached*>(visibleItems.at(i)->attached);
835             attached->setPrevSection(prevSection);
836             if (visibleItems.at(i)->index != -1) {
837                 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
838                 attached->setSection(sectionCriteria->sectionString(propValue));
839                 idx = visibleItems.at(i)->index;
840             }
841             createSection(static_cast<FxListItemSG*>(visibleItems.at(i)));
842             if (prevAtt)
843                 prevAtt->setNextSection(attached->section());
844             prevSection = attached->section();
845             prevAtt = attached;
846         }
847         if (prevAtt) {
848             if (idx > 0 && idx < model->count()-1)
849                 prevAtt->setNextSection(sectionAt(idx+1));
850             else
851                 prevAtt->setNextSection(QString());
852         }
853     }
854 }
855
856 void QSGListViewPrivate::updateCurrentSection()
857 {
858     Q_Q(QSGListView);
859     if (!sectionCriteria || visibleItems.isEmpty()) {
860         if (!currentSection.isEmpty()) {
861             currentSection.clear();
862             emit q->currentSectionChanged();
863         }
864         return;
865     }
866     int index = 0;
867     while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= position())
868         ++index;
869
870     QString newSection = currentSection;
871     if (index < visibleItems.count())
872         newSection = visibleItems.at(index)->attached->section();
873     else
874         newSection = (*visibleItems.constBegin())->attached->section();
875     if (newSection != currentSection) {
876         currentSection = newSection;
877         emit q->currentSectionChanged();
878     }
879 }
880
881 void QSGListViewPrivate::initializeCurrentItem()
882 {
883     QSGItemViewPrivate::initializeCurrentItem();
884
885     if (currentItem) {
886         FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
887
888         if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
889             // We can calculate exact postion in this case
890             listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
891         } else {
892             // Create current item now and position as best we can.
893             // Its position will be corrected when it becomes visible.
894             listItem->setPosition(positionAt(currentIndex));
895         }
896
897         // Avoid showing section delegate twice.  We still need the section heading so that
898         // currentItem positioning works correctly.
899         // This is slightly sub-optimal, but section heading caching minimizes the impact.
900         if (listItem->section)
901             listItem->section->setVisible(false);
902
903         if (visibleItems.isEmpty())
904             averageSize = listItem->size();
905     }
906 }
907
908 void QSGListViewPrivate::updateAverage()
909 {
910     if (!visibleItems.count())
911         return;
912     qreal sum = 0.0;
913     for (int i = 0; i < visibleItems.count(); ++i)
914         sum += visibleItems.at(i)->size();
915     averageSize = qRound(sum / visibleItems.count());
916 }
917
918 qreal QSGListViewPrivate::headerSize() const
919 {
920     return header ? header->size() : 0.0;
921 }
922
923 qreal QSGListViewPrivate::footerSize() const
924 {
925     return footer ? footer->size() : 0.0;
926 }
927
928 void QSGListViewPrivate::updateFooter()
929 {
930     Q_Q(QSGListView);
931     bool created = false;
932     if (!footer) {
933         QSGItem *item = createComponentItem(footerComponent, true);
934         if (!item)
935             return;
936         item->setZ(1);
937         footer = new FxListItemSG(item, q, true);
938         created = true;
939     }
940
941     FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
942     if (visibleItems.count()) {
943         qreal endPos = lastPosition();
944         if (findLastVisibleIndex() == model->count()-1) {
945             listItem->setPosition(endPos);
946         } else {
947             qreal visiblePos = position() + q->height();
948             if (endPos <= visiblePos || listItem->position() < endPos)
949                 listItem->setPosition(endPos);
950         }
951     } else {
952         listItem->setPosition(visiblePos);
953     }
954
955     if (created)
956         emit q->footerItemChanged();
957 }
958
959 void QSGListViewPrivate::updateHeader()
960 {
961     Q_Q(QSGListView);
962     bool created = false;
963     if (!header) {
964         QSGItem *item = createComponentItem(headerComponent, true);
965         if (!item)
966             return;
967         item->setZ(1);
968         header = new FxListItemSG(item, q, true);
969         created = true;
970     }
971
972     FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
973     if (listItem) {
974         if (visibleItems.count()) {
975             qreal startPos = originPosition();
976             if (visibleIndex == 0) {
977                 listItem->setPosition(startPos - headerSize());
978             } else {
979                 if (position() <= startPos || listItem->position() > startPos - headerSize())
980                     listItem->setPosition(startPos - headerSize());
981             }
982         } else {
983             listItem->setPosition(-headerSize());
984         }
985     }
986
987     if (created)
988         emit q->headerItemChanged();
989 }
990
991 void QSGListViewPrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
992 {
993     Q_Q(QSGListView);
994     QSGItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
995     if (!q->isComponentComplete())
996         return;
997     if (item != contentItem && (!highlight || item != highlight->item)) {
998         if ((orient == QSGListView::Vertical && newGeometry.height() != oldGeometry.height())
999             || (orient == QSGListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
1000             scheduleLayout();
1001         }
1002     }
1003 }
1004
1005 void QSGListViewPrivate::fixupPosition()
1006 {
1007     if ((haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange)
1008         || snapMode != QSGListView::NoSnap)
1009         moveReason = Other;
1010     if (orient == QSGListView::Vertical)
1011         fixupY();
1012     else
1013         fixupX();
1014 }
1015
1016 void QSGListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1017 {
1018     if ((orient == QSGListView::Horizontal && &data == &vData)
1019         || (orient == QSGListView::Vertical && &data == &hData))
1020         return;
1021
1022     correctFlick = false;
1023     fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1024
1025     qreal highlightStart;
1026     qreal highlightEnd;
1027     qreal viewPos;
1028     if (isRightToLeft()) {
1029         // Handle Right-To-Left exceptions
1030         viewPos = -position()-size();
1031         highlightStart = highlightRangeStartValid ? size() - highlightRangeEnd : highlightRangeStart;
1032         highlightEnd = highlightRangeEndValid ? size() - highlightRangeStart : highlightRangeEnd;
1033     } else {
1034         viewPos = position();
1035         highlightStart = highlightRangeStart;
1036         highlightEnd = highlightRangeEnd;
1037     }
1038
1039     if (currentItem && haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange
1040             && moveReason != QSGListViewPrivate::SetIndex) {
1041         updateHighlight();
1042         qreal pos = static_cast<FxListItemSG*>(currentItem)->itemPosition();
1043         if (viewPos < pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightEnd)
1044             viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightEnd;
1045         if (viewPos > pos - highlightStart)
1046             viewPos = pos - highlightStart;
1047         if (isRightToLeft())
1048             viewPos = -viewPos-size();
1049
1050         timeline.reset(data.move);
1051         if (viewPos != position()) {
1052             if (fixupMode != Immediate) {
1053                 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1054                 data.fixingUp = true;
1055             } else {
1056                 timeline.set(data.move, -viewPos);
1057             }
1058         }
1059         vTime = timeline.time();
1060     } else if (snapMode != QSGListView::NoSnap && moveReason != QSGListViewPrivate::SetIndex) {
1061         qreal tempPosition = isRightToLeft() ? -position()-size() : position();
1062         FxViewItem *topItem = snapItemAt(tempPosition+highlightStart);
1063         FxViewItem *bottomItem = snapItemAt(tempPosition+highlightEnd);
1064         qreal pos;
1065         bool isInBounds = -position() > maxExtent && -position() < minExtent;
1066         if (topItem && isInBounds) {
1067             if (topItem->index == 0 && header && tempPosition+highlightStart < header->position()+headerSize()/2) {
1068                 pos = isRightToLeft() ? - header->position() + highlightStart - size() : header->position() - highlightStart;
1069             } else {
1070                 if (isRightToLeft())
1071                     pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1072                 else
1073                     pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent);
1074             }
1075         } else if (bottomItem && isInBounds) {
1076             if (isRightToLeft())
1077                 pos = qMax(qMin(-bottomItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1078             else
1079                 pos = qMax(qMin(bottomItem->position() - highlightStart, -maxExtent), -minExtent);
1080         } else {
1081             QSGItemViewPrivate::fixup(data, minExtent, maxExtent);
1082             return;
1083         }
1084
1085         qreal dist = qAbs(data.move + pos);
1086         if (dist > 0) {
1087             timeline.reset(data.move);
1088             if (fixupMode != Immediate) {
1089                 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1090                 data.fixingUp = true;
1091             } else {
1092                 timeline.set(data.move, -pos);
1093             }
1094             vTime = timeline.time();
1095         }
1096     } else {
1097         QSGItemViewPrivate::fixup(data, minExtent, maxExtent);
1098     }
1099     data.inOvershoot = false;
1100     fixupMode = Normal;
1101 }
1102
1103 void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1104                                         QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
1105 {
1106     Q_Q(QSGListView);
1107
1108     data.fixingUp = false;
1109     moveReason = Mouse;
1110     if ((!haveHighlightRange || highlightRange != QSGListView::StrictlyEnforceRange) && snapMode == QSGListView::NoSnap) {
1111         correctFlick = true;
1112         QSGItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1113         return;
1114     }
1115     qreal maxDistance = 0;
1116     qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
1117     // -ve velocity means list is moving up/left
1118     if (velocity > 0) {
1119         if (data.move.value() < minExtent) {
1120             if (snapMode == QSGListView::SnapOneItem) {
1121                 if (FxViewItem *item = isRightToLeft() ? nextVisibleItem() : firstVisibleItem())
1122                     maxDistance = qAbs(item->position() + dataValue);
1123             } else {
1124                 maxDistance = qAbs(minExtent - data.move.value());
1125             }
1126         }
1127         if (snapMode == QSGListView::NoSnap && highlightRange != QSGListView::StrictlyEnforceRange)
1128             data.flickTarget = minExtent;
1129     } else {
1130         if (data.move.value() > maxExtent) {
1131             if (snapMode == QSGListView::SnapOneItem) {
1132                 if (FxViewItem *item = isRightToLeft() ? firstVisibleItem() : nextVisibleItem())
1133                     maxDistance = qAbs(item->position() + dataValue);
1134             } else {
1135                 maxDistance = qAbs(maxExtent - data.move.value());
1136             }
1137         }
1138         if (snapMode == QSGListView::NoSnap && highlightRange != QSGListView::StrictlyEnforceRange)
1139             data.flickTarget = maxExtent;
1140     }
1141     bool overShoot = boundsBehavior == QSGFlickable::DragAndOvershootBounds;
1142     qreal highlightStart = isRightToLeft() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
1143     if (maxDistance > 0 || overShoot) {
1144         // These modes require the list to stop exactly on an item boundary.
1145         // The initial flick will estimate the boundary to stop on.
1146         // Since list items can have variable sizes, the boundary will be
1147         // reevaluated and adjusted as we approach the boundary.
1148         qreal v = velocity;
1149         if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1150             if (v < 0)
1151                 v = -maxVelocity;
1152             else
1153                 v = maxVelocity;
1154         }
1155         if (!flickingHorizontally && !flickingVertically) {
1156             // the initial flick - estimate boundary
1157             qreal accel = deceleration;
1158             qreal v2 = v * v;
1159             overshootDist = 0.0;
1160             // + averageSize/4 to encourage moving at least one item in the flick direction
1161             qreal dist = v2 / (accel * 2.0) + averageSize/4;
1162             if (maxDistance > 0)
1163                 dist = qMin(dist, maxDistance);
1164             if (v > 0)
1165                 dist = -dist;
1166             if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QSGListView::SnapOneItem) {
1167                 qreal distTemp = isRightToLeft() ? -dist : dist;
1168                 data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart;
1169                 data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1170                 if (overShoot) {
1171                     if (data.flickTarget >= minExtent) {
1172                         overshootDist = overShootDistance(vSize);
1173                         data.flickTarget += overshootDist;
1174                     } else if (data.flickTarget <= maxExtent) {
1175                         overshootDist = overShootDistance(vSize);
1176                         data.flickTarget -= overshootDist;
1177                     }
1178                 }
1179                 qreal adjDist = -data.flickTarget + data.move.value();
1180                 if (qAbs(adjDist) > qAbs(dist)) {
1181                     // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1182                     qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1183                     if (adjv2 > v2) {
1184                         v2 = adjv2;
1185                         v = qSqrt(v2);
1186                         if (dist > 0)
1187                             v = -v;
1188                     }
1189                 }
1190                 dist = adjDist;
1191                 accel = v2 / (2.0f * qAbs(dist));
1192             } else if (overShoot) {
1193                 data.flickTarget = data.move.value() - dist;
1194                 if (data.flickTarget >= minExtent) {
1195                     overshootDist = overShootDistance(vSize);
1196                     data.flickTarget += overshootDist;
1197                 } else if (data.flickTarget <= maxExtent) {
1198                     overshootDist = overShootDistance(vSize);
1199                     data.flickTarget -= overshootDist;
1200                 }
1201             }
1202             timeline.reset(data.move);
1203             timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1204             timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1205             if (!flickingHorizontally && q->xflick()) {
1206                 flickingHorizontally = true;
1207                 emit q->flickingChanged();
1208                 emit q->flickingHorizontallyChanged();
1209                 emit q->flickStarted();
1210             }
1211             if (!flickingVertically && q->yflick()) {
1212                 flickingVertically = true;
1213                 emit q->flickingChanged();
1214                 emit q->flickingVerticallyChanged();
1215                 emit q->flickStarted();
1216             }
1217             correctFlick = true;
1218         } else {
1219             // reevaluate the target boundary.
1220             qreal newtarget = data.flickTarget;
1221             if (snapMode != QSGListView::NoSnap || highlightRange == QSGListView::StrictlyEnforceRange) {
1222                 qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1223                 newtarget = -snapPosAt(-(tempFlickTarget - highlightStart)) + highlightStart;
1224                 newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
1225             }
1226             if (velocity < 0 && newtarget <= maxExtent)
1227                 newtarget = maxExtent - overshootDist;
1228             else if (velocity > 0 && newtarget >= minExtent)
1229                 newtarget = minExtent + overshootDist;
1230             if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1231                 if (qAbs(velocity) < MinimumFlickVelocity)
1232                     correctFlick = false;
1233                 return;
1234             }
1235             data.flickTarget = newtarget;
1236             qreal dist = -newtarget + data.move.value();
1237             if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1238                 correctFlick = false;
1239                 timeline.reset(data.move);
1240                 fixup(data, minExtent, maxExtent);
1241                 return;
1242             }
1243             timeline.reset(data.move);
1244             timeline.accelDistance(data.move, v, -dist);
1245             timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1246         }
1247     } else {
1248         correctFlick = false;
1249         timeline.reset(data.move);
1250         fixup(data, minExtent, maxExtent);
1251     }
1252 }
1253
1254 //----------------------------------------------------------------------------
1255
1256 QSGListView::QSGListView(QSGItem *parent)
1257     : QSGItemView(*(new QSGListViewPrivate), parent)
1258 {
1259 }
1260
1261 QSGListView::~QSGListView()
1262 {
1263 }
1264
1265 void QSGListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1266 {
1267     Q_D(QSGListView);
1268     if (d->autoHighlight != autoHighlight) {
1269         if (!autoHighlight) {
1270             if (d->highlightPosAnimator)
1271                 d->highlightPosAnimator->stop();
1272             if (d->highlightSizeAnimator)
1273                 d->highlightSizeAnimator->stop();
1274         }
1275         QSGItemView::setHighlightFollowsCurrentItem(autoHighlight);
1276     }
1277 }
1278
1279 qreal QSGListView::spacing() const
1280 {
1281     Q_D(const QSGListView);
1282     return d->spacing;
1283 }
1284
1285 void QSGListView::setSpacing(qreal spacing)
1286 {
1287     Q_D(QSGListView);
1288     if (spacing != d->spacing) {
1289         d->spacing = spacing;
1290         d->layout();
1291         emit spacingChanged();
1292     }
1293 }
1294
1295 QSGListView::Orientation QSGListView::orientation() const
1296 {
1297     Q_D(const QSGListView);
1298     return d->orient;
1299 }
1300
1301 void QSGListView::setOrientation(QSGListView::Orientation orientation)
1302 {
1303     Q_D(QSGListView);
1304     if (d->orient != orientation) {
1305         d->orient = orientation;
1306         if (d->orient == Vertical) {
1307             setContentWidth(-1);
1308             setFlickableDirection(VerticalFlick);
1309             setContentX(0);
1310         } else {
1311             setContentHeight(-1);
1312             setFlickableDirection(HorizontalFlick);
1313             setContentY(0);
1314         }
1315         d->regenerate();
1316         emit orientationChanged();
1317     }
1318 }
1319
1320 QSGViewSection *QSGListView::sectionCriteria()
1321 {
1322     Q_D(QSGListView);
1323     if (!d->sectionCriteria) {
1324         d->sectionCriteria = new QSGViewSection(this);
1325         connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
1326     }
1327     return d->sectionCriteria;
1328 }
1329
1330 QString QSGListView::currentSection() const
1331 {
1332     Q_D(const QSGListView);
1333     return d->currentSection;
1334 }
1335
1336 qreal QSGListView::highlightMoveSpeed() const
1337 {
1338     Q_D(const QSGListView);
1339     return d->highlightMoveSpeed;
1340 }
1341
1342 void QSGListView::setHighlightMoveSpeed(qreal speed)
1343 {
1344     Q_D(QSGListView);
1345     if (d->highlightMoveSpeed != speed) {
1346         d->highlightMoveSpeed = speed;
1347         if (d->highlightPosAnimator)
1348             d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
1349         emit highlightMoveSpeedChanged();
1350     }
1351 }
1352
1353 void QSGListView::setHighlightMoveDuration(int duration)
1354 {
1355     Q_D(QSGListView);
1356     if (d->highlightMoveDuration != duration) {
1357         if (d->highlightPosAnimator)
1358             d->highlightPosAnimator->userDuration = duration;
1359         QSGItemView::setHighlightMoveDuration(duration);
1360     }
1361 }
1362
1363 qreal QSGListView::highlightResizeSpeed() const
1364 {
1365     Q_D(const QSGListView);
1366     return d->highlightResizeSpeed;
1367 }
1368
1369 void QSGListView::setHighlightResizeSpeed(qreal speed)
1370 {
1371     Q_D(QSGListView);
1372     if (d->highlightResizeSpeed != speed) {
1373         d->highlightResizeSpeed = speed;
1374         if (d->highlightSizeAnimator)
1375             d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
1376         emit highlightResizeSpeedChanged();
1377     }
1378 }
1379
1380 int QSGListView::highlightResizeDuration() const
1381 {
1382     Q_D(const QSGListView);
1383     return d->highlightResizeDuration;
1384 }
1385
1386 void QSGListView::setHighlightResizeDuration(int duration)
1387 {
1388     Q_D(QSGListView);
1389     if (d->highlightResizeDuration != duration) {
1390         d->highlightResizeDuration = duration;
1391         if (d->highlightSizeAnimator)
1392             d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
1393         emit highlightResizeDurationChanged();
1394     }
1395 }
1396
1397 QSGListView::SnapMode QSGListView::snapMode() const
1398 {
1399     Q_D(const QSGListView);
1400     return d->snapMode;
1401 }
1402
1403 void QSGListView::setSnapMode(SnapMode mode)
1404 {
1405     Q_D(QSGListView);
1406     if (d->snapMode != mode) {
1407         d->snapMode = mode;
1408         emit snapModeChanged();
1409     }
1410 }
1411
1412 void QSGListView::viewportMoved()
1413 {
1414     Q_D(QSGListView);
1415     QSGItemView::viewportMoved();
1416     if (!d->itemCount)
1417         return;
1418     // Recursion can occur due to refill changing the content size.
1419     if (d->inViewportMoved)
1420         return;
1421     d->inViewportMoved = true;
1422     d->lazyRelease = true;
1423     d->refill();
1424     if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically)
1425         d->moveReason = QSGListViewPrivate::Mouse;
1426     if (d->moveReason != QSGListViewPrivate::SetIndex) {
1427         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
1428             // reposition highlight
1429             qreal pos = d->highlight->position();
1430             qreal viewPos;
1431             qreal highlightStart;
1432             qreal highlightEnd;
1433             if (d->isRightToLeft()) {
1434                 // Handle Right-To-Left exceptions
1435                 viewPos = -d->position()-d->size();
1436                 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
1437                 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
1438             } else {
1439                 viewPos = d->position();
1440                 highlightStart = d->highlightRangeStart;
1441                 highlightEnd = d->highlightRangeEnd;
1442             }
1443             if (pos > viewPos + highlightEnd - d->highlight->size())
1444                 pos = viewPos + highlightEnd - d->highlight->size();
1445             if (pos < viewPos + highlightStart)
1446                 pos = viewPos + highlightStart;
1447             if (pos != d->highlight->position()) {
1448                 d->highlightPosAnimator->stop();
1449                 static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
1450             } else {
1451                 d->updateHighlight();
1452             }
1453
1454             // update current index
1455             if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
1456                 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
1457                     d->updateCurrent(snapItem->index);
1458             }
1459         }
1460     }
1461
1462     if ((d->flickingHorizontally || d->flickingVertically) && d->correctFlick && !d->inFlickCorrection) {
1463         d->inFlickCorrection = true;
1464         // Near an end and it seems that the extent has changed?
1465         // Recalculate the flick so that we don't end up in an odd position.
1466         if (yflick() && !d->vData.inOvershoot) {
1467             if (d->vData.velocity > 0) {
1468                 const qreal minY = minYExtent();
1469                 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
1470                     && minY != d->vData.flickTarget)
1471                     d->flickY(-d->vData.smoothVelocity.value());
1472                 d->bufferMode = QSGListViewPrivate::BufferBefore;
1473             } else if (d->vData.velocity < 0) {
1474                 const qreal maxY = maxYExtent();
1475                 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
1476                     && maxY != d->vData.flickTarget)
1477                     d->flickY(-d->vData.smoothVelocity.value());
1478                 d->bufferMode = QSGListViewPrivate::BufferAfter;
1479             }
1480         }
1481
1482         if (xflick() && !d->hData.inOvershoot) {
1483             if (d->hData.velocity > 0) {
1484                 const qreal minX = minXExtent();
1485                 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
1486                     && minX != d->hData.flickTarget)
1487                     d->flickX(-d->hData.smoothVelocity.value());
1488                 d->bufferMode = d->isRightToLeft() ? QSGListViewPrivate::BufferAfter : QSGListViewPrivate::BufferBefore;
1489             } else if (d->hData.velocity < 0) {
1490                 const qreal maxX = maxXExtent();
1491                 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
1492                     && maxX != d->hData.flickTarget)
1493                     d->flickX(-d->hData.smoothVelocity.value());
1494                 d->bufferMode = d->isRightToLeft() ? QSGListViewPrivate::BufferBefore : QSGListViewPrivate::BufferAfter;
1495             }
1496         }
1497         d->inFlickCorrection = false;
1498     }
1499     d->inViewportMoved = false;
1500 }
1501
1502 void QSGListView::keyPressEvent(QKeyEvent *event)
1503 {
1504     Q_D(QSGListView);
1505     if (d->model && d->model->count() && d->interactive) {
1506         if ((d->orient == QSGListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
1507                     || (d->orient == QSGListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
1508                     || (d->orient == QSGListView::Vertical && event->key() == Qt::Key_Up)) {
1509             if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
1510                 decrementCurrentIndex();
1511                 event->accept();
1512                 return;
1513             } else if (d->wrap) {
1514                 event->accept();
1515                 return;
1516             }
1517         } else if ((d->orient == QSGListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
1518                     || (d->orient == QSGListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
1519                     || (d->orient == QSGListView::Vertical && event->key() == Qt::Key_Down)) {
1520             if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
1521                 incrementCurrentIndex();
1522                 event->accept();
1523                 return;
1524             } else if (d->wrap) {
1525                 event->accept();
1526                 return;
1527             }
1528         }
1529     }
1530     event->ignore();
1531     QSGItemView::keyPressEvent(event);
1532 }
1533
1534 void QSGListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1535 {
1536     Q_D(QSGListView);
1537     if (d->isRightToLeft() && d->orient == QSGListView::Horizontal) {
1538         // maintain position relative to the right edge
1539         int dx = newGeometry.width() - oldGeometry.width();
1540         setContentX(contentX() - dx);
1541     }
1542     QSGItemView::geometryChanged(newGeometry, oldGeometry);
1543 }
1544
1545
1546 void QSGListView::incrementCurrentIndex()
1547 {
1548     Q_D(QSGListView);
1549     int count = d->model ? d->model->count() : 0;
1550     if (count && (currentIndex() < count - 1 || d->wrap)) {
1551         d->moveReason = QSGListViewPrivate::SetIndex;
1552         int index = currentIndex()+1;
1553         setCurrentIndex((index >= 0 && index < count) ? index : 0);
1554     }
1555 }
1556
1557 void QSGListView::decrementCurrentIndex()
1558 {
1559     Q_D(QSGListView);
1560     int count = d->model ? d->model->count() : 0;
1561     if (count && (currentIndex() > 0 || d->wrap)) {
1562         d->moveReason = QSGListViewPrivate::SetIndex;
1563         int index = currentIndex()-1;
1564         setCurrentIndex((index >= 0 && index < count) ? index : count-1);
1565     }
1566 }
1567
1568 void QSGListView::updateSections()
1569 {
1570     Q_D(QSGListView);
1571     if (isComponentComplete() && d->model) {
1572         QList<QByteArray> roles;
1573         if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
1574             roles << d->sectionCriteria->property().toUtf8();
1575         d->model->setWatchedRoles(roles);
1576         d->updateSections();
1577         if (d->itemCount)
1578             d->layout();
1579     }
1580 }
1581
1582 void QSGListView::itemsInserted(int modelIndex, int count)
1583 {
1584     Q_D(QSGListView);
1585     if (!isComponentComplete() || !d->model || !d->model->isValid())
1586         return;
1587     d->updateUnrequestedIndexes();
1588     d->moveReason = QSGListViewPrivate::Other;
1589
1590     qreal tempPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
1591     int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
1592     if (index < 0) {
1593         int i = d->visibleItems.count() - 1;
1594         while (i > 0 && d->visibleItems.at(i)->index == -1)
1595             --i;
1596         if (i == 0 && d->visibleItems.first()->index == -1) {
1597             // there are no visible items except items marked for removal
1598             index = d->visibleItems.count();
1599         } else if (d->visibleItems.at(i)->index + 1 == modelIndex
1600             && d->visibleItems.at(i)->endPosition() <= d->buffer+tempPos+d->size()) {
1601             // Special case of appending an item to the model.
1602             index = d->visibleItems.count();
1603         } else {
1604             if (modelIndex < d->visibleIndex) {
1605                 // Insert before visible items
1606                 d->visibleIndex += count;
1607                 for (int i = 0; i < d->visibleItems.count(); ++i) {
1608                     FxViewItem *item = d->visibleItems.at(i);
1609                     if (item->index != -1 && item->index >= modelIndex)
1610                         item->index += count;
1611                 }
1612             }
1613             if (d->currentIndex >= modelIndex) {
1614                 // adjust current item index
1615                 d->currentIndex += count;
1616                 if (d->currentItem)
1617                     d->currentItem->index = d->currentIndex;
1618                 emit currentIndexChanged();
1619             }
1620             d->scheduleLayout();
1621             d->itemCount += count;
1622             emit countChanged();
1623             return;
1624         }
1625     }
1626
1627     // index can be the next item past the end of the visible items list (i.e. appended)
1628     int pos = 0;
1629     if (d->visibleItems.count()) {
1630         pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position()
1631                                                 : d->visibleItems.last()->endPosition()+d->spacing;
1632     }
1633
1634     int initialPos = pos;
1635     int diff = 0;
1636     QList<FxListItemSG*> added;
1637     bool addedVisible = false;
1638     FxViewItem *firstVisible = d->firstVisibleItem();
1639     if (firstVisible && pos < firstVisible->position()) {
1640         // Insert items before the visible item.
1641         int insertionIdx = index;
1642         int i = 0;
1643         int from = tempPos - d->buffer;
1644         for (i = count-1; i >= 0 && pos > from; --i) {
1645             if (!addedVisible) {
1646                 d->scheduleLayout();
1647                 addedVisible = true;
1648             }
1649             FxListItemSG *item = static_cast<FxListItemSG*>(d->createItem(modelIndex + i));
1650             d->visibleItems.insert(insertionIdx, item);
1651             pos -= item->size() + d->spacing;
1652             item->setPosition(pos);
1653             index++;
1654         }
1655         if (i >= 0) {
1656             // If we didn't insert all our new items - anything
1657             // before the current index is not visible - remove it.
1658             while (insertionIdx--) {
1659                 FxListItemSG *item = static_cast<FxListItemSG*>(d->visibleItems.takeFirst());
1660                 if (item->index != -1)
1661                     d->visibleIndex++;
1662                 d->releaseItem(item);
1663             }
1664         } else {
1665             // adjust pos of items before inserted items.
1666             for (int i = insertionIdx-1; i >= 0; i--) {
1667                 FxListItemSG *listItem = static_cast<FxListItemSG*>(d->visibleItems.at(i));
1668                 listItem->setPosition(listItem->position() - (initialPos - pos));
1669             }
1670         }
1671     } else {
1672         int i = 0;
1673         int to = d->buffer+tempPos+d->size();
1674         for (i = 0; i < count && pos <= to; ++i) {
1675             if (!addedVisible) {
1676                 d->scheduleLayout();
1677                 addedVisible = true;
1678             }
1679             FxListItemSG *item = static_cast<FxListItemSG*>(d->createItem(modelIndex + i));
1680             d->visibleItems.insert(index, item);
1681             item->setPosition(pos);
1682             added.append(item);
1683             pos += item->size() + d->spacing;
1684             ++index;
1685         }
1686         if (i != count) {
1687             // We didn't insert all our new items, which means anything
1688             // beyond the current index is not visible - remove it.
1689             while (d->visibleItems.count() > index)
1690                 d->releaseItem(d->visibleItems.takeLast());
1691         }
1692         diff = pos - initialPos;
1693     }
1694     if (d->itemCount && d->currentIndex >= modelIndex) {
1695         // adjust current item index
1696         d->currentIndex += count;
1697         if (d->currentItem) {
1698             d->currentItem->index = d->currentIndex;
1699             static_cast<FxListItemSG *>(d->currentItem)->setPosition(static_cast<FxListItemSG *>(d->currentItem)->position() + diff);
1700         }
1701         emit currentIndexChanged();
1702     } else if (!d->itemCount && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
1703         d->updateCurrent(0);
1704     }
1705     // Update the indexes of the following visible items.
1706     for (; index < d->visibleItems.count(); ++index) {
1707         FxViewItem *item = d->visibleItems.at(index);
1708         if (d->currentItem && item->item != d->currentItem->item)
1709             static_cast<FxListItemSG*>(item)->setPosition(item->position() + diff);
1710         if (item->index != -1)
1711             item->index += count;
1712     }
1713     // everything is in order now - emit add() signal
1714     for (int j = 0; j < added.count(); ++j)
1715         added.at(j)->attached->emitAdd();
1716
1717     d->updateSections();
1718     d->itemCount += count;
1719     emit countChanged();
1720 }
1721
1722 void QSGListView::itemsRemoved(int modelIndex, int count)
1723 {
1724     Q_D(QSGListView);
1725     if (!isComponentComplete() || !d->model || !d->model->isValid())
1726         return;
1727     d->moveReason = QSGListViewPrivate::Other;
1728     d->updateUnrequestedIndexes();
1729     d->itemCount -= count;
1730
1731     FxViewItem *firstVisible = d->firstVisibleItem();
1732     int preRemovedSize = 0;
1733     bool removedVisible = false;
1734     // Remove the items from the visible list, skipping anything already marked for removal
1735     QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1736     while (it != d->visibleItems.end()) {
1737         FxViewItem *item = *it;
1738         if (item->index == -1 || item->index < modelIndex) {
1739             // already removed, or before removed items
1740             ++it;
1741         } else if (item->index >= modelIndex + count) {
1742             // after removed items
1743             item->index -= count;
1744             ++it;
1745         } else {
1746             // removed item
1747             if (!removedVisible) {
1748                 d->scheduleLayout();
1749                 removedVisible = true;
1750             }
1751             item->attached->emitRemove();
1752             if (item->attached->delayRemove()) {
1753                 item->index = -1;
1754                 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
1755                 ++it;
1756             } else {
1757                 if (item == firstVisible)
1758                     firstVisible = 0;
1759                 if (firstVisible && item->position() < firstVisible->position())
1760                     preRemovedSize += item->size();
1761                 it = d->visibleItems.erase(it);
1762                 d->releaseItem(item);
1763             }
1764         }
1765     }
1766
1767     if (firstVisible && d->visibleItems.first() != firstVisible)
1768         static_cast<FxListItemSG*>(d->visibleItems.first())->setPosition(d->visibleItems.first()->position() + preRemovedSize);
1769
1770     // update visibleIndex
1771     bool haveVisibleIndex = false;
1772     for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
1773         if ((*it)->index != -1) {
1774             d->visibleIndex = (*it)->index;
1775             haveVisibleIndex = true;
1776             break;
1777         }
1778     }
1779
1780     // fix current
1781     if (d->currentIndex >= modelIndex + count) {
1782         d->currentIndex -= count;
1783         if (d->currentItem)
1784             d->currentItem->index -= count;
1785         emit currentIndexChanged();
1786     } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
1787         // current item has been removed.
1788         if (d->currentItem) {
1789             d->currentItem->attached->setIsCurrentItem(false);
1790             d->releaseItem(d->currentItem);
1791             d->currentItem = 0;
1792         }
1793         d->currentIndex = -1;
1794         if (d->itemCount)
1795             d->updateCurrent(qMin(modelIndex, d->itemCount-1));
1796         else
1797             emit currentIndexChanged();
1798     }
1799
1800     if (!haveVisibleIndex) {
1801         d->timeline.clear();
1802         if (removedVisible && d->itemCount == 0) {
1803             d->visibleIndex = 0;
1804             d->visiblePos = 0;
1805             d->setPosition(d->contentStartPosition());
1806             d->updateHeader();
1807             d->updateFooter();
1808         } else {
1809             if (modelIndex < d->visibleIndex)
1810                 d->visibleIndex = modelIndex+1;
1811             d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0);
1812         }
1813     }
1814
1815     d->updateSections();
1816     emit countChanged();
1817 }
1818
1819 void QSGListView::itemsMoved(int from, int to, int count)
1820 {
1821     Q_D(QSGListView);
1822     if (!isComponentComplete() || !d->isValid())
1823         return;
1824     d->updateUnrequestedIndexes();
1825
1826     if (d->visibleItems.isEmpty()) {
1827         d->refill();
1828         return;
1829     }
1830
1831     d->moveReason = QSGListViewPrivate::Other;
1832
1833     bool movingBackwards = from > to;
1834     d->adjustMoveParameters(&from, &to, &count);
1835
1836     QHash<int,FxViewItem*> moved;
1837     int moveBy = 0;
1838     FxViewItem *firstVisible = d->firstVisibleItem();
1839     int firstItemIndex = firstVisible ? firstVisible->index : -1;
1840
1841     // if visibleItems.first() is above the content start pos, and the items
1842     // beneath it are moved, ensure this first item is later repositioned correctly
1843     // (to above the next visible item) so that subsequent layout() is correct
1844     bool repositionFirstItem = firstVisible
1845             && d->visibleItems.first()->position() < firstVisible->position()
1846             && from > d->visibleItems.first()->index;
1847
1848     QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1849     while (it != d->visibleItems.end()) {
1850         FxViewItem *item = *it;
1851         if (item->index >= from && item->index < from + count) {
1852             // take the items that are moving
1853             item->index += (to-from);
1854             moved.insert(item->index, item);
1855             if (repositionFirstItem)
1856                 moveBy += item->size();
1857             it = d->visibleItems.erase(it);
1858         } else {
1859             // move everything after the moved items.
1860             if (item->index > from && item->index != -1)
1861                 item->index -= count;
1862             ++it;
1863         }
1864     }
1865
1866     int movedCount = 0;
1867     int endIndex = d->visibleIndex;
1868     it = d->visibleItems.begin();
1869     while (it != d->visibleItems.end()) {
1870         FxViewItem *item = *it;
1871         if (movedCount < count && item->index >= to && item->index < to + count) {
1872             // place items in the target position, reusing any existing items
1873             int targetIndex = item->index + movedCount;
1874             FxViewItem *movedItem = moved.take(targetIndex);
1875             if (!movedItem)
1876                 movedItem = d->createItem(targetIndex);
1877             it = d->visibleItems.insert(it, movedItem);
1878             ++it;
1879             ++movedCount;
1880         } else {
1881             if (item->index != -1) {
1882                 if (item->index >= to) {
1883                     // update everything after the moved items.
1884                     item->index += count;
1885                 }
1886                 endIndex = item->index;
1887             }
1888             ++it;
1889         }
1890     }
1891
1892     // If we have moved items to the end of the visible items
1893     // then add any existing moved items that we have
1894     while (FxViewItem *item = moved.take(endIndex+1)) {
1895         d->visibleItems.append(item);
1896         ++endIndex;
1897     }
1898
1899     // update visibleIndex
1900     for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
1901         if ((*it)->index != -1) {
1902             d->visibleIndex = (*it)->index;
1903             break;
1904         }
1905     }
1906
1907     // if first visible item is moving but another item is moving up to replace it,
1908     // do this positioning now to avoid shifting all content forwards
1909     if (movingBackwards && firstItemIndex >= 0) {
1910         for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
1911             if ((*it)->index == firstItemIndex) {
1912                 static_cast<FxListItemSG*>(*it)->setPosition(firstVisible->position());
1913                 break;
1914             }
1915         }
1916     }
1917
1918     // Fix current index
1919     if (d->currentIndex >= 0 && d->currentItem) {
1920         int oldCurrent = d->currentIndex;
1921         d->currentIndex = d->model->indexOf(d->currentItem->item, this);
1922         if (oldCurrent != d->currentIndex) {
1923             d->currentItem->index = d->currentIndex;
1924             emit currentIndexChanged();
1925         }
1926     }
1927
1928     // Whatever moved items remain are no longer visible items.
1929     while (moved.count()) {
1930         int idx = moved.begin().key();
1931         FxViewItem *item = moved.take(idx);
1932         if (d->currentItem && item->item == d->currentItem->item)
1933             static_cast<FxListItemSG*>(item)->setPosition(d->positionAt(idx));
1934         d->releaseItem(item);
1935     }
1936
1937     // Ensure we don't cause an ugly list scroll.
1938     if (d->visibleItems.count())
1939         static_cast<FxListItemSG*>(d->visibleItems.first())->setPosition(d->visibleItems.first()->position() + moveBy);
1940
1941     d->updateSections();
1942     d->layout();
1943 }
1944
1945
1946 QSGListViewAttached *QSGListView::qmlAttachedProperties(QObject *obj)
1947 {
1948     return new QSGListViewAttached(obj);
1949 }
1950
1951 QT_END_NAMESPACE