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