Ensure section header/footer are cleaned up.
[profile/ivi/qtdeclarative.git] / src / quick / items / qquicklistview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquicklistview_p.h"
43 #include "qquickitemview_p_p.h"
44 #include "qquickvisualitemmodel_p.h"
45
46 #include <QtQml/qqmlexpression.h>
47 #include <QtQml/qqmlengine.h>
48 #include <QtQml/qqmlinfo.h>
49 #include <QtGui/qevent.h>
50 #include <QtCore/qmath.h>
51 #include <QtCore/qcoreapplication.h>
52
53 #include <private/qquicksmoothedanimation_p_p.h>
54 #include <private/qlistmodelinterface_p.h>
55 #include "qplatformdefs.h"
56
57 QT_BEGIN_NAMESPACE
58
59 #ifndef QML_FLICK_SNAPONETHRESHOLD
60 #define QML_FLICK_SNAPONETHRESHOLD 30
61 #endif
62
63 //#define DEBUG_DELEGATE_LIFECYCLE
64
65 class FxListItemSG;
66
67 class QQuickListViewPrivate : public QQuickItemViewPrivate
68 {
69     Q_DECLARE_PUBLIC(QQuickListView)
70 public:
71     static QQuickListViewPrivate* get(QQuickListView *item) { return item->d_func(); }
72
73     virtual Qt::Orientation layoutOrientation() const;
74     virtual bool isContentFlowReversed() const;
75     bool isRightToLeft() const;
76
77     virtual qreal positionAt(int index) const;
78     virtual qreal endPositionAt(int index) const;
79     virtual qreal originPosition() const;
80     virtual qreal lastPosition() const;
81
82     FxViewItem *itemBefore(int modelIndex) const;
83     QString sectionAt(int modelIndex);
84     qreal snapPosAt(qreal pos);
85     FxViewItem *snapItemAt(qreal pos);
86
87     virtual void init();
88     virtual void clear();
89
90     virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer);
91     virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
92     virtual void visibleItemsChanged();
93
94     virtual FxViewItem *newViewItem(int index, QQuickItem *item);
95     virtual void initializeViewItem(FxViewItem *item);
96     virtual bool releaseItem(FxViewItem *item);
97     virtual void repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer);
98     virtual void repositionPackageItemAt(QQuickItem *item, int index);
99     virtual void resetFirstItemPosition(qreal pos = 0.0);
100     virtual void adjustFirstItem(qreal forwards, qreal backwards, int);
101
102     virtual void createHighlight();
103     virtual void updateHighlight();
104     virtual void resetHighlightPosition();
105
106     virtual void setPosition(qreal pos);
107     virtual void layoutVisibleItems(int fromModelIndex = 0);
108
109     virtual bool applyInsertionChange(const QQuickChangeSet::Insert &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView);
110     virtual void translateAndTransitionItemsAfter(int afterIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult);
111
112     virtual void updateSections();
113     QQuickItem *getSectionItem(const QString &section);
114     void releaseSectionItem(QQuickItem *item);
115     void updateInlineSection(FxListItemSG *);
116     void updateCurrentSection();
117     void updateStickySections();
118
119     virtual qreal headerSize() const;
120     virtual qreal footerSize() const;
121     virtual bool showHeaderForIndex(int index) const;
122     virtual bool showFooterForIndex(int index) const;
123     virtual void updateHeader();
124     virtual void updateFooter();
125
126     virtual void changedVisibleIndex(int newIndex);
127     virtual void initializeCurrentItem();
128
129     void updateAverage();
130
131     void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
132     virtual void fixupPosition();
133     virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
134     virtual void flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
135                         QQuickTimeLineCallback::Callback fixupCallback, qreal velocity);
136
137     QQuickListView::Orientation orient;
138     qreal visiblePos;
139     qreal averageSize;
140     qreal spacing;
141     QQuickListView::SnapMode snapMode;
142
143     QSmoothedAnimation *highlightPosAnimator;
144     QSmoothedAnimation *highlightSizeAnimator;
145     qreal highlightMoveSpeed;
146     qreal highlightResizeSpeed;
147     int highlightResizeDuration;
148
149     QQuickViewSection *sectionCriteria;
150     QString currentSection;
151     static const int sectionCacheSize = 5;
152     QQuickItem *sectionCache[sectionCacheSize];
153     QQuickItem *currentSectionItem;
154     QString currentStickySection;
155     QQuickItem *nextSectionItem;
156     QString nextStickySection;
157     QString lastVisibleSection;
158     QString nextSection;
159
160     qreal overshootDist;
161     bool correctFlick : 1;
162     bool inFlickCorrection : 1;
163
164     QQuickListViewPrivate()
165         : orient(QQuickListView::Vertical)
166         , visiblePos(0)
167         , averageSize(100.0), spacing(0.0)
168         , snapMode(QQuickListView::NoSnap)
169         , highlightPosAnimator(0), highlightSizeAnimator(0)
170         , highlightMoveSpeed(400), highlightResizeSpeed(400), highlightResizeDuration(-1)
171         , sectionCriteria(0), currentSectionItem(0), nextSectionItem(0)
172         , overshootDist(0.0), correctFlick(false), inFlickCorrection(false)
173     {}
174     ~QQuickListViewPrivate() {
175         delete highlightPosAnimator;
176         delete highlightSizeAnimator;
177     }
178
179     friend class QQuickViewSection;
180 };
181
182 //----------------------------------------------------------------------------
183
184 QQuickViewSection::QQuickViewSection(QQuickListView *parent)
185     : QObject(parent), m_criteria(FullString), m_delegate(0), m_labelPositioning(InlineLabels)
186     , m_view(parent ? QQuickListViewPrivate::get(parent) : 0)
187 {
188 }
189
190 void QQuickViewSection::setProperty(const QString &property)
191 {
192     if (property != m_property) {
193         m_property = property;
194         emit propertyChanged();
195         m_view->updateSections();
196     }
197 }
198
199 void QQuickViewSection::setCriteria(QQuickViewSection::SectionCriteria criteria)
200 {
201     if (criteria != m_criteria) {
202         m_criteria = criteria;
203         emit criteriaChanged();
204         m_view->updateSections();
205     }
206 }
207
208 void QQuickViewSection::setDelegate(QQmlComponent *delegate)
209 {
210     if (delegate != m_delegate) {
211         m_delegate = delegate;
212         emit delegateChanged();
213         m_view->updateSections();
214     }
215 }
216
217 QString QQuickViewSection::sectionString(const QString &value)
218 {
219     if (m_criteria == FirstCharacter)
220         return value.isEmpty() ? QString() : value.at(0);
221     else
222         return value;
223 }
224
225 void QQuickViewSection::setLabelPositioning(int l)
226 {
227     if (m_labelPositioning != l) {
228         m_labelPositioning = l;
229         emit labelPositioningChanged();
230         m_view->updateSections();
231     }
232 }
233
234 //----------------------------------------------------------------------------
235
236 class FxListItemSG : public FxViewItem
237 {
238 public:
239     FxListItemSG(QQuickItem *i, QQuickListView *v, bool own) : FxViewItem(i, own), view(v) {
240         attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
241         if (attached)
242             static_cast<QQuickListViewAttached*>(attached)->setView(view);
243     }
244
245     ~FxListItemSG() {}
246
247     inline QQuickItem *section() const {
248         return attached ? static_cast<QQuickListViewAttached*>(attached)->m_sectionItem : 0;
249     }
250     void setSection(QQuickItem *s) {
251         if (!attached)
252             attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
253         static_cast<QQuickListViewAttached*>(attached)->m_sectionItem = s;
254     }
255
256     qreal position() const {
257         if (section()) {
258             if (view->orientation() == QQuickListView::Vertical)
259                 return section()->y();
260             else
261                 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section()->width()-section()->x() : section()->x());
262         } else {
263             return itemPosition();
264         }
265     }
266     qreal itemPosition() const {
267         if (view->orientation() == QQuickListView::Vertical)
268             return itemY();
269         else
270             return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-itemX() : itemX());
271     }
272     qreal size() const {
273         if (section())
274             return (view->orientation() == QQuickListView::Vertical ? item->height()+section()->height() : item->width()+section()->width());
275         else
276             return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
277     }
278     qreal itemSize() const {
279         return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
280     }
281     qreal sectionSize() const {
282         if (section())
283             return (view->orientation() == QQuickListView::Vertical ? section()->height() : section()->width());
284         return 0.0;
285     }
286     qreal endPosition() const {
287         if (view->orientation() == QQuickListView::Vertical) {
288             return itemY() + item->height();
289         } else {
290             return (view->effectiveLayoutDirection() == Qt::RightToLeft
291                     ? -itemX()
292                     : itemX() + item->width());
293         }
294     }
295     void setPosition(qreal pos, bool immediate = false) {
296         // position the section immediately even if there is a transition
297         if (section()) {
298             if (view->orientation() == QQuickListView::Vertical) {
299                 section()->setY(pos);
300             } else {
301                 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
302                     section()->setX(-section()->width()-pos);
303                 else
304                     section()->setX(pos);
305             }
306         }
307         moveTo(pointForPosition(pos), immediate);
308     }
309     void setSize(qreal size) {
310         if (view->orientation() == QQuickListView::Vertical)
311             item->setHeight(size);
312         else
313             item->setWidth(size);
314     }
315     bool contains(qreal x, qreal y) const {
316         return (x >= itemX() && x < itemX() + item->width() &&
317                 y >= itemY() && y < itemY() + item->height());
318     }
319
320     QQuickListView *view;
321
322 private:
323     QPointF pointForPosition(qreal pos) const {
324         if (view->orientation() == QQuickListView::Vertical) {
325             if (section())
326                 pos += section()->height();
327             return QPointF(itemX(), pos);
328         } else {
329             if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
330                 if (section())
331                     pos += section()->width();
332                 return QPointF(-item->width() - pos, itemY());
333             } else {
334                 if (section())
335                     pos += section()->width();
336                 return QPointF(pos, itemY());
337             }
338         }
339     }
340 };
341
342 //----------------------------------------------------------------------------
343
344 bool QQuickListViewPrivate::isContentFlowReversed() const
345 {
346     return isRightToLeft();
347 }
348
349 Qt::Orientation QQuickListViewPrivate::layoutOrientation() const
350 {
351     return static_cast<Qt::Orientation>(orient);
352 }
353
354 bool QQuickListViewPrivate::isRightToLeft() const
355 {
356     Q_Q(const QQuickListView);
357     return orient == QQuickListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
358 }
359
360 // Returns the item before modelIndex, if created.
361 // May return an item marked for removal.
362 FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const
363 {
364     if (modelIndex < visibleIndex)
365         return 0;
366     int idx = 1;
367     int lastIndex = -1;
368     while (idx < visibleItems.count()) {
369         FxViewItem *item = visibleItems.at(idx);
370         if (item->index != -1)
371             lastIndex = item->index;
372         if (item->index == modelIndex)
373             return visibleItems.at(idx-1);
374         ++idx;
375     }
376     if (lastIndex == modelIndex-1)
377         return visibleItems.last();
378     return 0;
379 }
380
381 void QQuickListViewPrivate::setPosition(qreal pos)
382 {
383     Q_Q(QQuickListView);
384     if (orient == QQuickListView::Vertical) {
385         q->QQuickFlickable::setContentY(pos);
386     } else {
387         if (isRightToLeft())
388             q->QQuickFlickable::setContentX(-pos-size());
389         else
390             q->QQuickFlickable::setContentX(pos);
391     }
392 }
393
394 qreal QQuickListViewPrivate::originPosition() const
395 {
396     qreal pos = 0;
397     if (!visibleItems.isEmpty()) {
398         pos = (*visibleItems.constBegin())->position();
399         if (visibleIndex > 0)
400             pos -= visibleIndex * (averageSize + spacing);
401     }
402     return pos;
403 }
404
405 qreal QQuickListViewPrivate::lastPosition() const
406 {
407     qreal pos = 0;
408     if (!visibleItems.isEmpty()) {
409         int invisibleCount = visibleItems.count() - visibleIndex;
410         for (int i = visibleItems.count()-1; i >= 0; --i) {
411             if (visibleItems.at(i)->index != -1) {
412                 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
413                 break;
414             }
415         }
416         pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
417     } else if (model && model->count()) {
418         pos = (model->count() * averageSize + (model->count()-1) * spacing);
419     }
420     return pos;
421 }
422
423 qreal QQuickListViewPrivate::positionAt(int modelIndex) const
424 {
425     if (FxViewItem *item = visibleItem(modelIndex)) {
426         return item->position();
427     }
428     if (!visibleItems.isEmpty()) {
429         if (modelIndex < visibleIndex) {
430             int count = visibleIndex - modelIndex;
431             qreal cs = 0;
432             if (modelIndex == currentIndex && currentItem) {
433                 cs = currentItem->size() + spacing;
434                 --count;
435             }
436             return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
437         } else {
438             int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
439             return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing);
440         }
441     }
442     return 0;
443 }
444
445 qreal QQuickListViewPrivate::endPositionAt(int modelIndex) const
446 {
447     if (FxViewItem *item = visibleItem(modelIndex))
448         return item->endPosition();
449     if (!visibleItems.isEmpty()) {
450         if (modelIndex < visibleIndex) {
451             int count = visibleIndex - modelIndex;
452             return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing;
453         } else {
454             int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
455             return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
456         }
457     }
458     return 0;
459 }
460
461 QString QQuickListViewPrivate::sectionAt(int modelIndex)
462 {
463     if (FxViewItem *item = visibleItem(modelIndex))
464         return item->attached->section();
465
466     QString section;
467     if (sectionCriteria) {
468         QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
469         section = sectionCriteria->sectionString(propValue);
470     }
471
472     return section;
473 }
474
475 qreal QQuickListViewPrivate::snapPosAt(qreal pos)
476 {
477     if (FxViewItem *snapItem = snapItemAt(pos))
478         return snapItem->position();
479     if (visibleItems.count()) {
480         qreal firstPos = (*visibleItems.constBegin())->position();
481         qreal endPos = (*(--visibleItems.constEnd()))->position();
482         if (pos < firstPos) {
483             return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
484         } else if (pos > endPos)
485             return endPos + qRound((pos - endPos) / averageSize) * averageSize;
486     }
487     return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
488 }
489
490 FxViewItem *QQuickListViewPrivate::snapItemAt(qreal pos)
491 {
492     FxViewItem *snapItem = 0;
493     qreal prevItemSize = 0;
494     for (int i = 0; i < visibleItems.count(); ++i) {
495         FxViewItem *item = visibleItems.at(i);
496         if (item->index == -1)
497             continue;
498         qreal itemTop = item->position();
499         if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
500             return item;
501         if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
502             snapItem = item;
503         prevItemSize = item->size();
504     }
505     return snapItem;
506 }
507
508 void QQuickListViewPrivate::changedVisibleIndex(int newIndex)
509 {
510     visiblePos = positionAt(newIndex);
511     visibleIndex = newIndex;
512 }
513
514 void QQuickListViewPrivate::init()
515 {
516     QQuickItemViewPrivate::init();
517     ::memset(sectionCache, 0, sizeof(QQuickItem*) * sectionCacheSize);
518 }
519
520 void QQuickListViewPrivate::clear()
521 {
522     for (int i = 0; i < sectionCacheSize; ++i) {
523         delete sectionCache[i];
524         sectionCache[i] = 0;
525     }
526     visiblePos = 0;
527     releaseSectionItem(currentSectionItem);
528     currentSectionItem = 0;
529     releaseSectionItem(nextSectionItem);
530     nextSectionItem = 0;
531     lastVisibleSection = QString();
532     QQuickItemViewPrivate::clear();
533 }
534
535 FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
536 {
537     Q_Q(QQuickListView);
538
539     FxListItemSG *listItem = new FxListItemSG(item, q, false);
540     listItem->index = modelIndex;
541
542     // initialise attached properties
543     if (sectionCriteria) {
544         QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
545         listItem->attached->m_section = sectionCriteria->sectionString(propValue);
546         if (modelIndex > 0) {
547             if (FxViewItem *item = itemBefore(modelIndex))
548                 listItem->attached->m_prevSection = item->attached->section();
549             else
550                 listItem->attached->m_prevSection = sectionAt(modelIndex-1);
551         }
552         if (modelIndex < model->count()-1) {
553             if (FxViewItem *item = visibleItem(modelIndex+1))
554                 listItem->attached->m_nextSection = static_cast<QQuickListViewAttached*>(item->attached)->section();
555             else
556                 listItem->attached->m_nextSection = sectionAt(modelIndex+1);
557         }
558     }
559
560     return listItem;
561 }
562
563 void QQuickListViewPrivate::initializeViewItem(FxViewItem *item)
564 {
565     QQuickItemViewPrivate::initializeViewItem(item);
566
567     QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
568     itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
569
570     if (sectionCriteria && sectionCriteria->delegate()) {
571         if (item->attached->m_prevSection != item->attached->m_section)
572             updateInlineSection(static_cast<FxListItemSG*>(item));
573     }
574 }
575
576 bool QQuickListViewPrivate::releaseItem(FxViewItem *item)
577 {
578     if (!item || !model)
579         return true;
580
581     QQuickListViewAttached *att = static_cast<QQuickListViewAttached*>(item->attached);
582
583     bool released = QQuickItemViewPrivate::releaseItem(item);
584     if (released && att && att->m_sectionItem) {
585         // We hold no more references to this item
586         int i = 0;
587         do {
588             if (!sectionCache[i]) {
589                 sectionCache[i] = att->m_sectionItem;
590                 sectionCache[i]->setVisible(false);
591                 att->m_sectionItem = 0;
592                 break;
593             }
594             ++i;
595         } while (i < sectionCacheSize);
596         delete att->m_sectionItem;
597         att->m_sectionItem = 0;
598     }
599
600     return released;
601 }
602
603 bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, bool doBuffer)
604 {
605     qreal itemEnd = visiblePos;
606     if (visibleItems.count()) {
607         visiblePos = (*visibleItems.constBegin())->position();
608         itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
609     }
610
611     int modelIndex = findLastVisibleIndex();
612     bool haveValidItems = modelIndex >= 0;
613     modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
614
615     if (haveValidItems && (fillFrom > itemEnd+averageSize+spacing
616         || fillTo < visiblePos - averageSize - spacing)) {
617         // We've jumped more than a page.  Estimate which items are now
618         // visible and fill from there.
619         int count = (fillFrom - itemEnd) / (averageSize + spacing);
620         int newModelIdx = qBound(0, modelIndex + count, model->count());
621         count = newModelIdx - modelIndex;
622         if (count) {
623             for (int i = 0; i < visibleItems.count(); ++i)
624                 releaseItem(visibleItems.at(i));
625             visibleItems.clear();
626             modelIndex = newModelIdx;
627             visibleIndex = modelIndex;
628             visiblePos = itemEnd + count * (averageSize + spacing);
629             itemEnd = visiblePos;
630         }
631     }
632
633     bool changed = false;
634     FxListItemSG *item = 0;
635     qreal pos = itemEnd;
636     while (modelIndex < model->count() && pos <= fillTo) {
637 #ifdef DEBUG_DELEGATE_LIFECYCLE
638         qDebug() << "refill: append item" << modelIndex << "pos" << pos;
639 #endif
640         if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, doBuffer))))
641             break;
642         if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
643             item->setPosition(pos, true);
644         item->item->setVisible(!doBuffer);
645         pos += item->size() + spacing;
646         visibleItems.append(item);
647         ++modelIndex;
648         changed = true;
649     }
650
651     if (doBuffer && requestedIndex != -1) // already waiting for an item
652         return changed;
653
654     while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos > fillFrom) {
655 #ifdef DEBUG_DELEGATE_LIFECYCLE
656         qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos;
657 #endif
658         if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1, doBuffer))))
659             break;
660         --visibleIndex;
661         visiblePos -= item->size() + spacing;
662         if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
663             item->setPosition(visiblePos, true);
664         item->item->setVisible(!doBuffer);
665         visibleItems.prepend(item);
666         changed = true;
667     }
668
669     return changed;
670 }
671
672 bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
673 {
674     FxViewItem *item = 0;
675     bool changed = false;
676
677     // Remove items from the start of the view.
678     // Zero-sized items shouldn't be removed unless a non-zero-sized item is also being
679     // removed, otherwise a zero-sized item is infinitely added and removed over and
680     // over by refill().
681     int index = 0;
682     while (visibleItems.count() > 1 && index < visibleItems.count()
683            && (item = visibleItems.at(index)) && item->endPosition() < bufferFrom) {
684         if (item->attached->delayRemove())
685             break;
686
687         if (item->size() > 0) {
688 #ifdef DEBUG_DELEGATE_LIFECYCLE
689             qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
690 #endif
691             // remove this item and all zero-sized items before it
692             while (item) {
693                 if (item->index != -1)
694                     visibleIndex++;
695                 visibleItems.removeAt(index);
696                 if (item->transitionScheduledOrRunning()) {
697 #ifdef DEBUG_DELEGATE_LIFECYCLE
698                     qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
699 #endif
700                     item->releaseAfterTransition = true;
701                     releasePendingTransition.append(item);
702                 } else {
703                     releaseItem(item);
704                 }
705                 if (index == 0)
706                     break;
707                 item = visibleItems.at(--index);
708             }
709             changed = true;
710         } else {
711             index++;
712         }
713     }
714
715     while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
716         if (item->attached->delayRemove())
717             break;
718 #ifdef DEBUG_DELEGATE_LIFECYCLE
719         qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
720 #endif
721         visibleItems.removeLast();
722         if (item->transitionScheduledOrRunning()) {
723 #ifdef DEBUG_DELEGATE_LIFECYCLE
724             qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
725 #endif
726             item->releaseAfterTransition = true;
727             releasePendingTransition.append(item);
728         } else {
729             releaseItem(item);
730         }
731         changed = true;
732     }
733
734     return changed;
735 }
736
737 void QQuickListViewPrivate::visibleItemsChanged()
738 {
739     if (visibleItems.count())
740         visiblePos = (*visibleItems.constBegin())->position();
741     updateAverage();
742     if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
743         static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
744         updateHighlight();
745     }
746     if (sectionCriteria)
747         updateCurrentSection();
748     updateUnrequestedPositions();
749 }
750
751 void QQuickListViewPrivate::layoutVisibleItems(int fromModelIndex)
752 {
753     if (!visibleItems.isEmpty()) {
754         const qreal from = isContentFlowReversed() ? -position() - size() : position();
755         const qreal to = isContentFlowReversed() ? -position() : position() + size();
756
757         FxViewItem *firstItem = *visibleItems.constBegin();
758         bool fixedCurrent = currentItem && firstItem->item == currentItem->item;
759         qreal sum = firstItem->size();
760         qreal pos = firstItem->position() + firstItem->size() + spacing;
761         firstItem->item->setVisible(firstItem->endPosition() >= from && firstItem->position() <= to);
762
763         for (int i=1; i < visibleItems.count(); ++i) {
764             FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
765             if (item->index >= fromModelIndex) {
766                 item->setPosition(pos);
767                 item->setVisible(item->endPosition() >= from && item->position() <= to);
768             }
769             pos += item->size() + spacing;
770             sum += item->size();
771             fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
772         }
773         averageSize = qRound(sum / visibleItems.count());
774
775         // move current item if it is not a visible item.
776         if (currentIndex >= 0 && currentItem && !fixedCurrent)
777             static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
778     }
779 }
780
781 void QQuickListViewPrivate::repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer)
782 {
783     static_cast<FxListItemSG *>(item)->setPosition(positionAt(index) + sizeBuffer);
784 }
785
786 void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
787 {
788     Q_Q(QQuickListView);
789     qreal pos = position();
790     if (orient == QQuickListView::Vertical) {
791         if (item->y() + item->height() > pos && item->y() < pos + q->height())
792             item->setY(positionAt(index));
793     } else {
794         if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
795             if (isRightToLeft())
796                 item->setX(-positionAt(index)-item->width());
797             else
798                 item->setX(positionAt(index));
799         }
800     }
801 }
802
803 void QQuickListViewPrivate::resetFirstItemPosition(qreal pos)
804 {
805     FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.first());
806     item->setPosition(pos);
807 }
808
809 void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int)
810 {
811     if (!visibleItems.count())
812         return;
813     qreal diff = forwards - backwards;
814     static_cast<FxListItemSG*>(visibleItems.first())->setPosition(visibleItems.first()->position() + diff);
815 }
816
817 void QQuickListViewPrivate::createHighlight()
818 {
819     Q_Q(QQuickListView);
820     bool changed = false;
821     if (highlight) {
822         if (trackedItem == highlight)
823             trackedItem = 0;
824         delete highlight;
825         highlight = 0;
826
827         delete highlightPosAnimator;
828         delete highlightSizeAnimator;
829         highlightPosAnimator = 0;
830         highlightSizeAnimator = 0;
831
832         changed = true;
833     }
834
835     if (currentItem) {
836         QQuickItem *item = createHighlightItem();
837         if (item) {
838             FxListItemSG *newHighlight = new FxListItemSG(item, q, true);
839
840             if (autoHighlight) {
841                 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
842                 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
843             }
844             const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x");
845             highlightPosAnimator = new QSmoothedAnimation;
846             highlightPosAnimator->target = QQmlProperty(item, posProp);
847             highlightPosAnimator->velocity = highlightMoveSpeed;
848             highlightPosAnimator->userDuration = highlightMoveDuration;
849
850             const QLatin1String sizeProp(orient == QQuickListView::Vertical ? "height" : "width");
851             highlightSizeAnimator = new QSmoothedAnimation;
852             highlightSizeAnimator->velocity = highlightResizeSpeed;
853             highlightSizeAnimator->userDuration = highlightResizeDuration;
854             highlightSizeAnimator->target = QQmlProperty(item, sizeProp);
855
856             highlight = newHighlight;
857             changed = true;
858         }
859     }
860     if (changed)
861         emit q->highlightItemChanged();
862 }
863
864 void QQuickListViewPrivate::updateHighlight()
865 {
866     applyPendingChanges();
867
868     if ((!currentItem && highlight) || (currentItem && !highlight))
869         createHighlight();
870     bool strictHighlight = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
871     if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
872         // auto-update highlight
873         FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
874         highlightPosAnimator->to = isRightToLeft()
875                 ? -listItem->itemPosition()-listItem->itemSize()
876                 : listItem->itemPosition();
877         highlightSizeAnimator->to = listItem->itemSize();
878         if (orient == QQuickListView::Vertical) {
879             if (highlight->item->width() == 0)
880                 highlight->item->setWidth(currentItem->item->width());
881         } else {
882             if (highlight->item->height() == 0)
883                 highlight->item->setHeight(currentItem->item->height());
884         }
885
886         highlightPosAnimator->restart();
887         highlightSizeAnimator->restart();
888     }
889     updateTrackedItem();
890 }
891
892 void QQuickListViewPrivate::resetHighlightPosition()
893 {
894     if (highlight && currentItem)
895         static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
896 }
897
898 QQuickItem * QQuickListViewPrivate::getSectionItem(const QString &section)
899 {
900     Q_Q(QQuickListView);
901     QQuickItem *sectionItem = 0;
902     int i = sectionCacheSize-1;
903     while (i >= 0 && !sectionCache[i])
904         --i;
905     if (i >= 0) {
906         sectionItem = sectionCache[i];
907         sectionCache[i] = 0;
908         sectionItem->setVisible(true);
909         QQmlContext *context = QQmlEngine::contextForObject(sectionItem)->parentContext();
910         context->setContextProperty(QLatin1String("section"), section);
911     } else {
912         QQmlContext *creationContext = sectionCriteria->delegate()->creationContext();
913         QQmlContext *context = new QQmlContext(
914                 creationContext ? creationContext : qmlContext(q));
915         context->setContextProperty(QLatin1String("section"), section);
916         QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
917         if (nobj) {
918             QQml_setParent_noEvent(context, nobj);
919             sectionItem = qobject_cast<QQuickItem *>(nobj);
920             if (!sectionItem) {
921                 delete nobj;
922             } else {
923                 sectionItem->setZ(2);
924                 QQml_setParent_noEvent(sectionItem, contentItem);
925                 sectionItem->setParentItem(contentItem);
926             }
927         } else {
928             delete context;
929         }
930         sectionCriteria->delegate()->completeCreate();
931     }
932
933     return sectionItem;
934 }
935
936 void QQuickListViewPrivate::releaseSectionItem(QQuickItem *item)
937 {
938     if (!item)
939         return;
940     int i = 0;
941     do {
942         if (!sectionCache[i]) {
943             sectionCache[i] = item;
944             sectionCache[i]->setVisible(false);
945             return;
946         }
947         ++i;
948     } while (i < sectionCacheSize);
949     delete item;
950 }
951
952 void QQuickListViewPrivate::updateInlineSection(FxListItemSG *listItem)
953 {
954     if (!sectionCriteria || !sectionCriteria->delegate())
955         return;
956     if (listItem->attached->m_prevSection != listItem->attached->m_section
957             && (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels
958                 || (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) {
959         if (!listItem->section()) {
960             qreal pos = listItem->position();
961             listItem->setSection(getSectionItem(listItem->attached->m_section));
962             listItem->setPosition(pos);
963         } else {
964             QQmlContext *context = QQmlEngine::contextForObject(listItem->section())->parentContext();
965             context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
966         }
967     } else if (listItem->section()) {
968         qreal pos = listItem->position();
969         releaseSectionItem(listItem->section());
970         listItem->setSection(0);
971         listItem->setPosition(pos);
972     }
973 }
974
975 void QQuickListViewPrivate::updateStickySections()
976 {
977     if (!sectionCriteria
978             || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem))
979         return;
980
981     bool isRtl = isRightToLeft();
982     qreal viewPos = isRightToLeft() ? -position()-size() : position();
983     QQuickItem *sectionItem = 0;
984     QQuickItem *lastSectionItem = 0;
985     int index = 0;
986     while (index < visibleItems.count()) {
987         if (QQuickItem *section = static_cast<FxListItemSG *>(visibleItems.at(index))->section()) {
988             // Find the current section header and last visible section header
989             // and hide them if they will overlap a static section header.
990             qreal sectionPos = orient == QQuickListView::Vertical ? section->y() : section->x();
991             qreal sectionSize = orient == QQuickListView::Vertical ? section->height() : section->width();
992             bool visTop = true;
993             if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart)
994                 visTop = isRtl ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos;
995             bool visBot = true;
996             if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd)
997                 visBot = isRtl ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size();
998             section->setVisible(visBot && visTop);
999             if (visTop && !sectionItem)
1000                 sectionItem = section;
1001             if (isRtl) {
1002                if (-sectionPos <= viewPos + size())
1003                     lastSectionItem = section;
1004             } else {
1005                 if (sectionPos + sectionSize < viewPos + size())
1006                     lastSectionItem = section;
1007             }
1008         }
1009         ++index;
1010     }
1011
1012     // Current section header
1013     if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart && isValid() && visibleItems.count()) {
1014         if (!currentSectionItem) {
1015             currentSectionItem = getSectionItem(currentSection);
1016         } else if (currentStickySection != currentSection) {
1017             QQmlContext *context = QQmlEngine::contextForObject(currentSectionItem)->parentContext();
1018             context->setContextProperty(QLatin1String("section"), currentSection);
1019         }
1020         currentStickySection = currentSection;
1021         if (!currentSectionItem)
1022             return;
1023
1024         qreal sectionSize = orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1025         bool atBeginning = orient == QQuickListView::Vertical ? vData.atBeginning : (isRightToLeft() ? hData.atEnd : hData.atBeginning);
1026         currentSectionItem->setVisible(!atBeginning && (!header || header->endPosition() < viewPos));
1027         qreal pos = isRtl ? position() + size() - sectionSize : viewPos;
1028         if (sectionItem) {
1029             qreal sectionPos = orient == QQuickListView::Vertical ? sectionItem->y() : sectionItem->x();
1030             pos = isRtl ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize);
1031         }
1032         if (header)
1033             pos = isRtl ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos);
1034         if (footer)
1035             pos = isRtl ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
1036         if (orient == QQuickListView::Vertical)
1037             currentSectionItem->setY(pos);
1038         else
1039             currentSectionItem->setX(pos);
1040     } else if (currentSectionItem) {
1041         releaseSectionItem(currentSectionItem);
1042         currentSectionItem = 0;
1043     }
1044
1045     // Next section footer
1046     if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd && isValid() && visibleItems.count()) {
1047         if (!nextSectionItem) {
1048             nextSectionItem = getSectionItem(nextSection);
1049         } else if (nextStickySection != nextSection) {
1050             QQmlContext *context = QQmlEngine::contextForObject(nextSectionItem)->parentContext();
1051             context->setContextProperty(QLatin1String("section"), nextSection);
1052         }
1053         nextStickySection = nextSection;
1054         if (!nextSectionItem)
1055             return;
1056
1057         qreal sectionSize = orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1058         nextSectionItem->setVisible(!nextSection.isEmpty());
1059         qreal pos = isRtl ? position() : viewPos + size() - sectionSize;
1060         if (lastSectionItem) {
1061             qreal sectionPos = orient == QQuickListView::Vertical ? lastSectionItem->y() : lastSectionItem->x();
1062             pos = isRtl ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize);
1063         }
1064         if (header)
1065             pos = isRtl ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
1066         if (orient == QQuickListView::Vertical)
1067             nextSectionItem->setY(pos);
1068         else
1069             nextSectionItem->setX(pos);
1070     } else if (nextSectionItem) {
1071         releaseSectionItem(nextSectionItem);
1072         nextSectionItem = 0;
1073     }
1074 }
1075
1076 void QQuickListViewPrivate::updateSections()
1077 {
1078     Q_Q(QQuickListView);
1079     if (!q->isComponentComplete())
1080         return;
1081
1082     QQuickItemViewPrivate::updateSections();
1083
1084     if (sectionCriteria && !visibleItems.isEmpty() && isValid()) {
1085         QString prevSection;
1086         if (visibleIndex > 0)
1087             prevSection = sectionAt(visibleIndex-1);
1088         QQuickListViewAttached *prevAtt = 0;
1089         int idx = -1;
1090         for (int i = 0; i < visibleItems.count(); ++i) {
1091             QQuickListViewAttached *attached = static_cast<QQuickListViewAttached*>(visibleItems.at(i)->attached);
1092             attached->setPrevSection(prevSection);
1093             if (visibleItems.at(i)->index != -1) {
1094                 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1095                 attached->setSection(sectionCriteria->sectionString(propValue));
1096                 idx = visibleItems.at(i)->index;
1097             }
1098             updateInlineSection(static_cast<FxListItemSG*>(visibleItems.at(i)));
1099             if (prevAtt)
1100                 prevAtt->setNextSection(attached->section());
1101             prevSection = attached->section();
1102             prevAtt = attached;
1103         }
1104         if (prevAtt) {
1105             if (idx > 0 && idx < model->count()-1)
1106                 prevAtt->setNextSection(sectionAt(idx+1));
1107             else
1108                 prevAtt->setNextSection(QString());
1109         }
1110     }
1111
1112     lastVisibleSection = QString();
1113     updateCurrentSection();
1114     updateStickySections();
1115 }
1116
1117 void QQuickListViewPrivate::updateCurrentSection()
1118 {
1119     Q_Q(QQuickListView);
1120     if (!sectionCriteria || visibleItems.isEmpty()) {
1121         if (!currentSection.isEmpty()) {
1122             currentSection.clear();
1123             emit q->currentSectionChanged();
1124         }
1125         return;
1126     }
1127     bool inlineSections = sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels;
1128     qreal sectionThreshold = position();
1129     if (currentSectionItem && !inlineSections)
1130         sectionThreshold += orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1131     int index = 0;
1132     int modelIndex = visibleIndex;
1133     while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= sectionThreshold) {
1134         if (visibleItems.at(index)->index != -1)
1135             modelIndex = visibleItems.at(index)->index;
1136         ++index;
1137     }
1138
1139     QString newSection = currentSection;
1140     if (index < visibleItems.count())
1141         newSection = visibleItems.at(index)->attached->section();
1142     else
1143         newSection = (*visibleItems.constBegin())->attached->section();
1144     if (newSection != currentSection) {
1145         currentSection = newSection;
1146         updateStickySections();
1147         emit q->currentSectionChanged();
1148     }
1149
1150     if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
1151         // Don't want to scan for next section on every movement, so remember
1152         // the last section in the visible area and only scan for the next
1153         // section when that changes.  Clearing lastVisibleSection will also
1154         // force searching.
1155         QString lastSection = currentSection;
1156         qreal endPos = isRightToLeft() ? -position() : position() + size();
1157         if (nextSectionItem && !inlineSections)
1158             endPos -= orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1159         while (index < visibleItems.count() && static_cast<FxListItemSG*>(visibleItems.at(index))->itemPosition() < endPos) {
1160             if (visibleItems.at(index)->index != -1)
1161                 modelIndex = visibleItems.at(index)->index;
1162             lastSection = visibleItems.at(index)->attached->section();
1163             ++index;
1164         }
1165
1166         if (lastVisibleSection != lastSection) {
1167             nextSection = QString();
1168             lastVisibleSection = lastSection;
1169             for (int i = modelIndex; i < itemCount; ++i) {
1170                 QString section = sectionAt(i);
1171                 if (section != lastSection) {
1172                     nextSection = section;
1173                     updateStickySections();
1174                     break;
1175                 }
1176             }
1177         }
1178     }
1179 }
1180
1181 void QQuickListViewPrivate::initializeCurrentItem()
1182 {
1183     QQuickItemViewPrivate::initializeCurrentItem();
1184
1185     if (currentItem) {
1186         FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
1187
1188         // don't reposition the item if it is already in the visibleItems list
1189         FxViewItem *actualItem = visibleItem(currentIndex);
1190         if (!actualItem) {
1191             if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
1192                 // We can calculate exact postion in this case
1193                 listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1194             } else {
1195                 // Create current item now and position as best we can.
1196                 // Its position will be corrected when it becomes visible.
1197                 listItem->setPosition(positionAt(currentIndex));
1198             }
1199         }
1200
1201         if (visibleItems.isEmpty())
1202             averageSize = listItem->size();
1203     }
1204 }
1205
1206 void QQuickListViewPrivate::updateAverage()
1207 {
1208     if (!visibleItems.count())
1209         return;
1210     qreal sum = 0.0;
1211     for (int i = 0; i < visibleItems.count(); ++i)
1212         sum += visibleItems.at(i)->size();
1213     averageSize = qRound(sum / visibleItems.count());
1214 }
1215
1216 qreal QQuickListViewPrivate::headerSize() const
1217 {
1218     return header ? header->size() : 0.0;
1219 }
1220
1221 qreal QQuickListViewPrivate::footerSize() const
1222 {
1223     return footer ? footer->size() : 0.0;
1224 }
1225
1226 bool QQuickListViewPrivate::showHeaderForIndex(int index) const
1227 {
1228     return index == 0;
1229 }
1230
1231 bool QQuickListViewPrivate::showFooterForIndex(int index) const
1232 {
1233     return index == model->count()-1;
1234 }
1235
1236 void QQuickListViewPrivate::updateFooter()
1237 {
1238     Q_Q(QQuickListView);
1239     bool created = false;
1240     if (!footer) {
1241         QQuickItem *item = createComponentItem(footerComponent, true);
1242         if (!item)
1243             return;
1244         item->setZ(1);
1245         footer = new FxListItemSG(item, q, true);
1246         created = true;
1247     }
1248
1249     FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
1250     if (visibleItems.count()) {
1251         qreal endPos = lastPosition();
1252         if (findLastVisibleIndex() == model->count()-1) {
1253             listItem->setPosition(endPos);
1254         } else {
1255             qreal visiblePos = position() + q->height();
1256             if (endPos <= visiblePos || listItem->position() < endPos)
1257                 listItem->setPosition(endPos);
1258         }
1259     } else {
1260         listItem->setPosition(visiblePos);
1261     }
1262
1263     if (created)
1264         emit q->footerItemChanged();
1265 }
1266
1267 void QQuickListViewPrivate::updateHeader()
1268 {
1269     Q_Q(QQuickListView);
1270     bool created = false;
1271     if (!header) {
1272         QQuickItem *item = createComponentItem(headerComponent, true);
1273         if (!item)
1274             return;
1275         item->setZ(1);
1276         header = new FxListItemSG(item, q, true);
1277         created = true;
1278     }
1279
1280     FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
1281     if (listItem) {
1282         if (visibleItems.count()) {
1283             qreal startPos = originPosition();
1284             if (visibleIndex == 0) {
1285                 listItem->setPosition(startPos - headerSize());
1286             } else {
1287                 if (position() <= startPos || listItem->position() > startPos - headerSize())
1288                     listItem->setPosition(startPos - headerSize());
1289             }
1290         } else {
1291             listItem->setPosition(-headerSize());
1292         }
1293     }
1294
1295     if (created)
1296         emit q->headerItemChanged();
1297 }
1298
1299 void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1300 {
1301     Q_Q(QQuickListView);
1302     QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1303     if (!q->isComponentComplete())
1304         return;
1305     if (item != contentItem && (!highlight || item != highlight->item)) {
1306         if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height())
1307             || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
1308             forceLayout = true;
1309             q->polish();
1310         }
1311     }
1312 }
1313
1314 void QQuickListViewPrivate::fixupPosition()
1315 {
1316     if ((haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange)
1317         || snapMode != QQuickListView::NoSnap)
1318         moveReason = Other;
1319     if (orient == QQuickListView::Vertical)
1320         fixupY();
1321     else
1322         fixupX();
1323 }
1324
1325 void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1326 {
1327     if ((orient == QQuickListView::Horizontal && &data == &vData)
1328         || (orient == QQuickListView::Vertical && &data == &hData))
1329         return;
1330
1331     correctFlick = false;
1332     fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1333     bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
1334
1335     qreal viewPos = isRightToLeft() ? -position()-size() : position();
1336
1337     if (snapMode != QQuickListView::NoSnap && moveReason != QQuickListViewPrivate::SetIndex) {
1338         qreal tempPosition = isRightToLeft() ? -position()-size() : position();
1339         if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
1340             // if we've been dragged < averageSize/2 then bias towards the next item
1341             qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1342             qreal bias = 0;
1343             if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
1344                 bias = averageSize/2;
1345             else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2)
1346                 bias = -averageSize/2;
1347             if (isRightToLeft())
1348                 bias = -bias;
1349             tempPosition -= bias;
1350         }
1351         FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
1352         if (!topItem && strictHighlightRange && currentItem) {
1353             // StrictlyEnforceRange always keeps an item in range
1354             updateHighlight();
1355             topItem = currentItem;
1356         }
1357         FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
1358         if (!bottomItem && strictHighlightRange && currentItem) {
1359             // StrictlyEnforceRange always keeps an item in range
1360             updateHighlight();
1361             bottomItem = currentItem;
1362         }
1363         qreal pos;
1364         bool isInBounds = -position() > maxExtent && -position() <= minExtent;
1365         if (topItem && (isInBounds || strictHighlightRange)) {
1366             if (topItem->index == 0 && header && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) {
1367                 pos = isRightToLeft() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart;
1368             } else {
1369                 if (isRightToLeft())
1370                     pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
1371                 else
1372                     pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
1373             }
1374         } else if (bottomItem && isInBounds) {
1375             if (isRightToLeft())
1376                 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
1377             else
1378                 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
1379         } else {
1380             QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1381             return;
1382         }
1383
1384         qreal dist = qAbs(data.move + pos);
1385         if (dist > 0) {
1386             timeline.reset(data.move);
1387             if (fixupMode != Immediate) {
1388                 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1389                 data.fixingUp = true;
1390             } else {
1391                 timeline.set(data.move, -pos);
1392             }
1393             vTime = timeline.time();
1394         }
1395     } else if (currentItem && strictHighlightRange && moveReason != QQuickListViewPrivate::SetIndex) {
1396         updateHighlight();
1397         qreal pos = static_cast<FxListItemSG*>(currentItem)->itemPosition();
1398         if (viewPos < pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd)
1399             viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd;
1400         if (viewPos > pos - highlightRangeStart)
1401             viewPos = pos - highlightRangeStart;
1402         if (isRightToLeft())
1403             viewPos = -viewPos-size();
1404
1405         timeline.reset(data.move);
1406         if (viewPos != position()) {
1407             if (fixupMode != Immediate) {
1408                 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1409                 data.fixingUp = true;
1410             } else {
1411                 timeline.set(data.move, -viewPos);
1412             }
1413         }
1414         vTime = timeline.time();
1415     } else {
1416         QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1417     }
1418     data.inOvershoot = false;
1419     fixupMode = Normal;
1420 }
1421
1422 void QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1423                                         QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
1424 {
1425     Q_Q(QQuickListView);
1426
1427     data.fixingUp = false;
1428     moveReason = Mouse;
1429     if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) {
1430         correctFlick = true;
1431         QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1432         return;
1433     }
1434     qreal maxDistance = 0;
1435     qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
1436
1437     // -ve velocity means list is moving up/left
1438     if (velocity > 0) {
1439         if (data.move.value() < minExtent) {
1440             if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1441                 // if we've been dragged < averageSize/2 then bias towards the next item
1442                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1443                 qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
1444                 if (isRightToLeft())
1445                     bias = -bias;
1446                 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart;
1447                 maxDistance = qAbs(data.flickTarget - data.move.value());
1448                 velocity = maxVelocity;
1449             } else {
1450                 maxDistance = qAbs(minExtent - data.move.value());
1451             }
1452         }
1453         if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1454             data.flickTarget = minExtent;
1455     } else {
1456         if (data.move.value() > maxExtent) {
1457             if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1458                 // if we've been dragged < averageSize/2 then bias towards the next item
1459                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1460                 qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
1461                 if (isRightToLeft())
1462                     bias = -bias;
1463                 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart;
1464                 maxDistance = qAbs(data.flickTarget - data.move.value());
1465                 velocity = -maxVelocity;
1466             } else {
1467                 maxDistance = qAbs(maxExtent - data.move.value());
1468             }
1469         }
1470         if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1471             data.flickTarget = maxExtent;
1472     }
1473     bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds;
1474     if (maxDistance > 0 || overShoot) {
1475         // These modes require the list to stop exactly on an item boundary.
1476         // The initial flick will estimate the boundary to stop on.
1477         // Since list items can have variable sizes, the boundary will be
1478         // reevaluated and adjusted as we approach the boundary.
1479         qreal v = velocity;
1480         if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1481             if (v < 0)
1482                 v = -maxVelocity;
1483             else
1484                 v = maxVelocity;
1485         }
1486         if (!hData.flicking && !vData.flicking) {
1487             // the initial flick - estimate boundary
1488             qreal accel = deceleration;
1489             qreal v2 = v * v;
1490             overshootDist = 0.0;
1491             // + averageSize/4 to encourage moving at least one item in the flick direction
1492             qreal dist = v2 / (accel * 2.0) + averageSize/4;
1493             if (maxDistance > 0)
1494                 dist = qMin(dist, maxDistance);
1495             if (v > 0)
1496                 dist = -dist;
1497             if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickListView::SnapOneItem) {
1498                 if (snapMode != QQuickListView::SnapOneItem) {
1499                     qreal distTemp = isRightToLeft() ? -dist : dist;
1500                     data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart;
1501                 }
1502                 data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1503                 if (overShoot) {
1504                     if (data.flickTarget >= minExtent) {
1505                         overshootDist = overShootDistance(vSize);
1506                         data.flickTarget += overshootDist;
1507                     } else if (data.flickTarget <= maxExtent) {
1508                         overshootDist = overShootDistance(vSize);
1509                         data.flickTarget -= overshootDist;
1510                     }
1511                 }
1512                 qreal adjDist = -data.flickTarget + data.move.value();
1513                 if (qAbs(adjDist) > qAbs(dist)) {
1514                     // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1515                     qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1516                     if (adjv2 > v2) {
1517                         v2 = adjv2;
1518                         v = qSqrt(v2);
1519                         if (dist > 0)
1520                             v = -v;
1521                     }
1522                 }
1523                 dist = adjDist;
1524                 accel = v2 / (2.0f * qAbs(dist));
1525             } else if (overShoot) {
1526                 data.flickTarget = data.move.value() - dist;
1527                 if (data.flickTarget >= minExtent) {
1528                     overshootDist = overShootDistance(vSize);
1529                     data.flickTarget += overshootDist;
1530                 } else if (data.flickTarget <= maxExtent) {
1531                     overshootDist = overShootDistance(vSize);
1532                     data.flickTarget -= overshootDist;
1533                 }
1534             }
1535             timeline.reset(data.move);
1536             timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1537             timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1538             if (!hData.flicking && q->xflick()) {
1539                 hData.flicking = true;
1540                 emit q->flickingChanged();
1541                 emit q->flickingHorizontallyChanged();
1542                 emit q->flickStarted();
1543             }
1544             if (!vData.flicking && q->yflick()) {
1545                 vData.flicking = true;
1546                 emit q->flickingChanged();
1547                 emit q->flickingVerticallyChanged();
1548                 emit q->flickStarted();
1549             }
1550             correctFlick = true;
1551         } else {
1552             // reevaluate the target boundary.
1553             qreal newtarget = data.flickTarget;
1554             if (snapMode != QQuickListView::NoSnap || highlightRange == QQuickListView::StrictlyEnforceRange) {
1555                 qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1556                 newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart;
1557                 newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
1558             }
1559             if (velocity < 0 && newtarget <= maxExtent)
1560                 newtarget = maxExtent - overshootDist;
1561             else if (velocity > 0 && newtarget >= minExtent)
1562                 newtarget = minExtent + overshootDist;
1563             if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1564                 if (qAbs(velocity) < MinimumFlickVelocity)
1565                     correctFlick = false;
1566                 return;
1567             }
1568             data.flickTarget = newtarget;
1569             qreal dist = -newtarget + data.move.value();
1570             if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1571                 correctFlick = false;
1572                 timeline.reset(data.move);
1573                 fixup(data, minExtent, maxExtent);
1574                 return;
1575             }
1576             timeline.reset(data.move);
1577             timeline.accelDistance(data.move, v, -dist);
1578             timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1579         }
1580     } else {
1581         correctFlick = false;
1582         timeline.reset(data.move);
1583         fixup(data, minExtent, maxExtent);
1584     }
1585 }
1586
1587 //----------------------------------------------------------------------------
1588
1589 /*!
1590     \qmlclass ListView QQuickListView
1591     \inqmlmodule QtQuick 2
1592     \ingroup qml-view-elements
1593     \inherits Flickable
1594     \brief The ListView item provides a list view of items provided by a model.
1595
1596     A ListView displays data from models created from built-in QML elements like ListModel
1597     and XmlListModel, or custom model classes defined in C++ that inherit from
1598     QAbstractListModel.
1599
1600     A ListView has a \l model, which defines the data to be displayed, and
1601     a \l delegate, which defines how the data should be displayed. Items in a
1602     ListView are laid out horizontally or vertically. List views are inherently
1603     flickable because ListView inherits from \l Flickable.
1604
1605     \section1 Example Usage
1606
1607     The following example shows the definition of a simple list model defined
1608     in a file called \c ContactModel.qml:
1609
1610     \snippet doc/src/snippets/qml/listview/ContactModel.qml 0
1611
1612     Another component can display this model data in a ListView, like this:
1613
1614     \snippet doc/src/snippets/qml/listview/listview.qml import
1615     \codeline
1616     \snippet doc/src/snippets/qml/listview/listview.qml classdocs simple
1617
1618     \image listview-simple.png
1619
1620     Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1621     for its delegate. The view will create a new \l Text component for each item in the model. Notice
1622     the delegate is able to access the model's \c name and \c number data directly.
1623
1624     An improved list view is shown below. The delegate is visually improved and is moved
1625     into a separate \c contactDelegate component.
1626
1627     \snippet doc/src/snippets/qml/listview/listview.qml classdocs advanced
1628     \image listview-highlight.png
1629
1630     The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1631     and \c focus is set to \c true to enable keyboard navigation for the list view.
1632     The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1633
1634     Delegates are instantiated as needed and may be destroyed at any time.
1635     State should \e never be stored in a delegate.
1636
1637     ListView attaches a number of properties to the root item of the delegate, for example
1638     \c {ListView.isCurrentItem}.  In the following example, the root delegate item can access
1639     this attached property directly as \c ListView.isCurrentItem, while the child
1640     \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1641
1642     \snippet doc/src/snippets/qml/listview/listview.qml isCurrentItem
1643
1644     \note Views do not enable \e clip automatically.  If the view
1645     is not clipped by another item or the screen, it will be necessary
1646     to set \e {clip: true} in order to have the out of view items clipped
1647     nicely.
1648
1649     \sa {QML Data Models}, GridView, {declarative/modelviews/listview}{ListView examples}
1650 */
1651 QQuickListView::QQuickListView(QQuickItem *parent)
1652     : QQuickItemView(*(new QQuickListViewPrivate), parent)
1653 {
1654 }
1655
1656 QQuickListView::~QQuickListView()
1657 {
1658 }
1659
1660 /*!
1661     \qmlattachedproperty bool QtQuick2::ListView::isCurrentItem
1662     This attached property is true if this delegate is the current item; otherwise false.
1663
1664     It is attached to each instance of the delegate.
1665
1666     This property may be used to adjust the appearance of the current item, for example:
1667
1668     \snippet doc/src/snippets/qml/listview/listview.qml isCurrentItem
1669 */
1670
1671 /*!
1672     \qmlattachedproperty ListView QtQuick2::ListView::view
1673     This attached property holds the view that manages this delegate instance.
1674
1675     It is attached to each instance of the delegate.
1676 */
1677
1678 /*!
1679     \qmlattachedproperty string QtQuick2::ListView::previousSection
1680     This attached property holds the section of the previous element.
1681
1682     It is attached to each instance of the delegate.
1683
1684     The section is evaluated using the \l {ListView::section.property}{section} properties.
1685 */
1686
1687 /*!
1688     \qmlattachedproperty string QtQuick2::ListView::nextSection
1689     This attached property holds the section of the next element.
1690
1691     It is attached to each instance of the delegate.
1692
1693     The section is evaluated using the \l {ListView::section.property}{section} properties.
1694 */
1695
1696 /*!
1697     \qmlattachedproperty string QtQuick2::ListView::section
1698     This attached property holds the section of this element.
1699
1700     It is attached to each instance of the delegate.
1701
1702     The section is evaluated using the \l {ListView::section.property}{section} properties.
1703 */
1704
1705 /*!
1706     \qmlattachedproperty bool QtQuick2::ListView::delayRemove
1707
1708     This attached property holds whether the delegate may be destroyed. It
1709     is attached to each instance of the delegate. The default value is false.
1710
1711     It is sometimes necessary to delay the destruction of an item
1712     until an animation completes. The example delegate below ensures that the
1713     animation completes before the item is removed from the list.
1714
1715     \snippet doc/src/snippets/qml/listview/listview.qml delayRemove
1716
1717     If a \l remove transition has been specified, it will not be applied until
1718     delayRemove is returned to \c false.
1719 */
1720
1721 /*!
1722     \qmlattachedsignal QtQuick2::ListView::onAdd()
1723     This attached signal handler is called immediately after an item is added to the view.
1724
1725     If an \l add transition is specified, it is applied immediately after
1726     this signal handler is called.
1727 */
1728
1729 /*!
1730     \qmlattachedsignal QtQuick2::ListView::onRemove()
1731     This attached handler is called immediately before an item is removed from the view.
1732
1733     If a \l remove transition has been specified, it is applied after
1734     this signal handler is called, providing that delayRemove is false.
1735 */
1736
1737 /*!
1738     \qmlproperty model QtQuick2::ListView::model
1739     This property holds the model providing data for the list.
1740
1741     The model provides the set of data that is used to create the items
1742     in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1743     or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1744     used, it must be a subclass of \l QAbstractItemModel or a simple list.
1745
1746     \sa {qmlmodels}{Data Models}
1747 */
1748
1749 /*!
1750     \qmlproperty Component QtQuick2::ListView::delegate
1751
1752     The delegate provides a template defining each item instantiated by the view.
1753     The index is exposed as an accessible \c index property.  Properties of the
1754     model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1755
1756     The number of elements in the delegate has a direct effect on the
1757     flicking performance of the view.  If at all possible, place functionality
1758     that is not needed for the normal display of the delegate in a \l Loader which
1759     can load additional elements when needed.
1760
1761     The ListView will lay out the items based on the size of the root item
1762     in the delegate.
1763
1764     It is recommended that the delegate's size be a whole number to avoid sub-pixel
1765     alignment of items.
1766
1767     \note Delegates are instantiated as needed and may be destroyed at any time.
1768     State should \e never be stored in a delegate.
1769 */
1770 /*!
1771     \qmlproperty int QtQuick2::ListView::currentIndex
1772     \qmlproperty Item QtQuick2::ListView::currentItem
1773
1774     The \c currentIndex property holds the index of the current item, and
1775     \c currentItem holds the current item.   Setting the currentIndex to -1
1776     will clear the highlight and set currentItem to null.
1777
1778     If highlightFollowsCurrentItem is \c true, setting either of these
1779     properties will smoothly scroll the ListView so that the current
1780     item becomes visible.
1781
1782     Note that the position of the current item
1783     may only be approximate until it becomes visible in the view.
1784 */
1785
1786 /*!
1787   \qmlproperty Item QtQuick2::ListView::highlightItem
1788
1789     This holds the highlight item created from the \l highlight component.
1790
1791   The \c highlightItem is managed by the view unless
1792   \l highlightFollowsCurrentItem is set to false.
1793
1794   \sa highlight, highlightFollowsCurrentItem
1795 */
1796
1797 /*!
1798   \qmlproperty int QtQuick2::ListView::count
1799   This property holds the number of items in the view.
1800 */
1801
1802 /*!
1803     \qmlproperty Component QtQuick2::ListView::highlight
1804     This property holds the component to use as the highlight.
1805
1806     An instance of the highlight component is created for each list.
1807     The geometry of the resulting component instance is managed by the list
1808     so as to stay with the current item, unless the highlightFollowsCurrentItem
1809     property is false.
1810
1811     \sa highlightItem, highlightFollowsCurrentItem, {declarative/modelviews/listview}{ListView examples}
1812 */
1813
1814 /*!
1815     \qmlproperty bool QtQuick2::ListView::highlightFollowsCurrentItem
1816     This property holds whether the highlight is managed by the view.
1817
1818     If this property is true (the default value), the highlight is moved smoothly
1819     to follow the current item.  Otherwise, the
1820     highlight is not moved by the view, and any movement must be implemented
1821     by the highlight.
1822
1823     Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1824
1825     \snippet doc/src/snippets/qml/listview/listview.qml highlightFollowsCurrentItem
1826
1827     Note that the highlight animation also affects the way that the view
1828     is scrolled.  This is because the view moves to maintain the
1829     highlight within the preferred highlight range (or visible viewport).
1830
1831     \sa highlight, highlightMoveSpeed
1832 */
1833 //###Possibly rename these properties, since they are very useful even without a highlight?
1834 /*!
1835     \qmlproperty real QtQuick2::ListView::preferredHighlightBegin
1836     \qmlproperty real QtQuick2::ListView::preferredHighlightEnd
1837     \qmlproperty enumeration QtQuick2::ListView::highlightRangeMode
1838
1839     These properties define the preferred range of the highlight (for the current item)
1840     within the view. The \c preferredHighlightBegin value must be less than the
1841     \c preferredHighlightEnd value.
1842
1843     These properties affect the position of the current item when the list is scrolled.
1844     For example, if the currently selected item should stay in the middle of the
1845     list when the view is scrolled, set the \c preferredHighlightBegin and
1846     \c preferredHighlightEnd values to the top and bottom coordinates of where the middle
1847     item would be. If the \c currentItem is changed programmatically, the list will
1848     automatically scroll so that the current item is in the middle of the view.
1849     Furthermore, the behavior of the current item index will occur whether or not a
1850     highlight exists.
1851
1852     Valid values for \c highlightRangeMode are:
1853
1854     \list
1855     \li ListView.ApplyRange - the view attempts to maintain the highlight within the range.
1856        However, the highlight can move outside of the range at the ends of the list or due
1857        to mouse interaction.
1858     \li ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
1859        The current item changes if a keyboard or mouse action would cause the highlight to move
1860        outside of the range.
1861     \li ListView.NoHighlightRange - this is the default value.
1862     \endlist
1863 */
1864 void QQuickListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1865 {
1866     Q_D(QQuickListView);
1867     if (d->autoHighlight != autoHighlight) {
1868         if (!autoHighlight) {
1869             if (d->highlightPosAnimator)
1870                 d->highlightPosAnimator->stop();
1871             if (d->highlightSizeAnimator)
1872                 d->highlightSizeAnimator->stop();
1873         }
1874         QQuickItemView::setHighlightFollowsCurrentItem(autoHighlight);
1875     }
1876 }
1877
1878 /*!
1879     \qmlproperty real QtQuick2::ListView::spacing
1880
1881     This property holds the spacing between items.
1882
1883     The default value is 0.
1884 */
1885 qreal QQuickListView::spacing() const
1886 {
1887     Q_D(const QQuickListView);
1888     return d->spacing;
1889 }
1890
1891 void QQuickListView::setSpacing(qreal spacing)
1892 {
1893     Q_D(QQuickListView);
1894     if (spacing != d->spacing) {
1895         d->spacing = spacing;
1896         d->forceLayout = true;
1897         polish();
1898         emit spacingChanged();
1899     }
1900 }
1901
1902 /*!
1903     \qmlproperty enumeration QtQuick2::ListView::orientation
1904     This property holds the orientation of the list.
1905
1906     Possible values:
1907
1908     \list
1909     \li ListView.Horizontal - Items are laid out horizontally
1910     \li ListView.Vertical (default) - Items are laid out vertically
1911     \endlist
1912
1913     \table
1914     \row
1915     \li Horizontal orientation:
1916     \image ListViewHorizontal.png
1917
1918     \row
1919     \li Vertical orientation:
1920     \image listview-highlight.png
1921     \endtable
1922 */
1923 QQuickListView::Orientation QQuickListView::orientation() const
1924 {
1925     Q_D(const QQuickListView);
1926     return d->orient;
1927 }
1928
1929 void QQuickListView::setOrientation(QQuickListView::Orientation orientation)
1930 {
1931     Q_D(QQuickListView);
1932     if (d->orient != orientation) {
1933         d->orient = orientation;
1934         if (d->orient == Vertical) {
1935             setContentWidth(-1);
1936             setFlickableDirection(VerticalFlick);
1937             setContentX(0);
1938         } else {
1939             setContentHeight(-1);
1940             setFlickableDirection(HorizontalFlick);
1941             setContentY(0);
1942         }
1943         d->regenerate();
1944         emit orientationChanged();
1945     }
1946 }
1947
1948 /*!
1949   \qmlproperty enumeration QtQuick2::ListView::layoutDirection
1950   This property holds the layout direction of the horizontal list.
1951
1952   Possible values:
1953
1954   \list
1955   \li Qt.LeftToRight (default) - Items will be laid out from left to right.
1956   \li Qt.RightToLeft - Items will be laid out from right to let.
1957   \endlist
1958
1959   \sa ListView::effectiveLayoutDirection
1960 */
1961
1962
1963 /*!
1964     \qmlproperty enumeration QtQuick2::ListView::effectiveLayoutDirection
1965     This property holds the effective layout direction of the horizontal list.
1966
1967     When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
1968     the visual layout direction of the horizontal list will be mirrored. However, the
1969     property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
1970
1971     \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
1972 */
1973
1974 /*!
1975     \qmlproperty bool QtQuick2::ListView::keyNavigationWraps
1976     This property holds whether the list wraps key navigation.
1977
1978     If this is true, key navigation that would move the current item selection
1979     past the end of the list instead wraps around and moves the selection to
1980     the start of the list, and vice-versa.
1981
1982     By default, key navigation is not wrapped.
1983 */
1984
1985
1986 /*!
1987     \qmlproperty int QtQuick2::ListView::cacheBuffer
1988     This property determines whether delegates are retained outside the
1989     visible area of the view.
1990
1991     If this value is non-zero, the view may keep as many delegates
1992     instantiated as it can fit within the buffer specified.  For example,
1993     if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
1994     set to 40, then up to 2 delegates above and 2 delegates below the visible
1995     area may be created/retained.  The buffered delegates are created asynchronously,
1996     allowing creation to occur across multiple frames and reducing the
1997     likelihood of skipping frames.  In order to improve painting performance
1998     delegates outside the visible area have their \l visible property set to
1999     false until they are moved into the visible area.
2000
2001     Note that cacheBuffer is not a pixel buffer - it only maintains additional
2002     instantiated delegates.
2003
2004     Setting this value can improve the smoothness of scrolling behavior at the expense
2005     of additional memory usage.  It is not a substitute for creating efficient
2006     delegates; the fewer elements in a delegate, the faster a view can be
2007     scrolled.
2008 */
2009
2010
2011 /*!
2012     \qmlproperty string QtQuick2::ListView::section.property
2013     \qmlproperty enumeration QtQuick2::ListView::section.criteria
2014     \qmlproperty Component QtQuick2::ListView::section.delegate
2015     \qmlproperty enumeration QtQuick2::ListView::section.labelPositioning
2016
2017     These properties determine the expression to be evaluated and appearance
2018     of the section labels.
2019
2020     \c section.property holds the name of the property that is the basis
2021     of each section.
2022
2023     \c section.criteria holds the criteria for forming each section based on
2024     \c section.property. This value can be one of:
2025
2026     \list
2027     \li ViewSection.FullString (default) - sections are created based on the
2028     \c section.property value.
2029     \li ViewSection.FirstCharacter - sections are created based on the first
2030     character of the \c section.property value (for example, 'A', 'B', 'C'
2031     sections, etc. for an address book)
2032     \endlist
2033
2034     \c section.delegate holds the delegate component for each section.
2035
2036     \c section.labelPositioning determines whether the current and/or
2037     next section labels stick to the start/end of the view, and whether
2038     the labels are shown inline.  This value can be a combination of:
2039
2040     \list
2041     \li ViewSection.InlineLabels - section labels are shown inline between
2042     the item delegates separating sections (default).
2043     \li ViewSection.CurrentLabelAtStart - the current section label sticks to the
2044     start of the view as it is moved.
2045     \li ViewSection.NextLabelAtEnd - the next section label (beyond all visible
2046     sections) sticks to the end of the view as it is moved. \note Enabling
2047     \c ViewSection.NextLabelAtEnd requires the view to scan ahead for the next
2048     section, which has performance implications, especially for slower models.
2049     \endlist
2050
2051     Each item in the list has attached properties named \c ListView.section,
2052     \c ListView.previousSection and \c ListView.nextSection.
2053
2054     For example, here is a ListView that displays a list of animals, separated
2055     into sections. Each item in the ListView is placed in a different section
2056     depending on the "size" property of the model item. The \c sectionHeading
2057     delegate component provides the light blue bar that marks the beginning of
2058     each section.
2059
2060
2061     \snippet examples/declarative/modelviews/listview/sections.qml 0
2062
2063     \image qml-listview-sections-example.png
2064
2065     \note Adding sections to a ListView does not automatically re-order the
2066     list items by the section criteria.
2067     If the model is not ordered by section, then it is possible that
2068     the sections created will not be unique; each boundary between
2069     differing sections will result in a section header being created
2070     even if that section exists elsewhere.
2071
2072     \sa {declarative/modelviews/listview}{ListView examples}
2073 */
2074 QQuickViewSection *QQuickListView::sectionCriteria()
2075 {
2076     Q_D(QQuickListView);
2077     if (!d->sectionCriteria) {
2078         d->sectionCriteria = new QQuickViewSection(this);
2079         connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2080     }
2081     return d->sectionCriteria;
2082 }
2083
2084 /*!
2085     \qmlproperty string QtQuick2::ListView::currentSection
2086     This property holds the section that is currently at the beginning of the view.
2087 */
2088 QString QQuickListView::currentSection() const
2089 {
2090     Q_D(const QQuickListView);
2091     return d->currentSection;
2092 }
2093
2094 /*!
2095     \qmlproperty real QtQuick2::ListView::highlightMoveSpeed
2096     \qmlproperty int QtQuick2::ListView::highlightMoveDuration
2097     \qmlproperty real QtQuick2::ListView::highlightResizeSpeed
2098     \qmlproperty int QtQuick2::ListView::highlightResizeDuration
2099
2100     These properties hold the move and resize animation speed of the highlight delegate.
2101
2102     \l highlightFollowsCurrentItem must be true for these properties
2103     to have effect.
2104
2105     The default value for the speed properties is 400 pixels/second.
2106     The default value for the duration properties is -1, i.e. the
2107     highlight will take as much time as necessary to move at the set speed.
2108
2109     These properties have the same characteristics as a SmoothedAnimation.
2110
2111     \sa highlightFollowsCurrentItem
2112 */
2113 qreal QQuickListView::highlightMoveSpeed() const
2114 {
2115     Q_D(const QQuickListView);
2116     return d->highlightMoveSpeed;
2117 }
2118
2119 void QQuickListView::setHighlightMoveSpeed(qreal speed)
2120 {
2121     Q_D(QQuickListView);
2122     if (d->highlightMoveSpeed != speed) {
2123         d->highlightMoveSpeed = speed;
2124         if (d->highlightPosAnimator)
2125             d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
2126         emit highlightMoveSpeedChanged();
2127     }
2128 }
2129
2130 void QQuickListView::setHighlightMoveDuration(int duration)
2131 {
2132     Q_D(QQuickListView);
2133     if (d->highlightMoveDuration != duration) {
2134         if (d->highlightPosAnimator)
2135             d->highlightPosAnimator->userDuration = duration;
2136         QQuickItemView::setHighlightMoveDuration(duration);
2137     }
2138 }
2139
2140 qreal QQuickListView::highlightResizeSpeed() const
2141 {
2142     Q_D(const QQuickListView);
2143     return d->highlightResizeSpeed;
2144 }
2145
2146 void QQuickListView::setHighlightResizeSpeed(qreal speed)
2147 {
2148     Q_D(QQuickListView);
2149     if (d->highlightResizeSpeed != speed) {
2150         d->highlightResizeSpeed = speed;
2151         if (d->highlightSizeAnimator)
2152             d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
2153         emit highlightResizeSpeedChanged();
2154     }
2155 }
2156
2157 int QQuickListView::highlightResizeDuration() const
2158 {
2159     Q_D(const QQuickListView);
2160     return d->highlightResizeDuration;
2161 }
2162
2163 void QQuickListView::setHighlightResizeDuration(int duration)
2164 {
2165     Q_D(QQuickListView);
2166     if (d->highlightResizeDuration != duration) {
2167         d->highlightResizeDuration = duration;
2168         if (d->highlightSizeAnimator)
2169             d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2170         emit highlightResizeDurationChanged();
2171     }
2172 }
2173
2174 /*!
2175     \qmlproperty enumeration QtQuick2::ListView::snapMode
2176
2177     This property determines how the view scrolling will settle following a drag or flick.
2178     The possible values are:
2179
2180     \list
2181     \li ListView.NoSnap (default) - the view stops anywhere within the visible area.
2182     \li ListView.SnapToItem - the view settles with an item aligned with the start of
2183     the view.
2184     \li ListView.SnapOneItem - the view settles no more than one item away from the first
2185     visible item at the time the mouse button is released.  This mode is particularly
2186     useful for moving one page at a time.
2187     \endlist
2188
2189     \c snapMode does not affect the \l currentIndex.  To update the
2190     \l currentIndex as the list is moved, set \l highlightRangeMode
2191     to \c ListView.StrictlyEnforceRange.
2192
2193     \sa highlightRangeMode
2194 */
2195 QQuickListView::SnapMode QQuickListView::snapMode() const
2196 {
2197     Q_D(const QQuickListView);
2198     return d->snapMode;
2199 }
2200
2201 void QQuickListView::setSnapMode(SnapMode mode)
2202 {
2203     Q_D(QQuickListView);
2204     if (d->snapMode != mode) {
2205         d->snapMode = mode;
2206         emit snapModeChanged();
2207     }
2208 }
2209
2210
2211 /*!
2212     \qmlproperty Component QtQuick2::ListView::footer
2213     This property holds the component to use as the footer.
2214
2215     An instance of the footer component is created for each view.  The
2216     footer is positioned at the end of the view, after any items.
2217
2218     \sa header, footerItem
2219 */
2220
2221
2222 /*!
2223     \qmlproperty Component QtQuick2::ListView::header
2224     This property holds the component to use as the header.
2225
2226     An instance of the header component is created for each view.  The
2227     header is positioned at the beginning of the view, before any items.
2228
2229     \sa footer, headertem
2230 */
2231
2232 /*!
2233     \qmlproperty Item QtQuick2::ListView::headerItem
2234     This holds the header item created from the \l header component.
2235
2236     An instance of the header component is created for each view.  The
2237     header is positioned at the beginning of the view, before any items.
2238
2239     \sa header, footerItem
2240 */
2241
2242 /*!
2243     \qmlproperty Item QtQuick2::ListView::footerItem
2244     This holds the footer item created from the \l footer component.
2245
2246     An instance of the footer component is created for each view.  The
2247     footer is positioned at the end of the view, after any items.
2248
2249     \sa footer, headerItem
2250 */
2251
2252 /*!
2253     \qmlproperty Transition QtQuick2::ListView::populate
2254
2255     This property holds the transition to apply to the items that are initially created
2256     for a view.
2257
2258     It is applied to all items that are created when:
2259
2260     \list
2261     \li The view is first created
2262     \li The view's \l model changes
2263     \li The view's \l model is \l {QAbstractItemModel::reset}{reset}, if the model is a QAbstractItemModel subclass
2264     \endlist
2265
2266     For example, here is a view that specifies such a transition:
2267
2268     \code
2269     ListView {
2270         ...
2271         populate: Transition {
2272             NumberAnimation { properties: "x,y"; duration: 1000 }
2273         }
2274     }
2275     \endcode
2276
2277     When the view is initialized, the view will create all the necessary items for the view,
2278     then animate them to their correct positions within the view over one second.
2279
2280     For more details and examples on how to use view transitions, see the ViewTransition
2281     documentation.
2282
2283     \sa add, ViewTransition
2284 */
2285
2286 /*!
2287     \qmlproperty Transition QtQuick2::ListView::add
2288
2289     This property holds the transition to apply to items that are added to the view.
2290
2291     For example, here is a view that specifies such a transition:
2292
2293     \code
2294     ListView {
2295         ...
2296         add: Transition {
2297             NumberAnimation { properties: "x,y"; from: 100; duration: 1000 }
2298         }
2299     }
2300     \endcode
2301
2302     Whenever an item is added to the above view, the item will be animated from the position (100,100)
2303     to its final x,y position within the view, over one second. The transition only applies to
2304     the new items that are added to the view; it does not apply to the items below that are
2305     displaced by the addition of the new items. To animate the displaced items, set the \l displaced
2306     or \l addDisplaced properties.
2307
2308     For more details and examples on how to use view transitions, see the ViewTransition
2309     documentation.
2310
2311     \note This transition is not applied to the items that are created when the view is initially
2312     populated, or when the view's \l model changes. In those cases, the \l populate transition is
2313     applied instead.
2314
2315     \sa addDisplaced, populate, ViewTransition
2316 */
2317
2318 /*!
2319     \qmlproperty Transition QtQuick2::ListView::addDisplaced
2320
2321     This property holds the transition to apply to items within the view that are displaced by
2322     the addition of other items to the view.
2323
2324     For example, here is a view that specifies such a transition:
2325
2326     \code
2327     ListView {
2328         ...
2329         addDisplaced: Transition {
2330             NumberAnimation { properties: "x,y"; duration: 1000 }
2331         }
2332     }
2333     \endcode
2334
2335     Whenever an item is added to the above view, all items beneath the new item are displaced, causing
2336     them to move down (or sideways, if horizontally orientated) within the view. As this
2337     displacement occurs, the items' movement to their new x,y positions within the view will be
2338     animated by a NumberAnimation over one second, as specified. This transition is not applied to
2339     the new item that has been added to the view; to animate the added items, set the \l add
2340     property.
2341
2342     If an item is displaced by multiple types of operations at the same time, it is not defined as to
2343     whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2344     if it is not necessary to specify different transitions depending on whether an item is displaced
2345     by an add, move or remove operation, consider setting the \l displaced property instead.
2346
2347     For more details and examples on how to use view transitions, see the ViewTransition
2348     documentation.
2349
2350     \note This transition is not applied to the items that are created when the view is initially
2351     populated, or when the view's \l model changes. In those cases, the \l populate transition is
2352     applied instead.
2353
2354     \sa displaced, add, populate, ViewTransition
2355 */
2356
2357 /*!
2358     \qmlproperty Transition QtQuick2::ListView::move
2359
2360     This property holds the transition to apply to items in the view that are being moved due
2361     to a move operation in the view's \l model.
2362
2363     For example, here is a view that specifies such a transition:
2364
2365     \code
2366     ListView {
2367         ...
2368         move: Transition {
2369             NumberAnimation { properties: "x,y"; duration: 1000 }
2370         }
2371     }
2372     \endcode
2373
2374     Whenever the \l model performs a move operation to move a particular set of indexes, the
2375     respective items in the view will be animated to their new positions in the view over one
2376     second. The transition only applies to the items that are the subject of the move operation
2377     in the model; it does not apply to items below them that are displaced by the move operation.
2378     To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
2379
2380     For more details and examples on how to use view transitions, see the ViewTransition
2381     documentation.
2382
2383     \sa moveDisplaced, ViewTransition
2384 */
2385
2386 /*!
2387     \qmlproperty Transition QtQuick2::ListView::moveDisplaced
2388
2389     This property holds the transition to apply to items that are displaced by a move operation in
2390     the view's \l model.
2391
2392     For example, here is a view that specifies such a transition:
2393
2394     \code
2395     ListView {
2396         ...
2397         moveDisplaced: Transition {
2398             NumberAnimation { properties: "x,y"; duration: 1000 }
2399         }
2400     }
2401     \endcode
2402
2403     Whenever the \l model performs a move operation to move a particular set of indexes, the items
2404     between the source and destination indexes of the move operation are displaced, causing them
2405     to move upwards or downwards (or sideways, if horizontally orientated) within the view. As this
2406     displacement occurs, the items' movement to their new x,y positions within the view will be
2407     animated by a NumberAnimation over one second, as specified. This transition is not applied to
2408     the items that are the actual subjects of the move operation; to animate the moved items, set
2409     the \l move property.
2410
2411     If an item is displaced by multiple types of operations at the same time, it is not defined as to
2412     whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2413     if it is not necessary to specify different transitions depending on whether an item is displaced
2414     by an add, move or remove operation, consider setting the \l displaced property instead.
2415
2416     For more details and examples on how to use view transitions, see the ViewTransition
2417     documentation.
2418
2419     \sa displaced, move, ViewTransition
2420 */
2421
2422 /*!
2423     \qmlproperty Transition QtQuick2::ListView::remove
2424
2425     This property holds the transition to apply to items that are removed from the view.
2426
2427     For example, here is a view that specifies such a transition:
2428
2429     \code
2430     ListView {
2431         ...
2432         remove: Transition {
2433             ParallelAnimation {
2434                 NumberAnimation { property: "opacity"; to: 0; duration: 1000 }
2435                 NumberAnimation { properties: "x,y"; to: 100; duration: 1000 }
2436             }
2437         }
2438     }
2439     \endcode
2440
2441     Whenever an item is removed from the above view, the item will be animated to the position (100,100)
2442     over one second, and in parallel will also change its opacity to 0. The transition
2443     only applies to the items that are removed from the view; it does not apply to the items below
2444     them that are displaced by the removal of the items. To animate the displaced items, set the
2445     \l displaced or \l removeDisplaced properties.
2446
2447     Note that by the time the transition is applied, the item has already been removed from the
2448     model; any references to the model data for the removed index will not be valid.
2449
2450     Additionally, if the \l delayRemove attached property has been set for a delegate item, the
2451     remove transition will not be applied until \l delayRemove becomes false again.
2452
2453     For more details and examples on how to use view transitions, see the ViewTransition
2454     documentation.
2455
2456     \sa removeDisplaced, ViewTransition
2457 */
2458
2459 /*!
2460     \qmlproperty Transition QtQuick2::ListView::removeDisplaced
2461
2462     This property holds the transition to apply to items in the view that are displaced by the
2463     removal of other items in the view.
2464
2465     For example, here is a view that specifies such a transition:
2466
2467     \code
2468     ListView {
2469         ...
2470         removeDisplaced: Transition {
2471             NumberAnimation { properties: "x,y"; duration: 1000 }
2472         }
2473     }
2474     \endcode
2475
2476     Whenever an item is removed from the above view, all items beneath it are displaced, causing
2477     them to move upwards (or sideways, if horizontally orientated) within the view. As this
2478     displacement occurs, the items' movement to their new x,y positions within the view will be
2479     animated by a NumberAnimation over one second, as specified. This transition is not applied to
2480     the item that has actually been removed from the view; to animate the removed items, set the
2481     \l remove property.
2482
2483     If an item is displaced by multiple types of operations at the same time, it is not defined as to
2484     whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
2485     if it is not necessary to specify different transitions depending on whether an item is displaced
2486     by an add, move or remove operation, consider setting the \l displaced property instead.
2487
2488     For more details and examples on how to use view transitions, see the ViewTransition
2489     documentation.
2490
2491     \sa displaced, remove, ViewTransition
2492 */
2493
2494 /*!
2495     \qmlproperty Transition QtQuick2::ListView::displaced
2496     This property holds the generic transition to apply to items that have been displaced by
2497     any model operation that affects the view.
2498
2499     This is a convenience for specifying the generic transition to be applied to any items
2500     that are displaced by an add, move or remove operation, without having to specify the
2501     individual addDisplaced, moveDisplaced and removeDisplaced properties. For example, here
2502     is a view that specifies a displaced transition:
2503
2504     \code
2505     ListView {
2506         ...
2507         displaced: Transition {
2508             NumberAnimation { properties: "x,y"; duration: 1000 }
2509         }
2510     }
2511     \endcode
2512
2513     When any item is added, moved or removed within the above view, the items below it are
2514     displaced, causing them to move down (or sideways, if horizontally orientated) within the
2515     view. As this displacement occurs, the items' movement to their new x,y positions within
2516     the view will be animated by a NumberAnimation over one second, as specified.
2517
2518     If a view specifies this generic displaced transition as well as a specific addDisplaced,
2519     moveDisplaced or removeDisplaced transition, the more specific transition will be used
2520     instead of the generic displaced transition when the relevant operation occurs, providing that
2521     the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
2522     to false). If it has indeed been disabled, the generic displaced transition is applied instead.
2523
2524     For more details and examples on how to use view transitions, see the ViewTransition
2525     documentation.
2526
2527     \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
2528 */
2529
2530 void QQuickListView::viewportMoved()
2531 {
2532     Q_D(QQuickListView);
2533     QQuickItemView::viewportMoved();
2534     if (!d->itemCount)
2535         return;
2536     // Recursion can occur due to refill changing the content size.
2537     if (d->inViewportMoved)
2538         return;
2539     d->inViewportMoved = true;
2540
2541     if (yflick())
2542         d->bufferMode = d->vData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2543     else if (d->isRightToLeft())
2544         d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferAfter : QQuickListViewPrivate::BufferBefore;
2545     else
2546         d->bufferMode = d->hData.smoothVelocity < 0 ? QQuickListViewPrivate::BufferBefore : QQuickListViewPrivate::BufferAfter;
2547
2548     d->refill();
2549
2550     // Set visibility of items to eliminate cost of items outside the visible area.
2551     qreal from = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
2552     qreal to = d->isContentFlowReversed() ? -d->position() : d->position()+d->size();
2553     for (int i = 0; i < d->visibleItems.count(); ++i) {
2554         FxViewItem *item = static_cast<FxListItemSG*>(d->visibleItems.at(i));
2555         item->item->setVisible(item->endPosition() >= from && item->position() <= to);
2556     }
2557     if (d->currentItem)
2558         d->currentItem->item->setVisible(d->currentItem->endPosition() >= from && d->currentItem->position() <= to);
2559
2560     if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2561         d->moveReason = QQuickListViewPrivate::Mouse;
2562     if (d->moveReason != QQuickListViewPrivate::SetIndex) {
2563         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2564             // reposition highlight
2565             qreal pos = d->highlight->position();
2566             qreal viewPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
2567             if (pos > viewPos + d->highlightRangeEnd - d->highlight->size())
2568                 pos = viewPos + d->highlightRangeEnd - d->highlight->size();
2569             if (pos < viewPos + d->highlightRangeStart)
2570                 pos = viewPos + d->highlightRangeStart;
2571             if (pos != d->highlight->position()) {
2572                 d->highlightPosAnimator->stop();
2573                 static_cast<FxListItemSG*>(d->highlight)->setPosition(pos);
2574             } else {
2575                 d->updateHighlight();
2576             }
2577
2578             // update current index
2579             if (FxViewItem *snapItem = d->snapItemAt(d->highlight->position())) {
2580                 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2581                     d->updateCurrent(snapItem->index);
2582             }
2583         }
2584     }
2585
2586     if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
2587         d->inFlickCorrection = true;
2588         // Near an end and it seems that the extent has changed?
2589         // Recalculate the flick so that we don't end up in an odd position.
2590         if (yflick() && !d->vData.inOvershoot) {
2591             if (d->vData.velocity > 0) {
2592                 const qreal minY = minYExtent();
2593                 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2594                     && minY != d->vData.flickTarget)
2595                     d->flickY(-d->vData.smoothVelocity.value());
2596             } else if (d->vData.velocity < 0) {
2597                 const qreal maxY = maxYExtent();
2598                 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2599                     && maxY != d->vData.flickTarget)
2600                     d->flickY(-d->vData.smoothVelocity.value());
2601             }
2602         }
2603
2604         if (xflick() && !d->hData.inOvershoot) {
2605             if (d->hData.velocity > 0) {
2606                 const qreal minX = minXExtent();
2607                 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2608                     && minX != d->hData.flickTarget)
2609                     d->flickX(-d->hData.smoothVelocity.value());
2610             } else if (d->hData.velocity < 0) {
2611                 const qreal maxX = maxXExtent();
2612                 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2613                     && maxX != d->hData.flickTarget)
2614                     d->flickX(-d->hData.smoothVelocity.value());
2615             }
2616         }
2617         d->inFlickCorrection = false;
2618     }
2619     if (d->sectionCriteria) {
2620         d->updateCurrentSection();
2621         d->updateStickySections();
2622     }
2623     d->inViewportMoved = false;
2624 }
2625
2626 void QQuickListView::keyPressEvent(QKeyEvent *event)
2627 {
2628     Q_D(QQuickListView);
2629     if (d->model && d->model->count() && d->interactive) {
2630         if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2631                     || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2632                     || (d->orient == QQuickListView::Vertical && event->key() == Qt::Key_Up)) {
2633             if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2634                 decrementCurrentIndex();
2635                 event->accept();
2636                 return;
2637             } else if (d->wrap) {
2638                 event->accept();
2639                 return;
2640             }
2641         } else if ((d->orient == QQuickListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2642                     || (d->orient == QQuickListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2643                     || (d->orient == QQuickListView::Vertical && event->key() == Qt::Key_Down)) {
2644             if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2645                 incrementCurrentIndex();
2646                 event->accept();
2647                 return;
2648             } else if (d->wrap) {
2649                 event->accept();
2650                 return;
2651             }
2652         }
2653     }
2654     event->ignore();
2655     QQuickItemView::keyPressEvent(event);
2656 }
2657
2658 void QQuickListView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
2659 {
2660     Q_D(QQuickListView);
2661     if (d->isRightToLeft() && d->orient == QQuickListView::Horizontal) {
2662         // maintain position relative to the right edge
2663         int dx = newGeometry.width() - oldGeometry.width();
2664         setContentX(contentX() - dx);
2665     }
2666     QQuickItemView::geometryChanged(newGeometry, oldGeometry);
2667 }
2668
2669
2670 /*!
2671     \qmlmethod QtQuick2::ListView::incrementCurrentIndex()
2672
2673     Increments the current index.  The current index will wrap
2674     if keyNavigationWraps is true and it is currently at the end.
2675     This method has no effect if the \l count is zero.
2676
2677     \b Note: methods should only be called after the Component has completed.
2678 */
2679 void QQuickListView::incrementCurrentIndex()
2680 {
2681     Q_D(QQuickListView);
2682     int count = d->model ? d->model->count() : 0;
2683     if (count && (currentIndex() < count - 1 || d->wrap)) {
2684         d->moveReason = QQuickListViewPrivate::SetIndex;
2685         int index = currentIndex()+1;
2686         setCurrentIndex((index >= 0 && index < count) ? index : 0);
2687     }
2688 }
2689
2690 /*!
2691     \qmlmethod QtQuick2::ListView::decrementCurrentIndex()
2692
2693     Decrements the current index.  The current index will wrap
2694     if keyNavigationWraps is true and it is currently at the beginning.
2695     This method has no effect if the \l count is zero.
2696
2697     \b Note: methods should only be called after the Component has completed.
2698 */
2699 void QQuickListView::decrementCurrentIndex()
2700 {
2701     Q_D(QQuickListView);
2702     int count = d->model ? d->model->count() : 0;
2703     if (count && (currentIndex() > 0 || d->wrap)) {
2704         d->moveReason = QQuickListViewPrivate::SetIndex;
2705         int index = currentIndex()-1;
2706         setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2707     }
2708 }
2709
2710 void QQuickListView::updateSections()
2711 {
2712     Q_D(QQuickListView);
2713     if (isComponentComplete() && d->model) {
2714         QList<QByteArray> roles;
2715         if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
2716             roles << d->sectionCriteria->property().toUtf8();
2717         d->model->setWatchedRoles(roles);
2718         d->updateSections();
2719         if (d->itemCount) {
2720             d->forceLayout = true;
2721             polish();
2722         }
2723     }
2724 }
2725
2726 bool QQuickListViewPrivate::applyInsertionChange(const QQuickChangeSet::Insert &change, ChangeResult *insertResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView)
2727 {
2728     int modelIndex = change.index;
2729     int count = change.count;
2730
2731     qreal tempPos = isRightToLeft() ? -position()-size() : position();
2732     int index = visibleItems.count() ? mapFromModel(modelIndex) : 0;
2733
2734     if (index < 0) {
2735         int i = visibleItems.count() - 1;
2736         while (i > 0 && visibleItems.at(i)->index == -1)
2737             --i;
2738         if (i == 0 && visibleItems.first()->index == -1) {
2739             // there are no visible items except items marked for removal
2740             index = visibleItems.count();
2741         } else if (visibleItems.at(i)->index + 1 == modelIndex
2742             && visibleItems.at(i)->endPosition() <= buffer+tempPos+size()) {
2743             // Special case of appending an item to the model.
2744             index = visibleItems.count();
2745         } else {
2746             if (modelIndex < visibleIndex) {
2747                 // Insert before visible items
2748                 visibleIndex += count;
2749                 for (int i = 0; i < visibleItems.count(); ++i) {
2750                     FxViewItem *item = visibleItems.at(i);
2751                     if (item->index != -1 && item->index >= modelIndex)
2752                         item->index += count;
2753                 }
2754             }
2755             return true;
2756         }
2757     }
2758
2759     // index can be the next item past the end of the visible items list (i.e. appended)
2760     int pos = 0;
2761     if (visibleItems.count()) {
2762         pos = index < visibleItems.count() ? visibleItems.at(index)->position()
2763                                                 : visibleItems.last()->endPosition()+spacing;
2764     }
2765
2766     int prevVisibleCount = visibleItems.count();
2767     if (insertResult->visiblePos.isValid() && pos < insertResult->visiblePos) {
2768         // Insert items before the visible item.
2769         int insertionIdx = index;
2770         int i = 0;
2771         int from = tempPos - buffer;
2772
2773         for (i = count-1; i >= 0; --i) {
2774             if (pos > from && insertionIdx < visibleIndex) {
2775                 // item won't be visible, just note the size for repositioning
2776                 insertResult->sizeChangesBeforeVisiblePos += averageSize + spacing;
2777                 pos -= averageSize + spacing;
2778             } else {
2779                 // item is before first visible e.g. in cache buffer
2780                 FxViewItem *item = 0;
2781                 if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2782                     item->index = modelIndex + i;
2783                 if (!item)
2784                     item = createItem(modelIndex + i);
2785                 if (!item)
2786                     return false;
2787
2788                 visibleItems.insert(insertionIdx, item);
2789                 if (insertionIdx == 0)
2790                     insertResult->changedFirstItem = true;
2791                 if (!change.isMove()) {
2792                     addedItems->append(item);
2793                     item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2794                 }
2795                 insertResult->sizeChangesBeforeVisiblePos += item->size() + spacing;
2796                 pos -= item->size() + spacing;
2797             }
2798             index++;
2799         }
2800     } else {
2801         int i = 0;
2802         int to = buffer+tempPos+size();
2803         for (i = 0; i < count && pos <= to; ++i) {
2804             FxViewItem *item = 0;
2805             if (change.isMove() && (item = currentChanges.removedItems.take(change.moveKey(modelIndex + i))))
2806                 item->index = modelIndex + i;
2807             bool newItem = !item;
2808             if (!item)
2809                 item = createItem(modelIndex + i);
2810             if (!item)
2811                 return false;
2812
2813             visibleItems.insert(index, item);
2814             if (index == 0)
2815                 insertResult->changedFirstItem = true;
2816             if (change.isMove()) {
2817                 // we know this is a move target, since move displaced items that are
2818                 // shuffled into view due to a move would be added in refill()
2819                 if (newItem && transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true))
2820                     movingIntoView->append(MovedItem(item, change.moveKey(item->index)));
2821             } else {
2822                 addedItems->append(item);
2823                 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, true);
2824             }
2825             insertResult->sizeChangesAfterVisiblePos += item->size() + spacing;
2826             pos += item->size() + spacing;
2827             ++index;
2828         }
2829     }
2830
2831     for (; index < visibleItems.count(); ++index) {
2832         FxViewItem *item = visibleItems.at(index);
2833         if (item->index != -1) {
2834             item->index += count;
2835             if (change.isMove())
2836                 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2837             else
2838                 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::AddTransition, false);
2839         }
2840     }
2841
2842     updateVisibleIndex();
2843
2844     return visibleItems.count() > prevVisibleCount;
2845 }
2846
2847 void QQuickListViewPrivate::translateAndTransitionItemsAfter(int afterModelIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult)
2848 {
2849     Q_UNUSED(insertionResult);
2850
2851     if (!transitioner)
2852         return;
2853
2854     int markerItemIndex = -1;
2855     for (int i=0; i<visibleItems.count(); i++) {
2856         if (visibleItems[i]->index == afterModelIndex) {
2857             markerItemIndex = i;
2858             break;
2859         }
2860     }
2861     if (markerItemIndex < 0)
2862         return;
2863
2864     const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
2865     qreal sizeRemoved = -removalResult.sizeChangesAfterVisiblePos
2866             - (removalResult.countChangeAfterVisibleItems * (averageSize + spacing));
2867
2868     for (int i=markerItemIndex+1; i<visibleItems.count() && visibleItems.at(i)->position() < viewEndPos; i++) {
2869         FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems[i]);
2870         if (!listItem->transitionScheduledOrRunning()) {
2871             qreal pos = listItem->position();
2872             listItem->setPosition(pos - sizeRemoved);
2873             listItem->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2874             listItem->setPosition(pos);
2875         }
2876     }
2877 }
2878
2879 /*!
2880     \qmlmethod QtQuick2::ListView::positionViewAtIndex(int index, PositionMode mode)
2881
2882     Positions the view such that the \a index is at the position specified by
2883     \a mode:
2884
2885     \list
2886     \li ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
2887     \li ListView.Center - position item in the center of the view.
2888     \li ListView.End - position item at bottom (or right for horizontal orientation) of the view.
2889     \li ListView.Visible - if any part of the item is visible then take no action, otherwise
2890     bring the item into view.
2891     \li ListView.Contain - ensure the entire item is visible.  If the item is larger than
2892     the view the item is positioned at the top (or left for horizontal orientation) of the view.
2893     \endlist
2894
2895     If positioning the view at \a index would cause empty space to be displayed at
2896     the beginning or end of the view, the view will be positioned at the boundary.
2897
2898     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2899     at a particular index.  This is unreliable since removing items from the start
2900     of the list does not cause all other items to be repositioned, and because
2901     the actual start of the view can vary based on the size of the delegates.
2902     The correct way to bring an item into view is with \c positionViewAtIndex.
2903
2904     \b Note: methods should only be called after the Component has completed.  To position
2905     the view at startup, this method should be called by Component.onCompleted.  For
2906     example, to position the view at the end:
2907
2908     \code
2909     Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
2910     \endcode
2911 */
2912
2913 /*!
2914     \qmlmethod QtQuick2::ListView::positionViewAtBeginning()
2915     \qmlmethod QtQuick2::ListView::positionViewAtEnd()
2916
2917     Positions the view at the beginning or end, taking into account any header or footer.
2918
2919     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2920     at a particular index.  This is unreliable since removing items from the start
2921     of the list does not cause all other items to be repositioned, and because
2922     the actual start of the view can vary based on the size of the delegates.
2923
2924     \b Note: methods should only be called after the Component has completed.  To position
2925     the view at startup, this method should be called by Component.onCompleted.  For
2926     example, to position the view at the end on startup:
2927
2928     \code
2929     Component.onCompleted: positionViewAtEnd()
2930     \endcode
2931 */
2932
2933 /*!
2934     \qmlmethod int QtQuick2::ListView::indexAt(int x, int y)
2935
2936     Returns the index of the visible item containing the point \a x, \a y in content
2937     coordinates.  If there is no item at the point specified, or the item is
2938     not visible -1 is returned.
2939
2940     If the item is outside the visible area, -1 is returned, regardless of
2941     whether an item will exist at that point when scrolled into view.
2942
2943     \b Note: methods should only be called after the Component has completed.
2944 */
2945
2946 /*!
2947     \qmlmethod Item QtQuick2::ListView::itemAt(int x, int y)
2948
2949     Returns the visible item containing the point \a x, \a y in content
2950     coordinates.  If there is no item at the point specified, or the item is
2951     not visible null is returned.
2952
2953     If the item is outside the visible area, null is returned, regardless of
2954     whether an item will exist at that point when scrolled into view.
2955
2956     \b Note: methods should only be called after the Component has completed.
2957 */
2958
2959 QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj)
2960 {
2961     return new QQuickListViewAttached(obj);
2962 }
2963
2964 QT_END_NAMESPACE