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