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