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