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