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