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