Use \b instead of \bold
[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 "qplatformdefs.h"
55
56 QT_BEGIN_NAMESPACE
57
58 #ifndef QML_FLICK_SNAPONETHRESHOLD
59 #define QML_FLICK_SNAPONETHRESHOLD 30
60 #endif
61
62 //#define DEBUG_DELEGATE_LIFECYCLE
63
64 class FxListItemSG;
65
66 class QQuickListViewPrivate : public QQuickItemViewPrivate
67 {
68     Q_DECLARE_PUBLIC(QQuickListView)
69 public:
70     static QQuickListViewPrivate* get(QQuickListView *item) { return item->d_func(); }
71
72     virtual Qt::Orientation layoutOrientation() const;
73     virtual bool isContentFlowReversed() const;
74     bool isRightToLeft() const;
75     bool isBottomToTop() const;
76
77     virtual qreal positionAt(int index) const;
78     virtual qreal endPositionAt(int index) const;
79     virtual qreal originPosition() const;
80     virtual qreal lastPosition() const;
81
82     FxViewItem *itemBefore(int modelIndex) const;
83     QString sectionAt(int modelIndex);
84     qreal snapPosAt(qreal pos);
85     FxViewItem *snapItemAt(qreal pos);
86
87     virtual void init();
88     virtual void clear();
89
90     virtual bool addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer);
91     virtual bool removeNonVisibleItems(qreal bufferFrom, qreal bufferTo);
92     virtual void visibleItemsChanged();
93
94     virtual FxViewItem *newViewItem(int index, QQuickItem *item);
95     virtual void initializeViewItem(FxViewItem *item);
96     virtual bool releaseItem(FxViewItem *item);
97     virtual void repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer);
98     virtual void repositionPackageItemAt(QQuickItem *item, int index);
99     virtual void resetFirstItemPosition(qreal pos = 0.0);
100     virtual void adjustFirstItem(qreal forwards, qreal backwards, int);
101
102     virtual void createHighlight();
103     virtual void updateHighlight();
104     virtual void resetHighlightPosition();
105
106     virtual void setPosition(qreal pos);
107     virtual void layoutVisibleItems(int fromModelIndex = 0);
108
109     virtual bool applyInsertionChange(const QQuickChangeSet::Insert &insert, ChangeResult *changeResult, QList<FxViewItem *> *addedItems, QList<MovedItem> *movingIntoView);
110     virtual void translateAndTransitionItemsAfter(int afterIndex, const ChangeResult &insertionResult, const ChangeResult &removalResult);
111
112     virtual void updateSectionCriteria();
113     virtual void updateSections();
114     QQuickItem *getSectionItem(const QString &section);
115     void releaseSectionItem(QQuickItem *item);
116     void releaseSectionItems();
117     void updateInlineSection(FxListItemSG *);
118     void updateCurrentSection();
119     void updateStickySections();
120
121     virtual qreal headerSize() const;
122     virtual qreal footerSize() const;
123     virtual bool showHeaderForIndex(int index) const;
124     virtual bool showFooterForIndex(int index) const;
125     virtual void updateHeader();
126     virtual void updateFooter();
127
128     virtual void changedVisibleIndex(int newIndex);
129     virtual void initializeCurrentItem();
130
131     void updateAverage();
132
133     void itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry);
134     virtual void fixupPosition();
135     virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
136     virtual bool flick(QQuickItemViewPrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
137                         QQuickTimeLineCallback::Callback fixupCallback, qreal velocity);
138
139     QQuickListView::Orientation orient;
140     qreal visiblePos;
141     qreal averageSize;
142     qreal spacing;
143     QQuickListView::SnapMode snapMode;
144
145     QSmoothedAnimation *highlightPosAnimator;
146     QSmoothedAnimation *highlightSizeAnimator;
147     qreal highlightMoveVelocity;
148     qreal highlightResizeVelocity;
149     int highlightResizeDuration;
150
151     QQuickViewSection *sectionCriteria;
152     QString currentSection;
153     static const int sectionCacheSize = 5;
154     QQuickItem *sectionCache[sectionCacheSize];
155     QQuickItem *currentSectionItem;
156     QString currentStickySection;
157     QQuickItem *nextSectionItem;
158     QString nextStickySection;
159     QString lastVisibleSection;
160     QString nextSection;
161
162     qreal overshootDist;
163     bool correctFlick : 1;
164     bool inFlickCorrection : 1;
165
166     QQuickListViewPrivate()
167         : orient(QQuickListView::Vertical)
168         , visiblePos(0)
169         , averageSize(100.0), spacing(0.0)
170         , snapMode(QQuickListView::NoSnap)
171         , highlightPosAnimator(0), highlightSizeAnimator(0)
172         , highlightMoveVelocity(400), highlightResizeVelocity(400), highlightResizeDuration(-1)
173         , sectionCriteria(0), currentSectionItem(0), nextSectionItem(0)
174         , overshootDist(0.0), correctFlick(false), inFlickCorrection(false)
175     {}
176     ~QQuickListViewPrivate() {
177         delete highlightPosAnimator;
178         delete highlightSizeAnimator;
179     }
180
181     friend class QQuickViewSection;
182 };
183
184 //----------------------------------------------------------------------------
185
186 QQuickViewSection::QQuickViewSection(QQuickListView *parent)
187     : QObject(parent), m_criteria(FullString), m_delegate(0), m_labelPositioning(InlineLabels)
188     , m_view(parent ? QQuickListViewPrivate::get(parent) : 0)
189 {
190 }
191
192 void QQuickViewSection::setProperty(const QString &property)
193 {
194     if (property != m_property) {
195         m_property = property;
196         emit propertyChanged();
197         // notify view that the contents of the sections must be recalculated
198         m_view->updateSectionCriteria();
199     }
200 }
201
202 void QQuickViewSection::setCriteria(QQuickViewSection::SectionCriteria criteria)
203 {
204     if (criteria != m_criteria) {
205         m_criteria = criteria;
206         emit criteriaChanged();
207         // notify view that the contents of the sections must be recalculated
208         m_view->updateSectionCriteria();
209     }
210 }
211
212 void QQuickViewSection::setDelegate(QQmlComponent *delegate)
213 {
214     if (delegate != m_delegate) {
215         if (m_delegate)
216             m_view->releaseSectionItems();
217         m_delegate = delegate;
218         emit delegateChanged();
219         m_view->forceLayoutPolish();
220     }
221 }
222
223 QString QQuickViewSection::sectionString(const QString &value)
224 {
225     if (m_criteria == FirstCharacter)
226         return value.isEmpty() ? QString() : value.at(0);
227     else
228         return value;
229 }
230
231 void QQuickViewSection::setLabelPositioning(int l)
232 {
233     if (m_labelPositioning != l) {
234         m_labelPositioning = l;
235         emit labelPositioningChanged();
236         m_view->forceLayoutPolish();
237     }
238 }
239
240 //----------------------------------------------------------------------------
241
242 class FxListItemSG : public FxViewItem
243 {
244 public:
245     FxListItemSG(QQuickItem *i, QQuickListView *v, bool own, bool trackGeometry) : FxViewItem(i, own, trackGeometry), view(v) {
246         attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
247         if (trackGeometry) {
248             QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
249             itemPrivate->addItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
250         }
251     }
252
253     ~FxListItemSG() {
254         if (trackGeom) {
255             QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
256             itemPrivate->removeItemChangeListener(QQuickItemViewPrivate::get(view), QQuickItemPrivate::Geometry);
257         }
258     }
259
260     inline QQuickItem *section() const {
261         return attached ? static_cast<QQuickListViewAttached*>(attached)->m_sectionItem : 0;
262     }
263     void setSection(QQuickItem *s) {
264         if (!attached)
265             attached = static_cast<QQuickListViewAttached*>(qmlAttachedPropertiesObject<QQuickListView>(item));
266         static_cast<QQuickListViewAttached*>(attached)->m_sectionItem = s;
267     }
268
269     qreal position() const {
270         if (section()) {
271             if (view->orientation() == QQuickListView::Vertical)
272                 return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -section()->height()-section()->y() : section()->y());
273             else
274                 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section()->width()-section()->x() : section()->x());
275         } else {
276             return itemPosition();
277         }
278     }
279     qreal itemPosition() const {
280         if (view->orientation() == QQuickListView::Vertical)
281             return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop ? -item->height()-itemY() : itemY());
282         else
283             return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-itemX() : itemX());
284     }
285     qreal size() const {
286         if (section())
287             return (view->orientation() == QQuickListView::Vertical ? item->height()+section()->height() : item->width()+section()->width());
288         else
289             return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
290     }
291     qreal itemSize() const {
292         return (view->orientation() == QQuickListView::Vertical ? item->height() : item->width());
293     }
294     qreal sectionSize() const {
295         if (section())
296             return (view->orientation() == QQuickListView::Vertical ? section()->height() : section()->width());
297         return 0.0;
298     }
299     qreal endPosition() const {
300         if (view->orientation() == QQuickListView::Vertical) {
301             return (view->verticalLayoutDirection() == QQuickItemView::BottomToTop
302                     ? -itemY()
303                     : itemY() + item->height());
304         } else {
305             return (view->effectiveLayoutDirection() == Qt::RightToLeft
306                     ? -itemX()
307                     : itemX() + item->width());
308         }
309     }
310     void setPosition(qreal pos, bool immediate = false) {
311         // position the section immediately even if there is a transition
312         if (section()) {
313             if (view->orientation() == QQuickListView::Vertical) {
314                 if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop)
315                     section()->setY(-section()->height()-pos);
316                 else
317                     section()->setY(pos);
318             } else {
319                 if (view->effectiveLayoutDirection() == Qt::RightToLeft)
320                     section()->setX(-section()->width()-pos);
321                 else
322                     section()->setX(pos);
323             }
324         }
325         moveTo(pointForPosition(pos), immediate);
326     }
327     void setSize(qreal size) {
328         if (view->orientation() == QQuickListView::Vertical)
329             item->setHeight(size);
330         else
331             item->setWidth(size);
332     }
333     bool contains(qreal x, qreal y) const {
334         return (x >= itemX() && x < itemX() + item->width() &&
335                 y >= itemY() && y < itemY() + item->height());
336     }
337
338     QQuickListView *view;
339
340 private:
341     QPointF pointForPosition(qreal pos) const {
342         if (view->orientation() == QQuickListView::Vertical) {
343             if (view->verticalLayoutDirection() == QQuickItemView::BottomToTop) {
344                 if (section())
345                     pos += section()->height();
346                 return QPointF(itemX(), -item->height() - pos);
347             } else {
348                 if (section())
349                     pos += section()->height();
350                 return QPointF(itemX(), pos);
351             }
352         } else {
353             if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
354                 if (section())
355                     pos += section()->width();
356                 return QPointF(-item->width() - pos, itemY());
357             } else {
358                 if (section())
359                     pos += section()->width();
360                 return QPointF(pos, itemY());
361             }
362         }
363     }
364 };
365
366 //----------------------------------------------------------------------------
367
368 bool QQuickListViewPrivate::isContentFlowReversed() const
369 {
370     return isRightToLeft() || isBottomToTop();
371 }
372
373 Qt::Orientation QQuickListViewPrivate::layoutOrientation() const
374 {
375     return static_cast<Qt::Orientation>(orient);
376 }
377
378 bool QQuickListViewPrivate::isRightToLeft() const
379 {
380     Q_Q(const QQuickListView);
381     return orient == QQuickListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
382 }
383
384 bool QQuickListViewPrivate::isBottomToTop() const
385 {
386     return orient == QQuickListView::Vertical && verticalLayoutDirection == QQuickItemView::BottomToTop;
387 }
388
389 // Returns the item before modelIndex, if created.
390 // May return an item marked for removal.
391 FxViewItem *QQuickListViewPrivate::itemBefore(int modelIndex) const
392 {
393     if (modelIndex < visibleIndex)
394         return 0;
395     int idx = 1;
396     int lastIndex = -1;
397     while (idx < visibleItems.count()) {
398         FxViewItem *item = visibleItems.at(idx);
399         if (item->index != -1)
400             lastIndex = item->index;
401         if (item->index == modelIndex)
402             return visibleItems.at(idx-1);
403         ++idx;
404     }
405     if (lastIndex == modelIndex-1)
406         return visibleItems.last();
407     return 0;
408 }
409
410 void QQuickListViewPrivate::setPosition(qreal pos)
411 {
412     Q_Q(QQuickListView);
413     if (orient == QQuickListView::Vertical) {
414         if (isBottomToTop())
415             q->QQuickFlickable::setContentY(-pos-size());
416         else
417             q->QQuickFlickable::setContentY(pos);
418     } else {
419         if (isRightToLeft())
420             q->QQuickFlickable::setContentX(-pos-size());
421         else
422             q->QQuickFlickable::setContentX(pos);
423     }
424 }
425
426 qreal QQuickListViewPrivate::originPosition() const
427 {
428     qreal pos = 0;
429     if (!visibleItems.isEmpty()) {
430         pos = (*visibleItems.constBegin())->position();
431         if (visibleIndex > 0)
432             pos -= visibleIndex * (averageSize + spacing);
433     }
434     return pos;
435 }
436
437 qreal QQuickListViewPrivate::lastPosition() const
438 {
439     qreal pos = 0;
440     if (!visibleItems.isEmpty()) {
441         int invisibleCount = visibleItems.count() - visibleIndex;
442         for (int i = visibleItems.count()-1; i >= 0; --i) {
443             if (visibleItems.at(i)->index != -1) {
444                 invisibleCount = model->count() - visibleItems.at(i)->index - 1;
445                 break;
446             }
447         }
448         pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
449     } else if (model && model->count()) {
450         pos = (model->count() * averageSize + (model->count()-1) * spacing);
451     }
452     return pos;
453 }
454
455 qreal QQuickListViewPrivate::positionAt(int modelIndex) const
456 {
457     if (FxViewItem *item = visibleItem(modelIndex)) {
458         return item->position();
459     }
460     if (!visibleItems.isEmpty()) {
461         if (modelIndex < visibleIndex) {
462             int count = visibleIndex - modelIndex;
463             qreal cs = 0;
464             if (modelIndex == currentIndex && currentItem) {
465                 cs = currentItem->size() + spacing;
466                 --count;
467             }
468             return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
469         } else {
470             int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
471             return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing);
472         }
473     }
474     return 0;
475 }
476
477 qreal QQuickListViewPrivate::endPositionAt(int modelIndex) const
478 {
479     if (FxViewItem *item = visibleItem(modelIndex))
480         return item->endPosition();
481     if (!visibleItems.isEmpty()) {
482         if (modelIndex < visibleIndex) {
483             int count = visibleIndex - modelIndex;
484             return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing;
485         } else {
486             int count = modelIndex - findLastVisibleIndex(visibleIndex) - 1;
487             return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
488         }
489     }
490     return 0;
491 }
492
493 QString QQuickListViewPrivate::sectionAt(int modelIndex)
494 {
495     if (FxViewItem *item = visibleItem(modelIndex))
496         return item->attached->section();
497
498     QString section;
499     if (sectionCriteria) {
500         QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
501         section = sectionCriteria->sectionString(propValue);
502     }
503
504     return section;
505 }
506
507 qreal QQuickListViewPrivate::snapPosAt(qreal pos)
508 {
509     if (FxViewItem *snapItem = snapItemAt(pos))
510         return snapItem->position();
511     if (visibleItems.count()) {
512         qreal firstPos = (*visibleItems.constBegin())->position();
513         qreal endPos = (*(--visibleItems.constEnd()))->position();
514         if (pos < firstPos) {
515             return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
516         } else if (pos > endPos)
517             return endPos + qRound((pos - endPos) / averageSize) * averageSize;
518     }
519     return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
520 }
521
522 FxViewItem *QQuickListViewPrivate::snapItemAt(qreal pos)
523 {
524     FxViewItem *snapItem = 0;
525     qreal prevItemSize = 0;
526     for (int i = 0; i < visibleItems.count(); ++i) {
527         FxViewItem *item = visibleItems.at(i);
528         if (item->index == -1)
529             continue;
530         qreal itemTop = item->position();
531         if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size())
532             return item;
533         if (itemTop+item->size()/2 >= pos && itemTop-prevItemSize/2 < pos)
534             snapItem = item;
535         prevItemSize = item->size();
536     }
537     return snapItem;
538 }
539
540 void QQuickListViewPrivate::changedVisibleIndex(int newIndex)
541 {
542     visiblePos = positionAt(newIndex);
543     visibleIndex = newIndex;
544 }
545
546 void QQuickListViewPrivate::init()
547 {
548     QQuickItemViewPrivate::init();
549     ::memset(sectionCache, 0, sizeof(QQuickItem*) * sectionCacheSize);
550 }
551
552 void QQuickListViewPrivate::clear()
553 {
554     for (int i = 0; i < sectionCacheSize; ++i) {
555         delete sectionCache[i];
556         sectionCache[i] = 0;
557     }
558     visiblePos = 0;
559     releaseSectionItem(currentSectionItem);
560     currentSectionItem = 0;
561     releaseSectionItem(nextSectionItem);
562     nextSectionItem = 0;
563     lastVisibleSection = QString();
564     QQuickItemViewPrivate::clear();
565 }
566
567 FxViewItem *QQuickListViewPrivate::newViewItem(int modelIndex, QQuickItem *item)
568 {
569     Q_Q(QQuickListView);
570
571     FxListItemSG *listItem = new FxListItemSG(item, q, false, false);
572     listItem->index = modelIndex;
573
574     // initialise attached properties
575     if (sectionCriteria) {
576         QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
577         listItem->attached->setSection(sectionCriteria->sectionString(propValue));
578         if (modelIndex > 0) {
579             if (FxViewItem *item = itemBefore(modelIndex))
580                 listItem->attached->setPrevSection(item->attached->section());
581             else
582                 listItem->attached->setPrevSection(sectionAt(modelIndex-1));
583         }
584         if (modelIndex < model->count()-1) {
585             if (FxViewItem *item = visibleItem(modelIndex+1))
586                 listItem->attached->setNextSection(static_cast<QQuickListViewAttached*>(item->attached)->section());
587             else
588                 listItem->attached->setNextSection(sectionAt(modelIndex+1));
589         }
590     }
591
592     return listItem;
593 }
594
595 void QQuickListViewPrivate::initializeViewItem(FxViewItem *item)
596 {
597     QQuickItemViewPrivate::initializeViewItem(item);
598
599     QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
600     itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
601
602     if (sectionCriteria && sectionCriteria->delegate()) {
603         if (QString::compare(item->attached->m_prevSection, item->attached->m_section, Qt::CaseInsensitive))
604             updateInlineSection(static_cast<FxListItemSG*>(item));
605     }
606 }
607
608 bool QQuickListViewPrivate::releaseItem(FxViewItem *item)
609 {
610     if (!item || !model)
611         return true;
612
613     QQuickListViewAttached *att = static_cast<QQuickListViewAttached*>(item->attached);
614
615     bool released = QQuickItemViewPrivate::releaseItem(item);
616     if (released && att && att->m_sectionItem) {
617         // We hold no more references to this item
618         int i = 0;
619         do {
620             if (!sectionCache[i]) {
621                 sectionCache[i] = att->m_sectionItem;
622                 sectionCache[i]->setVisible(false);
623                 att->m_sectionItem = 0;
624                 break;
625             }
626             ++i;
627         } while (i < sectionCacheSize);
628         delete att->m_sectionItem;
629         att->m_sectionItem = 0;
630     }
631
632     return released;
633 }
634
635 bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal bufferFrom, qreal bufferTo, bool doBuffer)
636 {
637     qreal itemEnd = visiblePos;
638     if (visibleItems.count()) {
639         visiblePos = (*visibleItems.constBegin())->position();
640         itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
641     }
642
643     int modelIndex = findLastVisibleIndex();
644     bool haveValidItems = modelIndex >= 0;
645     modelIndex = modelIndex < 0 ? visibleIndex : modelIndex + 1;
646
647     if (haveValidItems && (bufferFrom > itemEnd+averageSize+spacing
648         || bufferTo < visiblePos - averageSize - spacing)) {
649         // We've jumped more than a page.  Estimate which items are now
650         // visible and fill from there.
651         int count = (fillFrom - itemEnd) / (averageSize + spacing);
652         int newModelIdx = qBound(0, modelIndex + count, model->count());
653         count = newModelIdx - modelIndex;
654         if (count) {
655             for (int i = 0; i < visibleItems.count(); ++i)
656                 releaseItem(visibleItems.at(i));
657             visibleItems.clear();
658             modelIndex = newModelIdx;
659             visibleIndex = modelIndex;
660             visiblePos = itemEnd + count * (averageSize + spacing);
661             itemEnd = visiblePos;
662         }
663     }
664
665     bool changed = false;
666     FxListItemSG *item = 0;
667     qreal pos = itemEnd;
668     while (modelIndex < model->count() && pos <= fillTo) {
669 #ifdef DEBUG_DELEGATE_LIFECYCLE
670         qDebug() << "refill: append item" << modelIndex << "pos" << pos << "buffer" << doBuffer;
671 #endif
672         if (!(item = static_cast<FxListItemSG*>(createItem(modelIndex, doBuffer))))
673             break;
674         if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
675             item->setPosition(pos, true);
676         QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
677         pos += item->size() + spacing;
678         visibleItems.append(item);
679         ++modelIndex;
680         changed = true;
681     }
682
683     if (doBuffer && requestedIndex != -1) // already waiting for an item
684         return changed;
685
686     while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos > fillFrom) {
687 #ifdef DEBUG_DELEGATE_LIFECYCLE
688         qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos << "buffer" << doBuffer;
689 #endif
690         if (!(item = static_cast<FxListItemSG*>(createItem(visibleIndex-1, doBuffer))))
691             break;
692         --visibleIndex;
693         visiblePos -= item->size() + spacing;
694         if (!transitioner || !transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) // pos will be set by layoutVisibleItems()
695             item->setPosition(visiblePos, true);
696         QQuickItemPrivate::get(item->item)->setCulled(doBuffer);
697         visibleItems.prepend(item);
698         changed = true;
699     }
700
701     return changed;
702 }
703
704 bool QQuickListViewPrivate::removeNonVisibleItems(qreal bufferFrom, qreal bufferTo)
705 {
706     FxViewItem *item = 0;
707     bool changed = false;
708
709     // Remove items from the start of the view.
710     // Zero-sized items shouldn't be removed unless a non-zero-sized item is also being
711     // removed, otherwise a zero-sized item is infinitely added and removed over and
712     // over by refill().
713     int index = 0;
714     while (visibleItems.count() > 1 && index < visibleItems.count()
715            && (item = visibleItems.at(index)) && item->endPosition() < bufferFrom) {
716         if (item->attached->delayRemove())
717             break;
718
719         if (item->size() > 0) {
720 #ifdef DEBUG_DELEGATE_LIFECYCLE
721             qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
722 #endif
723             // remove this item and all zero-sized items before it
724             while (item) {
725                 if (item->index != -1)
726                     visibleIndex++;
727                 visibleItems.removeAt(index);
728                 if (item->transitionScheduledOrRunning()) {
729 #ifdef DEBUG_DELEGATE_LIFECYCLE
730                     qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
731 #endif
732                     item->releaseAfterTransition = true;
733                     releasePendingTransition.append(item);
734                 } else {
735                     releaseItem(item);
736                 }
737                 if (index == 0)
738                     break;
739                 item = visibleItems.at(--index);
740             }
741             changed = true;
742         } else {
743             index++;
744         }
745     }
746
747     while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
748         if (item->attached->delayRemove())
749             break;
750 #ifdef DEBUG_DELEGATE_LIFECYCLE
751         qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
752 #endif
753         visibleItems.removeLast();
754         if (item->transitionScheduledOrRunning()) {
755 #ifdef DEBUG_DELEGATE_LIFECYCLE
756             qDebug() << "refill not releasing animating item" << item->index << item->item->objectName();
757 #endif
758             item->releaseAfterTransition = true;
759             releasePendingTransition.append(item);
760         } else {
761             releaseItem(item);
762         }
763         changed = true;
764     }
765
766     return changed;
767 }
768
769 void QQuickListViewPrivate::visibleItemsChanged()
770 {
771     if (visibleItems.count())
772         visiblePos = (*visibleItems.constBegin())->position();
773     updateAverage();
774     if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
775         static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
776         updateHighlight();
777     }
778     if (sectionCriteria)
779         updateCurrentSection();
780     updateUnrequestedPositions();
781 }
782
783 void QQuickListViewPrivate::layoutVisibleItems(int fromModelIndex)
784 {
785     if (!visibleItems.isEmpty()) {
786         const qreal from = isContentFlowReversed() ? -position() - size() : position();
787         const qreal to = isContentFlowReversed() ? -position() : position() + size();
788
789         FxViewItem *firstItem = *visibleItems.constBegin();
790         bool fixedCurrent = currentItem && firstItem->item == currentItem->item;
791         qreal sum = firstItem->size();
792         qreal pos = firstItem->position() + firstItem->size() + spacing;
793         firstItem->setVisible(firstItem->endPosition() >= from && firstItem->position() <= to);
794
795         for (int i=1; i < visibleItems.count(); ++i) {
796             FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.at(i));
797             if (item->index >= fromModelIndex) {
798                 item->setPosition(pos);
799                 item->setVisible(item->endPosition() >= from && item->position() <= to);
800             }
801             pos += item->size() + spacing;
802             sum += item->size();
803             fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
804         }
805         averageSize = qRound(sum / visibleItems.count());
806
807         // move current item if it is not a visible item.
808         if (currentIndex >= 0 && currentItem && !fixedCurrent)
809             static_cast<FxListItemSG*>(currentItem)->setPosition(positionAt(currentIndex));
810
811         updateCurrentSection();
812         updateStickySections();
813     }
814 }
815
816 void QQuickListViewPrivate::repositionItemAt(FxViewItem *item, int index, qreal sizeBuffer)
817 {
818     static_cast<FxListItemSG *>(item)->setPosition(positionAt(index) + sizeBuffer);
819 }
820
821 void QQuickListViewPrivate::repositionPackageItemAt(QQuickItem *item, int index)
822 {
823     Q_Q(QQuickListView);
824     qreal pos = position();
825     if (orient == QQuickListView::Vertical) {
826         if (item->y() + item->height() > pos && item->y() < pos + q->height()) {
827             if (isBottomToTop())
828                 item->setY(-positionAt(index)-item->height());
829             else
830                 item->setY(positionAt(index));
831         }
832     } else {
833         if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
834             if (isRightToLeft())
835                 item->setX(-positionAt(index)-item->width());
836             else
837                 item->setX(positionAt(index));
838         }
839     }
840 }
841
842 void QQuickListViewPrivate::resetFirstItemPosition(qreal pos)
843 {
844     FxListItemSG *item = static_cast<FxListItemSG*>(visibleItems.first());
845     item->setPosition(pos);
846 }
847
848 void QQuickListViewPrivate::adjustFirstItem(qreal forwards, qreal backwards, int)
849 {
850     if (!visibleItems.count())
851         return;
852     qreal diff = forwards - backwards;
853     static_cast<FxListItemSG*>(visibleItems.first())->setPosition(visibleItems.first()->position() + diff);
854 }
855
856 void QQuickListViewPrivate::createHighlight()
857 {
858     Q_Q(QQuickListView);
859     bool changed = false;
860     if (highlight) {
861         if (trackedItem == highlight)
862             trackedItem = 0;
863         delete highlight;
864         highlight = 0;
865
866         delete highlightPosAnimator;
867         delete highlightSizeAnimator;
868         highlightPosAnimator = 0;
869         highlightSizeAnimator = 0;
870
871         changed = true;
872     }
873
874     if (currentItem) {
875         QQuickItem *item = createHighlightItem();
876         if (item) {
877             FxListItemSG *newHighlight = new FxListItemSG(item, q, true, true);
878
879             if (autoHighlight) {
880                 newHighlight->setSize(static_cast<FxListItemSG*>(currentItem)->itemSize());
881                 newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
882             }
883             const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x");
884             highlightPosAnimator = new QSmoothedAnimation;
885             highlightPosAnimator->target = QQmlProperty(item, posProp);
886             highlightPosAnimator->velocity = highlightMoveVelocity;
887             highlightPosAnimator->userDuration = highlightMoveDuration;
888
889             const QLatin1String sizeProp(orient == QQuickListView::Vertical ? "height" : "width");
890             highlightSizeAnimator = new QSmoothedAnimation;
891             highlightSizeAnimator->velocity = highlightResizeVelocity;
892             highlightSizeAnimator->userDuration = highlightResizeDuration;
893             highlightSizeAnimator->target = QQmlProperty(item, sizeProp);
894
895             highlight = newHighlight;
896             changed = true;
897         }
898     }
899     if (changed)
900         emit q->highlightItemChanged();
901 }
902
903 void QQuickListViewPrivate::updateHighlight()
904 {
905     applyPendingChanges();
906
907     if ((!currentItem && highlight) || (currentItem && !highlight))
908         createHighlight();
909     bool strictHighlight = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
910     if (currentItem && autoHighlight && highlight && (!strictHighlight || !pressed)) {
911         // auto-update highlight
912         FxListItemSG *listItem = static_cast<FxListItemSG*>(currentItem);
913         highlightPosAnimator->to = isContentFlowReversed()
914                 ? -listItem->itemPosition()-listItem->itemSize()
915                 : listItem->itemPosition();
916         highlightSizeAnimator->to = listItem->itemSize();
917         if (orient == QQuickListView::Vertical) {
918             if (highlight->item->width() == 0)
919                 highlight->item->setWidth(currentItem->item->width());
920         } else {
921             if (highlight->item->height() == 0)
922                 highlight->item->setHeight(currentItem->item->height());
923         }
924
925         highlightPosAnimator->restart();
926         highlightSizeAnimator->restart();
927     }
928     updateTrackedItem();
929 }
930
931 void QQuickListViewPrivate::resetHighlightPosition()
932 {
933     if (highlight && currentItem)
934         static_cast<FxListItemSG*>(highlight)->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
935 }
936
937 QQuickItem * QQuickListViewPrivate::getSectionItem(const QString &section)
938 {
939     Q_Q(QQuickListView);
940     QQuickItem *sectionItem = 0;
941     int i = sectionCacheSize-1;
942     while (i >= 0 && !sectionCache[i])
943         --i;
944     if (i >= 0) {
945         sectionItem = sectionCache[i];
946         sectionCache[i] = 0;
947         sectionItem->setVisible(true);
948         QQmlContext *context = QQmlEngine::contextForObject(sectionItem)->parentContext();
949         context->setContextProperty(QLatin1String("section"), section);
950     } else {
951         QQmlContext *creationContext = sectionCriteria->delegate()->creationContext();
952         QQmlContext *context = new QQmlContext(
953                 creationContext ? creationContext : qmlContext(q));
954         context->setContextProperty(QLatin1String("section"), section);
955         QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
956         if (nobj) {
957             QQml_setParent_noEvent(context, nobj);
958             sectionItem = qobject_cast<QQuickItem *>(nobj);
959             if (!sectionItem) {
960                 delete nobj;
961             } else {
962                 sectionItem->setZ(2);
963                 QQml_setParent_noEvent(sectionItem, contentItem);
964                 sectionItem->setParentItem(contentItem);
965             }
966         } else {
967             delete context;
968         }
969         sectionCriteria->delegate()->completeCreate();
970     }
971
972     return sectionItem;
973 }
974
975 void QQuickListViewPrivate::releaseSectionItem(QQuickItem *item)
976 {
977     if (!item)
978         return;
979     int i = 0;
980     do {
981         if (!sectionCache[i]) {
982             sectionCache[i] = item;
983             sectionCache[i]->setVisible(false);
984             return;
985         }
986         ++i;
987     } while (i < sectionCacheSize);
988     delete item;
989 }
990
991
992 void QQuickListViewPrivate::releaseSectionItems()
993 {
994     for (int i = 0; i < visibleItems.count(); ++i) {
995         FxListItemSG *listItem = static_cast<FxListItemSG *>(visibleItems.at(i));
996         if (listItem->section()) {
997             qreal pos = listItem->position();
998             releaseSectionItem(listItem->section());
999             listItem->setSection(0);
1000             listItem->setPosition(pos);
1001         }
1002     }
1003     for (int i = 0; i < sectionCacheSize; ++i) {
1004         delete sectionCache[i];
1005         sectionCache[i] = 0;
1006     }
1007 }
1008
1009 void QQuickListViewPrivate::updateInlineSection(FxListItemSG *listItem)
1010 {
1011     if (!sectionCriteria || !sectionCriteria->delegate())
1012         return;
1013     if (QString::compare(listItem->attached->m_prevSection, listItem->attached->m_section, Qt::CaseInsensitive)
1014             && (sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels
1015                 || (listItem->index == 0 && sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart))) {
1016         if (!listItem->section()) {
1017             qreal pos = listItem->position();
1018             listItem->setSection(getSectionItem(listItem->attached->m_section));
1019             listItem->setPosition(pos);
1020         } else {
1021             QQmlContext *context = QQmlEngine::contextForObject(listItem->section())->parentContext();
1022             context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1023         }
1024     } else if (listItem->section()) {
1025         qreal pos = listItem->position();
1026         releaseSectionItem(listItem->section());
1027         listItem->setSection(0);
1028         listItem->setPosition(pos);
1029     }
1030 }
1031
1032 void QQuickListViewPrivate::updateStickySections()
1033 {
1034     if (!sectionCriteria
1035             || (!sectionCriteria->labelPositioning() && !currentSectionItem && !nextSectionItem))
1036         return;
1037
1038     bool isFlowReversed = isContentFlowReversed();
1039     qreal viewPos = isFlowReversed ? -position()-size() : position();
1040     QQuickItem *sectionItem = 0;
1041     QQuickItem *lastSectionItem = 0;
1042     int index = 0;
1043     while (index < visibleItems.count()) {
1044         if (QQuickItem *section = static_cast<FxListItemSG *>(visibleItems.at(index))->section()) {
1045             // Find the current section header and last visible section header
1046             // and hide them if they will overlap a static section header.
1047             qreal sectionPos = orient == QQuickListView::Vertical ? section->y() : section->x();
1048             qreal sectionSize = orient == QQuickListView::Vertical ? section->height() : section->width();
1049             bool visTop = true;
1050             if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart)
1051                 visTop = isFlowReversed ? -sectionPos-sectionSize >= viewPos : sectionPos >= viewPos;
1052             bool visBot = true;
1053             if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd)
1054                 visBot = isFlowReversed ? -sectionPos <= viewPos + size() : sectionPos + sectionSize < viewPos + size();
1055             section->setVisible(visBot && visTop);
1056             if (visTop && !sectionItem)
1057                 sectionItem = section;
1058             if (isFlowReversed) {
1059                if (-sectionPos <= viewPos + size())
1060                     lastSectionItem = section;
1061             } else {
1062                 if (sectionPos + sectionSize < viewPos + size())
1063                     lastSectionItem = section;
1064             }
1065         }
1066         ++index;
1067     }
1068
1069     // Current section header
1070     if (sectionCriteria->labelPositioning() & QQuickViewSection::CurrentLabelAtStart && isValid() && visibleItems.count()) {
1071         if (!currentSectionItem) {
1072             currentSectionItem = getSectionItem(currentSection);
1073         } else if (QString::compare(currentStickySection, currentSection, Qt::CaseInsensitive)) {
1074             QQmlContext *context = QQmlEngine::contextForObject(currentSectionItem)->parentContext();
1075             context->setContextProperty(QLatin1String("section"), currentSection);
1076         }
1077         currentStickySection = currentSection;
1078         if (!currentSectionItem)
1079             return;
1080
1081         qreal sectionSize = orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1082         bool atBeginning = orient == QQuickListView::Vertical ? (isBottomToTop() ? vData.atEnd : vData.atBeginning) : (isRightToLeft() ? hData.atEnd : hData.atBeginning);
1083
1084         currentSectionItem->setVisible(!atBeginning && (!header || header->endPosition() < viewPos));
1085         qreal pos = isFlowReversed ? position() + size() - sectionSize : viewPos;
1086         if (sectionItem) {
1087             qreal sectionPos = orient == QQuickListView::Vertical ? sectionItem->y() : sectionItem->x();
1088             pos = isFlowReversed ? qMax(pos, sectionPos + sectionSize) : qMin(pos, sectionPos - sectionSize);
1089         }
1090         if (header)
1091             pos = isFlowReversed ? qMin(header->endPosition(), pos) : qMax(header->endPosition(), pos);
1092         if (footer)
1093             pos = isFlowReversed ? qMax(-footer->position(), pos) : qMin(footer->position() - sectionSize, pos);
1094         if (orient == QQuickListView::Vertical)
1095             currentSectionItem->setY(pos);
1096         else
1097             currentSectionItem->setX(pos);
1098     } else if (currentSectionItem) {
1099         releaseSectionItem(currentSectionItem);
1100         currentSectionItem = 0;
1101     }
1102
1103     // Next section footer
1104     if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd && isValid() && visibleItems.count()) {
1105         if (!nextSectionItem) {
1106             nextSectionItem = getSectionItem(nextSection);
1107         } else if (QString::compare(nextStickySection, nextSection, Qt::CaseInsensitive)) {
1108             QQmlContext *context = QQmlEngine::contextForObject(nextSectionItem)->parentContext();
1109             context->setContextProperty(QLatin1String("section"), nextSection);
1110         }
1111         nextStickySection = nextSection;
1112         if (!nextSectionItem)
1113             return;
1114
1115         qreal sectionSize = orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1116         nextSectionItem->setVisible(!nextSection.isEmpty());
1117         qreal pos = isFlowReversed ? position() : viewPos + size() - sectionSize;
1118         if (lastSectionItem) {
1119             qreal sectionPos = orient == QQuickListView::Vertical ? lastSectionItem->y() : lastSectionItem->x();
1120             pos = isFlowReversed ? qMin(pos, sectionPos - sectionSize) : qMax(pos, sectionPos + sectionSize);
1121         }
1122         if (header)
1123             pos = isFlowReversed ? qMin(header->endPosition() - sectionSize, pos) : qMax(header->endPosition(), pos);
1124         if (orient == QQuickListView::Vertical)
1125             nextSectionItem->setY(pos);
1126         else
1127             nextSectionItem->setX(pos);
1128     } else if (nextSectionItem) {
1129         releaseSectionItem(nextSectionItem);
1130         nextSectionItem = 0;
1131     }
1132 }
1133
1134 void QQuickListViewPrivate::updateSections()
1135 {
1136     Q_Q(QQuickListView);
1137     if (!q->isComponentComplete())
1138         return;
1139
1140     QQuickItemViewPrivate::updateSections();
1141
1142     if (sectionCriteria && !visibleItems.isEmpty() && isValid()) {
1143         QString prevSection;
1144         if (visibleIndex > 0)
1145             prevSection = sectionAt(visibleIndex-1);
1146         QQuickListViewAttached *prevAtt = 0;
1147         int idx = -1;
1148         for (int i = 0; i < visibleItems.count(); ++i) {
1149             QQuickListViewAttached *attached = static_cast<QQuickListViewAttached*>(visibleItems.at(i)->attached);
1150             attached->setPrevSection(prevSection);
1151             if (visibleItems.at(i)->index != -1) {
1152                 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1153                 attached->setSection(sectionCriteria->sectionString(propValue));
1154                 idx = visibleItems.at(i)->index;
1155             }
1156             updateInlineSection(static_cast<FxListItemSG*>(visibleItems.at(i)));
1157             if (prevAtt)
1158                 prevAtt->setNextSection(attached->section());
1159             prevSection = attached->section();
1160             prevAtt = attached;
1161         }
1162         if (prevAtt) {
1163             if (idx > 0 && idx < model->count()-1)
1164                 prevAtt->setNextSection(sectionAt(idx+1));
1165             else
1166                 prevAtt->setNextSection(QString());
1167         }
1168     }
1169
1170     lastVisibleSection = QString();
1171 }
1172
1173 void QQuickListViewPrivate::updateCurrentSection()
1174 {
1175     Q_Q(QQuickListView);
1176     if (!sectionCriteria || visibleItems.isEmpty()) {
1177         if (!currentSection.isEmpty()) {
1178             currentSection.clear();
1179             emit q->currentSectionChanged();
1180         }
1181         return;
1182     }
1183     bool inlineSections = sectionCriteria->labelPositioning() & QQuickViewSection::InlineLabels;
1184     qreal sectionThreshold = position();
1185     if (currentSectionItem && !inlineSections)
1186         sectionThreshold += orient == QQuickListView::Vertical ? currentSectionItem->height() : currentSectionItem->width();
1187     int index = 0;
1188     int modelIndex = visibleIndex;
1189     while (index < visibleItems.count() && visibleItems.at(index)->endPosition() <= sectionThreshold) {
1190         if (visibleItems.at(index)->index != -1)
1191             modelIndex = visibleItems.at(index)->index;
1192         ++index;
1193     }
1194
1195     QString newSection = currentSection;
1196     if (index < visibleItems.count())
1197         newSection = visibleItems.at(index)->attached->section();
1198     else
1199         newSection = (*visibleItems.constBegin())->attached->section();
1200     if (newSection != currentSection) {
1201         currentSection = newSection;
1202         updateStickySections();
1203         emit q->currentSectionChanged();
1204     }
1205
1206     if (sectionCriteria->labelPositioning() & QQuickViewSection::NextLabelAtEnd) {
1207         // Don't want to scan for next section on every movement, so remember
1208         // the last section in the visible area and only scan for the next
1209         // section when that changes.  Clearing lastVisibleSection will also
1210         // force searching.
1211         QString lastSection = currentSection;
1212         qreal endPos = isContentFlowReversed() ? -position() : position() + size();
1213         if (nextSectionItem && !inlineSections)
1214             endPos -= orient == QQuickListView::Vertical ? nextSectionItem->height() : nextSectionItem->width();
1215         while (index < visibleItems.count() && static_cast<FxListItemSG*>(visibleItems.at(index))->itemPosition() < endPos) {
1216             if (visibleItems.at(index)->index != -1)
1217                 modelIndex = visibleItems.at(index)->index;
1218             lastSection = visibleItems.at(index)->attached->section();
1219             ++index;
1220         }
1221
1222         if (lastVisibleSection != lastSection) {
1223             nextSection = QString();
1224             lastVisibleSection = lastSection;
1225             for (int i = modelIndex; i < itemCount; ++i) {
1226                 QString section = sectionAt(i);
1227                 if (section != lastSection) {
1228                     nextSection = section;
1229                     updateStickySections();
1230                     break;
1231                 }
1232             }
1233         }
1234     }
1235 }
1236
1237 void QQuickListViewPrivate::initializeCurrentItem()
1238 {
1239     QQuickItemViewPrivate::initializeCurrentItem();
1240
1241     if (currentItem) {
1242         FxListItemSG *listItem = static_cast<FxListItemSG *>(currentItem);
1243
1244         // don't reposition the item if it is already in the visibleItems list
1245         FxViewItem *actualItem = visibleItem(currentIndex);
1246         if (!actualItem) {
1247             if (currentIndex == visibleIndex - 1 && visibleItems.count()) {
1248                 // We can calculate exact postion in this case
1249                 listItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1250             } else {
1251                 // Create current item now and position as best we can.
1252                 // Its position will be corrected when it becomes visible.
1253                 listItem->setPosition(positionAt(currentIndex));
1254             }
1255         }
1256
1257         if (visibleItems.isEmpty())
1258             averageSize = listItem->size();
1259     }
1260 }
1261
1262 void QQuickListViewPrivate::updateAverage()
1263 {
1264     if (!visibleItems.count())
1265         return;
1266     qreal sum = 0.0;
1267     for (int i = 0; i < visibleItems.count(); ++i)
1268         sum += visibleItems.at(i)->size();
1269     averageSize = qRound(sum / visibleItems.count());
1270 }
1271
1272 qreal QQuickListViewPrivate::headerSize() const
1273 {
1274     return header ? header->size() : 0.0;
1275 }
1276
1277 qreal QQuickListViewPrivate::footerSize() const
1278 {
1279     return footer ? footer->size() : 0.0;
1280 }
1281
1282 bool QQuickListViewPrivate::showHeaderForIndex(int index) const
1283 {
1284     return index == 0;
1285 }
1286
1287 bool QQuickListViewPrivate::showFooterForIndex(int index) const
1288 {
1289     return index == model->count()-1;
1290 }
1291
1292 void QQuickListViewPrivate::updateFooter()
1293 {
1294     Q_Q(QQuickListView);
1295     bool created = false;
1296     if (!footer) {
1297         QQuickItem *item = createComponentItem(footerComponent, 1.0);
1298         if (!item)
1299             return;
1300         footer = new FxListItemSG(item, q, true, true);
1301         created = true;
1302     }
1303
1304     FxListItemSG *listItem = static_cast<FxListItemSG*>(footer);
1305     if (visibleItems.count()) {
1306         qreal endPos = lastPosition();
1307         if (findLastVisibleIndex() == model->count()-1) {
1308             listItem->setPosition(endPos);
1309         } else {
1310             qreal visiblePos = position() + q->height();
1311             if (endPos <= visiblePos || listItem->position() < endPos)
1312                 listItem->setPosition(endPos);
1313         }
1314     } else {
1315         listItem->setPosition(visiblePos);
1316     }
1317
1318     if (created)
1319         emit q->footerItemChanged();
1320 }
1321
1322 void QQuickListViewPrivate::updateHeader()
1323 {
1324     Q_Q(QQuickListView);
1325     bool created = false;
1326     if (!header) {
1327         QQuickItem *item = createComponentItem(headerComponent, 1.0);
1328         if (!item)
1329             return;
1330         header = new FxListItemSG(item, q, true, true);
1331         created = true;
1332     }
1333
1334     FxListItemSG *listItem = static_cast<FxListItemSG*>(header);
1335     if (listItem) {
1336         if (visibleItems.count()) {
1337             qreal startPos = originPosition();
1338             if (visibleIndex == 0) {
1339                 listItem->setPosition(startPos - headerSize());
1340             } else {
1341                 if (position() <= startPos || listItem->position() > startPos - headerSize())
1342                     listItem->setPosition(startPos - headerSize());
1343             }
1344         } else {
1345             listItem->setPosition(-headerSize());
1346         }
1347     }
1348
1349     if (created)
1350         emit q->headerItemChanged();
1351 }
1352
1353 void QQuickListViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1354 {
1355     Q_Q(QQuickListView);
1356     QQuickItemViewPrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1357     if (!q->isComponentComplete())
1358         return;
1359
1360     if (item != contentItem && (!highlight || item != highlight->item)) {
1361         if ((orient == QQuickListView::Vertical && newGeometry.height() != oldGeometry.height())
1362             || (orient == QQuickListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
1363
1364             // if visibleItems.first() has resized, adjust its pos since it is used to
1365             // position all subsequent items
1366             if (visibleItems.count() && item == visibleItems.first()->item) {
1367                 FxListItemSG *listItem = static_cast<FxListItemSG*>(visibleItems.first());
1368                 if (orient == QQuickListView::Vertical) {
1369                     qreal diff = newGeometry.height() - oldGeometry.height();
1370                     if (verticalLayoutDirection == QQuickListView::TopToBottom && listItem->endPosition() < q->contentY())
1371                         listItem->setPosition(listItem->position() - diff, true);
1372                     else if (verticalLayoutDirection == QQuickListView::BottomToTop && listItem->endPosition() > q->contentY())
1373                         listItem->setPosition(listItem->position() + diff, true);
1374                 } else {
1375                     qreal diff = newGeometry.width() - oldGeometry.width();
1376                     if (q->effectiveLayoutDirection() == Qt::LeftToRight && listItem->endPosition() < q->contentX())
1377                         listItem->setPosition(listItem->position() - diff, true);
1378                     else if (q->effectiveLayoutDirection() == Qt::RightToLeft && listItem->endPosition() > q->contentX())
1379                         listItem->setPosition(listItem->position() + diff, true);
1380                 }
1381             }
1382             forceLayoutPolish();
1383         }
1384     }
1385 }
1386
1387 void QQuickListViewPrivate::fixupPosition()
1388 {
1389     if ((haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange)
1390         || snapMode != QQuickListView::NoSnap)
1391         moveReason = Other;
1392     if (orient == QQuickListView::Vertical)
1393         fixupY();
1394     else
1395         fixupX();
1396 }
1397
1398 void QQuickListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1399 {
1400     if ((orient == QQuickListView::Horizontal && &data == &vData)
1401         || (orient == QQuickListView::Vertical && &data == &hData))
1402         return;
1403
1404     correctFlick = false;
1405     fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1406     bool strictHighlightRange = haveHighlightRange && highlightRange == QQuickListView::StrictlyEnforceRange;
1407
1408     qreal viewPos = isContentFlowReversed() ? -position()-size() : position();
1409
1410     if (snapMode != QQuickListView::NoSnap && moveReason != QQuickListViewPrivate::SetIndex) {
1411         qreal tempPosition = isContentFlowReversed() ? -position()-size() : position();
1412         if (snapMode == QQuickListView::SnapOneItem && moveReason == Mouse) {
1413             // if we've been dragged < averageSize/2 then bias towards the next item
1414             qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1415             qreal bias = 0;
1416             if (data.velocity > 0 && dist > QML_FLICK_SNAPONETHRESHOLD && dist < averageSize/2)
1417                 bias = averageSize/2;
1418             else if (data.velocity < 0 && dist < -QML_FLICK_SNAPONETHRESHOLD && dist > -averageSize/2)
1419                 bias = -averageSize/2;
1420             if (isContentFlowReversed())
1421                 bias = -bias;
1422             tempPosition -= bias;
1423         }
1424         FxViewItem *topItem = snapItemAt(tempPosition+highlightRangeStart);
1425         if (!topItem && strictHighlightRange && currentItem) {
1426             // StrictlyEnforceRange always keeps an item in range
1427             updateHighlight();
1428             topItem = currentItem;
1429         }
1430         FxViewItem *bottomItem = snapItemAt(tempPosition+highlightRangeEnd);
1431         if (!bottomItem && strictHighlightRange && currentItem) {
1432             // StrictlyEnforceRange always keeps an item in range
1433             updateHighlight();
1434             bottomItem = currentItem;
1435         }
1436         qreal pos;
1437         bool isInBounds = -position() > maxExtent && -position() <= minExtent;
1438         if (topItem && (isInBounds || strictHighlightRange)) {
1439             if (topItem->index == 0 && header && tempPosition+highlightRangeStart < header->position()+header->size()/2 && !strictHighlightRange) {
1440                 pos = isContentFlowReversed() ? - header->position() + highlightRangeStart - size() : header->position() - highlightRangeStart;
1441             } else {
1442                 if (isContentFlowReversed())
1443                     pos = qMax(qMin(-topItem->position() + highlightRangeStart - size(), -maxExtent), -minExtent);
1444                 else
1445                     pos = qMax(qMin(topItem->position() - highlightRangeStart, -maxExtent), -minExtent);
1446             }
1447         } else if (bottomItem && isInBounds) {
1448             if (isContentFlowReversed())
1449                 pos = qMax(qMin(-bottomItem->position() + highlightRangeEnd - size(), -maxExtent), -minExtent);
1450             else
1451                 pos = qMax(qMin(bottomItem->position() - highlightRangeEnd, -maxExtent), -minExtent);
1452         } else {
1453             QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1454             return;
1455         }
1456
1457         qreal dist = qAbs(data.move + pos);
1458         if (dist > 0) {
1459             timeline.reset(data.move);
1460             if (fixupMode != Immediate) {
1461                 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1462                 data.fixingUp = true;
1463             } else {
1464                 timeline.set(data.move, -pos);
1465             }
1466             vTime = timeline.time();
1467         }
1468     } else if (currentItem && strictHighlightRange && moveReason != QQuickListViewPrivate::SetIndex) {
1469         updateHighlight();
1470         qreal pos = static_cast<FxListItemSG*>(currentItem)->itemPosition();
1471         if (viewPos < pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd)
1472             viewPos = pos + static_cast<FxListItemSG*>(currentItem)->itemSize() - highlightRangeEnd;
1473         if (viewPos > pos - highlightRangeStart)
1474             viewPos = pos - highlightRangeStart;
1475         if (isContentFlowReversed())
1476             viewPos = -viewPos-size();
1477
1478         timeline.reset(data.move);
1479         if (viewPos != position()) {
1480             if (fixupMode != Immediate) {
1481                 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1482                 data.fixingUp = true;
1483             } else {
1484                 timeline.set(data.move, -viewPos);
1485             }
1486         }
1487         vTime = timeline.time();
1488     } else {
1489         QQuickItemViewPrivate::fixup(data, minExtent, maxExtent);
1490     }
1491     data.inOvershoot = false;
1492     fixupMode = Normal;
1493 }
1494
1495 bool QQuickListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1496                                         QQuickTimeLineCallback::Callback fixupCallback, qreal velocity)
1497 {
1498     data.fixingUp = false;
1499     moveReason = Mouse;
1500     if ((!haveHighlightRange || highlightRange != QQuickListView::StrictlyEnforceRange) && snapMode == QQuickListView::NoSnap) {
1501         correctFlick = true;
1502         return QQuickItemViewPrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1503     }
1504     qreal maxDistance = 0;
1505     qreal dataValue = isContentFlowReversed() ? -data.move.value()+size() : data.move.value();
1506
1507     // -ve velocity means list is moving up/left
1508     if (velocity > 0) {
1509         if (data.move.value() < minExtent) {
1510             if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1511                 // if we've been dragged < averageSize/2 then bias towards the next item
1512                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1513                 qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
1514                 if (isContentFlowReversed())
1515                     bias = -bias;
1516                 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) - bias) + highlightRangeStart;
1517                 maxDistance = qAbs(data.flickTarget - data.move.value());
1518                 velocity = maxVelocity;
1519             } else {
1520                 maxDistance = qAbs(minExtent - data.move.value());
1521             }
1522         }
1523         if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1524             data.flickTarget = minExtent;
1525     } else {
1526         if (data.move.value() > maxExtent) {
1527             if (snapMode == QQuickListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1528                 // if we've been dragged < averageSize/2 then bias towards the next item
1529                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1530                 qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
1531                 if (isContentFlowReversed())
1532                     bias = -bias;
1533                 data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + bias) + highlightRangeStart;
1534                 maxDistance = qAbs(data.flickTarget - data.move.value());
1535                 velocity = -maxVelocity;
1536             } else {
1537                 maxDistance = qAbs(maxExtent - data.move.value());
1538             }
1539         }
1540         if (snapMode == QQuickListView::NoSnap && highlightRange != QQuickListView::StrictlyEnforceRange)
1541             data.flickTarget = maxExtent;
1542     }
1543     bool overShoot = boundsBehavior == QQuickFlickable::DragAndOvershootBounds;
1544     if (maxDistance > 0 || overShoot) {
1545         // These modes require the list to stop exactly on an item boundary.
1546         // The initial flick will estimate the boundary to stop on.
1547         // Since list items can have variable sizes, the boundary will be
1548         // reevaluated and adjusted as we approach the boundary.
1549         qreal v = velocity;
1550         if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1551             if (v < 0)
1552                 v = -maxVelocity;
1553             else
1554                 v = maxVelocity;
1555         }
1556         if (!hData.flicking && !vData.flicking) {
1557             // the initial flick - estimate boundary
1558             qreal accel = deceleration;
1559             qreal v2 = v * v;
1560             overshootDist = 0.0;
1561             // + averageSize/4 to encourage moving at least one item in the flick direction
1562             qreal dist = v2 / (accel * 2.0) + averageSize/4;
1563             if (maxDistance > 0)
1564                 dist = qMin(dist, maxDistance);
1565             if (v > 0)
1566                 dist = -dist;
1567             if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QQuickListView::SnapOneItem) {
1568                 if (snapMode != QQuickListView::SnapOneItem) {
1569                     qreal distTemp = isContentFlowReversed() ? -dist : dist;
1570                     data.flickTarget = -snapPosAt(-(dataValue - highlightRangeStart) + distTemp) + highlightRangeStart;
1571                 }
1572                 data.flickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1573                 if (overShoot) {
1574                     if (data.flickTarget >= minExtent) {
1575                         overshootDist = overShootDistance(vSize);
1576                         data.flickTarget += overshootDist;
1577                     } else if (data.flickTarget <= maxExtent) {
1578                         overshootDist = overShootDistance(vSize);
1579                         data.flickTarget -= overshootDist;
1580                     }
1581                 }
1582                 qreal adjDist = -data.flickTarget + data.move.value();
1583                 if (qAbs(adjDist) > qAbs(dist)) {
1584                     // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1585                     qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1586                     if (adjv2 > v2) {
1587                         v2 = adjv2;
1588                         v = qSqrt(v2);
1589                         if (dist > 0)
1590                             v = -v;
1591                     }
1592                 }
1593                 dist = adjDist;
1594                 accel = v2 / (2.0f * qAbs(dist));
1595             } else if (overShoot) {
1596                 data.flickTarget = data.move.value() - dist;
1597                 if (data.flickTarget >= minExtent) {
1598                     overshootDist = overShootDistance(vSize);
1599                     data.flickTarget += overshootDist;
1600                 } else if (data.flickTarget <= maxExtent) {
1601                     overshootDist = overShootDistance(vSize);
1602                     data.flickTarget -= overshootDist;
1603                 }
1604             }
1605             timeline.reset(data.move);
1606             timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1607             timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1608             correctFlick = true;
1609             return true;
1610         } else {
1611             // reevaluate the target boundary.
1612             qreal newtarget = data.flickTarget;
1613             if (snapMode != QQuickListView::NoSnap || highlightRange == QQuickListView::StrictlyEnforceRange) {
1614                 qreal tempFlickTarget = isContentFlowReversed() ? -data.flickTarget+size() : data.flickTarget;
1615                 newtarget = -snapPosAt(-(tempFlickTarget - highlightRangeStart)) + highlightRangeStart;
1616                 newtarget = isContentFlowReversed() ? -newtarget+size() : newtarget;
1617             }
1618             if (velocity < 0 && newtarget <= maxExtent)
1619                 newtarget = maxExtent - overshootDist;
1620             else if (velocity > 0 && newtarget >= minExtent)
1621                 newtarget = minExtent + overshootDist;
1622             if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1623                 if (qAbs(velocity) < MinimumFlickVelocity)
1624                     correctFlick = false;
1625                 return false;
1626             }
1627             data.flickTarget = newtarget;
1628             qreal dist = -newtarget + data.move.value();
1629             if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1630                 correctFlick = false;
1631                 timeline.reset(data.move);
1632                 fixup(data, minExtent, maxExtent);
1633                 return false;
1634             }
1635             timeline.reset(data.move);
1636             timeline.accelDistance(data.move, v, -dist);
1637             timeline.callback(QQuickTimeLineCallback(&data.move, fixupCallback, this));
1638             return false;
1639         }
1640     } else {
1641         correctFlick = false;
1642         timeline.reset(data.move);
1643         fixup(data, minExtent, maxExtent);
1644         return false;
1645     }
1646 }
1647
1648 //----------------------------------------------------------------------------
1649
1650 /*!
1651     \qmltype ListView
1652     \instantiates 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 types 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 item
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             \b 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             \b 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 objects and bindings 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 components 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 objects and bindings 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     \li ListView.SnapPosition - position the item at \l preferredHighlightBegin.  This mode
3036     is only valid if \l highlightRangeMode is StrictlyEnforceRange or snapping is enabled
3037     via \l snapMode.
3038     \endlist
3039
3040     If positioning the view at \a index would cause empty space to be displayed at
3041     the beginning or end of the view, the view will be positioned at the boundary.
3042
3043     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3044     at a particular index.  This is unreliable since removing items from the start
3045     of the list does not cause all other items to be repositioned, and because
3046     the actual start of the view can vary based on the size of the delegates.
3047     The correct way to bring an item into view is with \c positionViewAtIndex.
3048
3049     \b Note: methods should only be called after the Component has completed.  To position
3050     the view at startup, this method should be called by Component.onCompleted.  For
3051     example, to position the view at the end:
3052
3053     \code
3054     Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
3055     \endcode
3056 */
3057
3058 /*!
3059     \qmlmethod QtQuick2::ListView::positionViewAtBeginning()
3060     \qmlmethod QtQuick2::ListView::positionViewAtEnd()
3061
3062     Positions the view at the beginning or end, taking into account any header or footer.
3063
3064     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3065     at a particular index.  This is unreliable since removing items from the start
3066     of the list does not cause all other items to be repositioned, and because
3067     the actual start of the view can vary based on the size of the delegates.
3068
3069     \b Note: methods should only be called after the Component has completed.  To position
3070     the view at startup, this method should be called by Component.onCompleted.  For
3071     example, to position the view at the end on startup:
3072
3073     \code
3074     Component.onCompleted: positionViewAtEnd()
3075     \endcode
3076 */
3077
3078 /*!
3079     \qmlmethod int QtQuick2::ListView::indexAt(int x, int y)
3080
3081     Returns the index of the visible item containing the point \a x, \a y in content
3082     coordinates.  If there is no item at the point specified, or the item is
3083     not visible -1 is returned.
3084
3085     If the item is outside the visible area, -1 is returned, regardless of
3086     whether an item will exist at that point when scrolled into view.
3087
3088     \b Note: methods should only be called after the Component has completed.
3089 */
3090
3091 /*!
3092     \qmlmethod Item QtQuick2::ListView::itemAt(int x, int y)
3093
3094     Returns the visible item containing the point \a x, \a y in content
3095     coordinates.  If there is no item at the point specified, or the item is
3096     not visible null is returned.
3097
3098     If the item is outside the visible area, null is returned, regardless of
3099     whether an item will exist at that point when scrolled into view.
3100
3101     \b Note: methods should only be called after the Component has completed.
3102 */
3103
3104 QQuickListViewAttached *QQuickListView::qmlAttachedProperties(QObject *obj)
3105 {
3106     return new QQuickListViewAttached(obj);
3107 }
3108
3109 QT_END_NAMESPACE