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