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