Merge remote branch 'origin/4.8' into 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         FxListItem *bottomItem = snapItemAt(tempPosition+highlightEnd);
1330         qreal pos;
1331         bool isInBounds = -position() > maxExtent && -position() <= minExtent;
1332         if (topItem && isInBounds) {
1333             if (topItem->index == 0 && header && tempPosition+highlightStart < header->position()+header->size()/2 && !strictHighlightRange) {
1334                 pos = isRightToLeft() ? - header->position() + highlightStart - size() : header->position() - highlightStart;
1335             } else {
1336                 if (isRightToLeft())
1337                     pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1338                 else
1339                     pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent);
1340             }
1341         } else if (bottomItem && isInBounds) {
1342             if (isRightToLeft())
1343                 pos = qMax(qMin(-bottomItem->position() + highlightEnd - size(), -maxExtent), -minExtent);
1344             else
1345                 pos = qMax(qMin(bottomItem->position() - highlightEnd, -maxExtent), -minExtent);
1346         } else {
1347             QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent);
1348             return;
1349         }
1350
1351         qreal dist = qAbs(data.move + pos);
1352         if (dist > 0) {
1353             timeline.reset(data.move);
1354             if (fixupMode != Immediate) {
1355                 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1356                 data.fixingUp = true;
1357             } else {
1358                 timeline.set(data.move, -pos);
1359             }
1360             vTime = timeline.time();
1361         }
1362     } else if (currentItem && strictHighlightRange
1363                 && moveReason != QDeclarativeListViewPrivate::SetIndex) {
1364         updateHighlight();
1365         qreal pos = currentItem->itemPosition();
1366         if (viewPos < pos + currentItem->itemSize() - highlightEnd)
1367             viewPos = pos + currentItem->itemSize() - highlightEnd;
1368         if (viewPos > pos - highlightStart)
1369             viewPos = pos - highlightStart;
1370         if (isRightToLeft())
1371             viewPos = -viewPos-size();
1372
1373         timeline.reset(data.move);
1374         if (viewPos != position()) {
1375             if (fixupMode != Immediate) {
1376                 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1377                 data.fixingUp = true;
1378             } else {
1379                 timeline.set(data.move, -viewPos);
1380             }
1381         }
1382         vTime = timeline.time();
1383     } else {
1384         QDeclarativeFlickablePrivate::fixup(data, minExtent, maxExtent);
1385     }
1386     data.inOvershoot = false;
1387     fixupMode = Normal;
1388 }
1389
1390 void QDeclarativeListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1391                                         QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
1392 {
1393     Q_Q(QDeclarativeListView);
1394
1395     data.fixingUp = false;
1396     moveReason = Mouse;
1397     if ((!haveHighlightRange || highlightRange != QDeclarativeListView::StrictlyEnforceRange) && snapMode == QDeclarativeListView::NoSnap) {
1398         correctFlick = true;
1399         QDeclarativeFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1400         return;
1401     }
1402     qreal maxDistance = 0;
1403     qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
1404     qreal highlightStart = isRightToLeft() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
1405     // -ve velocity means list is moving up/left
1406     if (velocity > 0) {
1407         if (data.move.value() < minExtent) {
1408             if (snapMode == QDeclarativeListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1409                 // if we've been dragged < averageSize/2 then bias towards the next item
1410                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1411                 qreal bias = dist < averageSize/2 ? averageSize/2 : 0;
1412                 if (isRightToLeft())
1413                     bias = -bias;
1414                 data.flickTarget = -snapPosAt(-(dataValue - highlightStart) - bias) + highlightStart;
1415                 maxDistance = qAbs(data.flickTarget - data.move.value());
1416                 velocity = maxVelocity;
1417             } else {
1418                 maxDistance = qAbs(minExtent - data.move.value());
1419             }
1420         }
1421         if (snapMode == QDeclarativeListView::NoSnap && highlightRange != QDeclarativeListView::StrictlyEnforceRange)
1422             data.flickTarget = minExtent;
1423     } else {
1424         if (data.move.value() > maxExtent) {
1425             if (snapMode == QDeclarativeListView::SnapOneItem && !hData.flicking && !vData.flicking) {
1426                 // if we've been dragged < averageSize/2 then bias towards the next item
1427                 qreal dist = data.move.value() - (data.pressPos - data.dragStartOffset);
1428                 qreal bias = -dist < averageSize/2 ? averageSize/2 : 0;
1429                 if (isRightToLeft())
1430                     bias = -bias;
1431                 data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + bias) + highlightStart;
1432                 maxDistance = qAbs(data.flickTarget - data.move.value());
1433                 velocity = -maxVelocity;
1434             } else {
1435                 maxDistance = qAbs(maxExtent - data.move.value());
1436             }
1437         }
1438         if (snapMode == QDeclarativeListView::NoSnap && highlightRange != QDeclarativeListView::StrictlyEnforceRange)
1439             data.flickTarget = maxExtent;
1440     }
1441
1442     bool overShoot = boundsBehavior == QDeclarativeFlickable::DragAndOvershootBounds;
1443
1444     if (maxDistance > 0 || overShoot) {
1445         // These modes require the list to stop exactly on an item boundary.
1446         // The initial flick will estimate the boundary to stop on.
1447         // Since list items can have variable sizes, the boundary will be
1448         // reevaluated and adjusted as we approach the boundary.
1449         qreal v = velocity;
1450         if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1451             if (v < 0)
1452                 v = -maxVelocity;
1453             else
1454                 v = maxVelocity;
1455         }
1456         if (!hData.flicking && !vData.flicking) {
1457             // the initial flick - estimate boundary
1458             qreal accel = deceleration;
1459             qreal v2 = v * v;
1460             overshootDist = 0.0;
1461             // + averageSize/4 to encourage moving at least one item in the flick direction
1462             qreal dist = v2 / (accel * 2.0) + averageSize/4;
1463             if (maxDistance > 0)
1464                 dist = qMin(dist, maxDistance);
1465             if (v > 0)
1466                 dist = -dist;
1467             if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QDeclarativeListView::SnapOneItem) {
1468                 if (snapMode != QDeclarativeListView::SnapOneItem) {
1469                     qreal distTemp = isRightToLeft() ? -dist : dist;
1470                     data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart;
1471                 }
1472                 data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1473                 if (overShoot) {
1474                     if (data.flickTarget >= minExtent) {
1475                         overshootDist = overShootDistance(vSize);
1476                         data.flickTarget += overshootDist;
1477                     } else if (data.flickTarget <= maxExtent) {
1478                         overshootDist = overShootDistance(vSize);
1479                         data.flickTarget -= overshootDist;
1480                     }
1481                 }
1482                 qreal adjDist = -data.flickTarget + data.move.value();
1483                 if (qAbs(adjDist) > qAbs(dist)) {
1484                     // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1485                     qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1486                     if (adjv2 > v2) {
1487                         v2 = adjv2;
1488                         v = qSqrt(v2);
1489                         if (dist > 0)
1490                             v = -v;
1491                     }
1492                 }
1493                 dist = adjDist;
1494                 accel = v2 / (2.0f * qAbs(dist));
1495             } else if (overShoot) {
1496                 data.flickTarget = data.move.value() - dist;
1497                 if (data.flickTarget >= minExtent) {
1498                     overshootDist = overShootDistance(vSize);
1499                     data.flickTarget += overshootDist;
1500                 } else if (data.flickTarget <= maxExtent) {
1501                     overshootDist = overShootDistance(vSize);
1502                     data.flickTarget -= overshootDist;
1503                 }
1504             }
1505
1506             timeline.reset(data.move);
1507             timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1508             timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1509             if (!hData.flicking && q->xflick()) {
1510                 hData.flicking = true;
1511                 emit q->flickingChanged();
1512                 emit q->flickingHorizontallyChanged();
1513                 emit q->flickStarted();
1514             }
1515             if (!vData.flicking && q->yflick()) {
1516                 vData.flicking = true;
1517                 emit q->flickingChanged();
1518                 emit q->flickingVerticallyChanged();
1519                 emit q->flickStarted();
1520             }
1521             correctFlick = true;
1522         } else {
1523             // reevaluate the target boundary.
1524             qreal newtarget = data.flickTarget;
1525             if (snapMode != QDeclarativeListView::NoSnap || highlightRange == QDeclarativeListView::StrictlyEnforceRange) {
1526                 qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1527                 newtarget = -snapPosAt(-(tempFlickTarget - highlightStart)) + highlightStart;
1528                 newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
1529             }
1530             if (velocity < 0 && newtarget <= maxExtent)
1531                 newtarget = maxExtent - overshootDist;
1532             else if (velocity > 0 && newtarget >= minExtent)
1533                 newtarget = minExtent + overshootDist;
1534             if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1535                 if (qAbs(velocity) < MinimumFlickVelocity)
1536                     correctFlick = false;
1537                 return;
1538             }
1539             data.flickTarget = newtarget;
1540             qreal dist = -newtarget + data.move.value();
1541             if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1542                 correctFlick = false;
1543                 timeline.reset(data.move);
1544                 fixup(data, minExtent, maxExtent);
1545                 return;
1546             }
1547
1548             timeline.reset(data.move);
1549             timeline.accelDistance(data.move, v, -dist);
1550             timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1551         }
1552     } else {
1553         correctFlick = false;
1554         timeline.reset(data.move);
1555         fixup(data, minExtent, maxExtent);
1556     }
1557 }
1558
1559 //----------------------------------------------------------------------------
1560
1561 /*!
1562     \qmlclass ListView QDeclarativeListView
1563     \ingroup qml-view-elements
1564     \since 4.7
1565     \inherits Flickable
1566     \brief The ListView item provides a list view of items provided by a model.
1567
1568     A ListView displays data from models created from built-in QML elements like ListModel
1569     and XmlListModel, or custom model classes defined in C++ that inherit from
1570     QAbstractListModel.
1571
1572     A ListView has a \l model, which defines the data to be displayed, and
1573     a \l delegate, which defines how the data should be displayed. Items in a
1574     ListView are laid out horizontally or vertically. List views are inherently
1575     flickable because ListView inherits from \l Flickable.
1576
1577     \section1 Example Usage
1578
1579     The following example shows the definition of a simple list model defined
1580     in a file called \c ContactModel.qml:
1581
1582     \snippet doc/src/snippets/declarative/listview/ContactModel.qml 0
1583
1584     Another component can display this model data in a ListView, like this:
1585
1586     \snippet doc/src/snippets/declarative/listview/listview.qml import
1587     \codeline
1588     \snippet doc/src/snippets/declarative/listview/listview.qml classdocs simple
1589
1590     \image listview-simple.png
1591
1592     Here, the ListView creates a \c ContactModel component for its model, and a \l Text element
1593     for its delegate. The view will create a new \l Text component for each item in the model. Notice
1594     the delegate is able to access the model's \c name and \c number data directly.
1595
1596     An improved list view is shown below. The delegate is visually improved and is moved 
1597     into a separate \c contactDelegate component.
1598
1599     \snippet doc/src/snippets/declarative/listview/listview.qml classdocs advanced
1600     \image listview-highlight.png
1601
1602     The currently selected item is highlighted with a blue \l Rectangle using the \l highlight property,
1603     and \c focus is set to \c true to enable keyboard navigation for the list view.
1604     The list view itself is a focus scope (see \l{qmlfocus#Acquiring Focus and Focus Scopes}{the focus documentation page} for more details).
1605
1606     Delegates are instantiated as needed and may be destroyed at any time.
1607     State should \e never be stored in a delegate.
1608
1609     ListView attaches a number of properties to the root item of the delegate, for example
1610     \c {ListView.isCurrentItem}.  In the following example, the root delegate item can access
1611     this attached property directly as \c ListView.isCurrentItem, while the child
1612     \c contactInfo object must refer to this property as \c wrapper.ListView.isCurrentItem.
1613
1614     \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1615
1616     \note Views do not enable \e clip automatically.  If the view
1617     is not clipped by another item or the screen, it will be necessary
1618     to set \e {clip: true} in order to have the out of view items clipped
1619     nicely.
1620
1621     \sa {QML Data Models}, GridView, {declarative/modelviews/listview}{ListView examples}
1622 */
1623
1624 QDeclarativeListView::QDeclarativeListView(QDeclarativeItem *parent)
1625     : QDeclarativeFlickable(*(new QDeclarativeListViewPrivate), parent)
1626 {
1627     Q_D(QDeclarativeListView);
1628     d->init();
1629 }
1630
1631 QDeclarativeListView::~QDeclarativeListView()
1632 {
1633     Q_D(QDeclarativeListView);
1634     d->clear();
1635     if (d->ownModel)
1636         delete d->model;
1637     delete d->header;
1638     delete d->footer;
1639 }
1640
1641 /*!
1642     \qmlattachedproperty bool ListView::isCurrentItem
1643     This attached property is true if this delegate is the current item; otherwise false.
1644
1645     It is attached to each instance of the delegate.
1646
1647     This property may be used to adjust the appearance of the current item, for example:
1648
1649     \snippet doc/src/snippets/declarative/listview/listview.qml isCurrentItem
1650 */
1651
1652 /*!
1653     \qmlattachedproperty ListView ListView::view
1654     This attached property holds the view that manages this delegate instance.
1655
1656     It is attached to each instance of the delegate.
1657 */
1658
1659 /*!
1660     \qmlattachedproperty string ListView::previousSection
1661     This attached property holds the section of the previous element.
1662
1663     It is attached to each instance of the delegate.
1664
1665     The section is evaluated using the \l {ListView::section.property}{section} properties.
1666 */
1667
1668 /*!
1669     \qmlattachedproperty string ListView::nextSection
1670     This attached property holds the section of the next element.
1671
1672     It is attached to each instance of the delegate.
1673
1674     The section is evaluated using the \l {ListView::section.property}{section} properties.
1675 */
1676
1677 /*!
1678     \qmlattachedproperty string ListView::section
1679     This attached property holds the section of this element.
1680
1681     It is attached to each instance of the delegate.
1682
1683     The section is evaluated using the \l {ListView::section.property}{section} properties.
1684 */
1685
1686 /*!
1687     \qmlattachedproperty bool ListView::delayRemove
1688     This attached property holds whether the delegate may be destroyed.
1689
1690     It is attached to each instance of the delegate.
1691
1692     It is sometimes necessary to delay the destruction of an item
1693     until an animation completes.
1694
1695     The example delegate below ensures that the animation completes before
1696     the item is removed from the list.
1697
1698     \snippet doc/src/snippets/declarative/listview/listview.qml delayRemove
1699 */
1700
1701 /*!
1702     \qmlattachedsignal ListView::onAdd()
1703     This attached handler is called immediately after an item is added to the view.
1704 */
1705
1706 /*!
1707     \qmlattachedsignal ListView::onRemove()
1708     This attached handler is called immediately before an item is removed from the view.
1709 */
1710
1711 /*!
1712     \qmlproperty model ListView::model
1713     This property holds the model providing data for the list.
1714
1715     The model provides the set of data that is used to create the items
1716     in the view. Models can be created directly in QML using \l ListModel, \l XmlListModel
1717     or \l VisualItemModel, or provided by C++ model classes. If a C++ model class is
1718     used, it must be a subclass of \l QAbstractItemModel or a simple list.
1719
1720     \sa {qmlmodels}{Data Models}
1721 */
1722 QVariant QDeclarativeListView::model() const
1723 {
1724     Q_D(const QDeclarativeListView);
1725     return d->modelVariant;
1726 }
1727
1728 void QDeclarativeListView::setModel(const QVariant &model)
1729 {
1730     Q_D(QDeclarativeListView);
1731     if (d->modelVariant == model)
1732         return;
1733     if (d->model) {
1734         disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1735         disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1736         disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1737         disconnect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1738         disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1739         disconnect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
1740         disconnect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
1741     }
1742     d->clear();
1743     QDeclarativeVisualModel *oldModel = d->model;
1744     d->model = 0;
1745     d->setPosition(0);
1746     d->modelVariant = model;
1747     QObject *object = qvariant_cast<QObject*>(model);
1748     QDeclarativeVisualModel *vim = 0;
1749     if (object && (vim = qobject_cast<QDeclarativeVisualModel *>(object))) {
1750         if (d->ownModel) {
1751             delete oldModel;
1752             d->ownModel = false;
1753         }
1754         d->model = vim;
1755     } else {
1756         if (!d->ownModel) {
1757             d->model = new QDeclarativeVisualDataModel(qmlContext(this), this);
1758             d->ownModel = true;
1759         } else {
1760             d->model = oldModel;
1761         }
1762         if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
1763             dataModel->setModel(model);
1764     }
1765     if (d->model) {
1766         d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter;
1767         if (isComponentComplete()) {
1768             updateSections();
1769             refill();
1770             if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
1771                 setCurrentIndex(0);
1772             } else {
1773                 d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1774                 d->updateCurrent(d->currentIndex);
1775                 if (d->highlight && d->currentItem) {
1776                     if (d->autoHighlight)
1777                         d->highlight->setPosition(d->currentItem->position());
1778                     d->updateTrackedItem();
1779                 }
1780             }
1781             d->updateViewport();
1782         }
1783         connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1784         connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1785         connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1786         connect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1787         connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1788         connect(d->model, SIGNAL(createdItem(int,QDeclarativeItem*)), this, SLOT(createdItem(int,QDeclarativeItem*)));
1789         connect(d->model, SIGNAL(destroyingItem(QDeclarativeItem*)), this, SLOT(destroyingItem(QDeclarativeItem*)));
1790         emit countChanged();
1791     }
1792     emit modelChanged();
1793 }
1794
1795 /*!
1796     \qmlproperty Component ListView::delegate
1797
1798     The delegate provides a template defining each item instantiated by the view.
1799     The index is exposed as an accessible \c index property.  Properties of the
1800     model are also available depending upon the type of \l {qmlmodels}{Data Model}.
1801
1802     The number of elements in the delegate has a direct effect on the
1803     flicking performance of the view.  If at all possible, place functionality
1804     that is not needed for the normal display of the delegate in a \l Loader which
1805     can load additional elements when needed.
1806
1807     The ListView will lay out the items based on the size of the root item
1808     in the delegate.
1809
1810     It is recommended that the delagate's size be a whole number to avoid sub-pixel
1811     alignment of items.
1812
1813     \note Delegates are instantiated as needed and may be destroyed at any time.
1814     State should \e never be stored in a delegate.
1815 */
1816 QDeclarativeComponent *QDeclarativeListView::delegate() const
1817 {
1818     Q_D(const QDeclarativeListView);
1819     if (d->model) {
1820         if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model))
1821             return dataModel->delegate();
1822     }
1823
1824     return 0;
1825 }
1826
1827 void QDeclarativeListView::setDelegate(QDeclarativeComponent *delegate)
1828 {
1829     Q_D(QDeclarativeListView);
1830     if (delegate == this->delegate())
1831         return;
1832     if (!d->ownModel) {
1833         d->model = new QDeclarativeVisualDataModel(qmlContext(this));
1834         d->ownModel = true;
1835     }
1836     if (QDeclarativeVisualDataModel *dataModel = qobject_cast<QDeclarativeVisualDataModel*>(d->model)) {
1837         int oldCount = dataModel->count();
1838         dataModel->setDelegate(delegate);
1839         if (isComponentComplete()) {
1840             for (int i = 0; i < d->visibleItems.count(); ++i)
1841                 d->releaseItem(d->visibleItems.at(i));
1842             d->visibleItems.clear();
1843             d->releaseItem(d->currentItem);
1844             d->currentItem = 0;
1845             updateSections();
1846             refill();
1847             d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1848             d->updateCurrent(d->currentIndex);
1849             if (d->highlight && d->currentItem) {
1850                 if (d->autoHighlight)
1851                     d->highlight->setPosition(d->currentItem->position());
1852                 d->updateTrackedItem();
1853             }
1854             d->updateViewport();
1855         }
1856         if (oldCount != dataModel->count())
1857             emit countChanged();
1858     }
1859     emit delegateChanged();
1860 }
1861
1862 /*!
1863     \qmlproperty int ListView::currentIndex
1864     \qmlproperty Item ListView::currentItem
1865
1866     The \c currentIndex property holds the index of the current item, and
1867     \c currentItem holds the current item.   Setting the currentIndex to -1
1868     will clear the highlight and set currentItem to null.
1869
1870     If highlightFollowsCurrentItem is \c true, setting either of these 
1871     properties will smoothly scroll the ListView so that the current 
1872     item becomes visible.
1873     
1874     Note that the position of the current item
1875     may only be approximate until it becomes visible in the view.
1876 */
1877 int QDeclarativeListView::currentIndex() const
1878 {
1879     Q_D(const QDeclarativeListView);
1880     return d->currentIndex;
1881 }
1882
1883 void QDeclarativeListView::setCurrentIndex(int index)
1884 {
1885     Q_D(QDeclarativeListView);
1886     if (d->requestedIndex >= 0)  // currently creating item
1887         return;
1888     d->currentIndexCleared = (index == -1);
1889     if (index == d->currentIndex)
1890         return;
1891     if (isComponentComplete() && d->isValid()) {
1892         if (d->layoutScheduled)
1893             d->layout();
1894         d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1895         d->updateCurrent(index);
1896     } else if (d->currentIndex != index) {
1897         d->currentIndex = index;
1898         emit currentIndexChanged();
1899     }
1900 }
1901
1902 QDeclarativeItem *QDeclarativeListView::currentItem()
1903 {
1904     Q_D(QDeclarativeListView);
1905     if (!d->currentItem)
1906         return 0;
1907     return d->currentItem->item;
1908 }
1909
1910 /*!
1911   \qmlproperty Item ListView::highlightItem
1912
1913     This holds the highlight item created from the \l highlight component.
1914
1915   The \c highlightItem is managed by the view unless
1916   \l highlightFollowsCurrentItem is set to false.
1917
1918   \sa highlight, highlightFollowsCurrentItem
1919 */
1920 QDeclarativeItem *QDeclarativeListView::highlightItem()
1921 {
1922     Q_D(QDeclarativeListView);
1923     if (!d->highlight)
1924         return 0;
1925     return d->highlight->item;
1926 }
1927
1928 /*!
1929   \qmlproperty int ListView::count
1930   This property holds the number of items in the view.
1931 */
1932 int QDeclarativeListView::count() const
1933 {
1934     Q_D(const QDeclarativeListView);
1935     if (d->model)
1936         return d->model->count();
1937     return 0;
1938 }
1939
1940 /*!
1941     \qmlproperty Component ListView::highlight
1942     This property holds the component to use as the highlight.
1943
1944     An instance of the highlight component is created for each list.
1945     The geometry of the resulting component instance is managed by the list
1946     so as to stay with the current item, unless the highlightFollowsCurrentItem
1947     property is false.
1948
1949     \sa highlightItem, highlightFollowsCurrentItem, {declarative/modelviews/listview}{ListView examples}
1950 */
1951 QDeclarativeComponent *QDeclarativeListView::highlight() const
1952 {
1953     Q_D(const QDeclarativeListView);
1954     return d->highlightComponent;
1955 }
1956
1957 void QDeclarativeListView::setHighlight(QDeclarativeComponent *highlight)
1958 {
1959     Q_D(QDeclarativeListView);
1960     if (highlight != d->highlightComponent) {
1961         d->highlightComponent = highlight;
1962         d->createHighlight();
1963         if (d->currentItem)
1964             d->updateHighlight();
1965         emit highlightChanged();
1966     }
1967 }
1968
1969 /*!
1970     \qmlproperty bool ListView::highlightFollowsCurrentItem
1971     This property holds whether the highlight is managed by the view.
1972
1973     If this property is true (the default value), the highlight is moved smoothly
1974     to follow the current item.  Otherwise, the
1975     highlight is not moved by the view, and any movement must be implemented
1976     by the highlight.  
1977     
1978     Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1979
1980     \snippet doc/src/snippets/declarative/listview/listview.qml highlightFollowsCurrentItem
1981
1982     Note that the highlight animation also affects the way that the view
1983     is scrolled.  This is because the view moves to maintain the
1984     highlight within the preferred highlight range (or visible viewport).
1985
1986     \sa highlight, highlightMoveSpeed
1987 */
1988 bool QDeclarativeListView::highlightFollowsCurrentItem() const
1989 {
1990     Q_D(const QDeclarativeListView);
1991     return d->autoHighlight;
1992 }
1993
1994 void QDeclarativeListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1995 {
1996     Q_D(QDeclarativeListView);
1997     if (d->autoHighlight != autoHighlight) {
1998         d->autoHighlight = autoHighlight;
1999         if (autoHighlight) {
2000             d->updateHighlight();
2001         } else {
2002             if (d->highlightPosAnimator)
2003                 d->highlightPosAnimator->stop();
2004             if (d->highlightSizeAnimator)
2005                 d->highlightSizeAnimator->stop();
2006         }
2007         emit highlightFollowsCurrentItemChanged();
2008     }
2009 }
2010
2011 //###Possibly rename these properties, since they are very useful even without a highlight?
2012 /*!
2013     \qmlproperty real ListView::preferredHighlightBegin
2014     \qmlproperty real ListView::preferredHighlightEnd
2015     \qmlproperty enumeration ListView::highlightRangeMode
2016
2017     These properties define the preferred range of the highlight (for the current item)
2018     within the view. The \c preferredHighlightBegin value must be less than the
2019     \c preferredHighlightEnd value. 
2020
2021     These properties affect the position of the current item when the list is scrolled.
2022     For example, if the currently selected item should stay in the middle of the
2023     list when the view is scrolled, set the \c preferredHighlightBegin and 
2024     \c preferredHighlightEnd values to the top and bottom coordinates of where the middle 
2025     item would be. If the \c currentItem is changed programmatically, the list will
2026     automatically scroll so that the current item is in the middle of the view.
2027     Furthermore, the behavior of the current item index will occur whether or not a
2028     highlight exists.
2029
2030     Valid values for \c highlightRangeMode are:
2031
2032     \list
2033     \o ListView.ApplyRange - the view attempts to maintain the highlight within the range.
2034        However, the highlight can move outside of the range at the ends of the list or due
2035        to mouse interaction.
2036     \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
2037        The current item changes if a keyboard or mouse action would cause the highlight to move
2038        outside of the range.
2039     \o ListView.NoHighlightRange - this is the default value.
2040     \endlist
2041 */
2042 qreal QDeclarativeListView::preferredHighlightBegin() const
2043 {
2044     Q_D(const QDeclarativeListView);
2045     return d->highlightRangeStart;
2046 }
2047
2048 void QDeclarativeListView::setPreferredHighlightBegin(qreal start)
2049 {
2050     Q_D(QDeclarativeListView);
2051     d->highlightRangeStartValid = true;
2052     if (d->highlightRangeStart == start)
2053         return;
2054     d->highlightRangeStart = start;
2055     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2056     emit preferredHighlightBeginChanged();
2057 }
2058
2059 void QDeclarativeListView::resetPreferredHighlightBegin()
2060 {
2061     Q_D(QDeclarativeListView);
2062     d->highlightRangeStartValid = false;
2063     if (d->highlightRangeStart == 0)
2064         return;
2065     d->highlightRangeStart = 0;
2066     emit preferredHighlightBeginChanged();
2067 }
2068
2069 qreal QDeclarativeListView::preferredHighlightEnd() const
2070 {
2071     Q_D(const QDeclarativeListView);
2072     return d->highlightRangeEnd;
2073 }
2074
2075 void QDeclarativeListView::setPreferredHighlightEnd(qreal end)
2076 {
2077     Q_D(QDeclarativeListView);
2078     d->highlightRangeEndValid = true;
2079     if (d->highlightRangeEnd == end)
2080         return;
2081     d->highlightRangeEnd = end;
2082     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2083     emit preferredHighlightEndChanged();
2084 }
2085
2086 void QDeclarativeListView::resetPreferredHighlightEnd()
2087 {
2088     Q_D(QDeclarativeListView);
2089     d->highlightRangeEndValid = false;
2090     if (d->highlightRangeEnd == 0)
2091         return;
2092     d->highlightRangeEnd = 0;
2093     emit preferredHighlightEndChanged();
2094 }
2095
2096 QDeclarativeListView::HighlightRangeMode QDeclarativeListView::highlightRangeMode() const
2097 {
2098     Q_D(const QDeclarativeListView);
2099     return d->highlightRange;
2100 }
2101
2102 void QDeclarativeListView::setHighlightRangeMode(HighlightRangeMode mode)
2103 {
2104     Q_D(QDeclarativeListView);
2105     if (d->highlightRange == mode)
2106         return;
2107     d->highlightRange = mode;
2108     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2109     emit highlightRangeModeChanged();
2110 }
2111
2112 /*!
2113     \qmlproperty real ListView::spacing
2114
2115     This property holds the spacing between items.
2116
2117     The default value is 0.
2118 */
2119 qreal QDeclarativeListView::spacing() const
2120 {
2121     Q_D(const QDeclarativeListView);
2122     return d->spacing;
2123 }
2124
2125 void QDeclarativeListView::setSpacing(qreal spacing)
2126 {
2127     Q_D(QDeclarativeListView);
2128     if (spacing != d->spacing) {
2129         d->spacing = spacing;
2130         d->layout();
2131         emit spacingChanged();
2132     }
2133 }
2134
2135 /*!
2136     \qmlproperty enumeration ListView::orientation
2137     This property holds the orientation of the list.
2138
2139     Possible values:
2140
2141     \list
2142     \o ListView.Horizontal - Items are laid out horizontally
2143     \o ListView.Vertical (default) - Items are laid out vertically
2144     \endlist
2145
2146     \table
2147     \row
2148     \o Horizontal orientation:
2149     \image ListViewHorizontal.png
2150
2151     \row
2152     \o Vertical orientation:
2153     \image listview-highlight.png
2154     \endtable
2155 */
2156 QDeclarativeListView::Orientation QDeclarativeListView::orientation() const
2157 {
2158     Q_D(const QDeclarativeListView);
2159     return d->orient;
2160 }
2161
2162 void QDeclarativeListView::setOrientation(QDeclarativeListView::Orientation orientation)
2163 {
2164     Q_D(QDeclarativeListView);
2165     if (d->orient != orientation) {
2166         d->orient = orientation;
2167         if (d->orient == QDeclarativeListView::Vertical) {
2168             setContentWidth(-1);
2169             setFlickableDirection(VerticalFlick);
2170             setContentX(0);
2171         } else {
2172             setContentHeight(-1);
2173             setFlickableDirection(HorizontalFlick);
2174             setContentY(0);
2175         }
2176         d->regenerate();
2177         emit orientationChanged();
2178     }
2179 }
2180
2181 /*!
2182   \qmlproperty enumeration ListView::layoutDirection
2183   This property holds the layout direction of the horizontal list.
2184
2185   Possible values:
2186
2187   \list
2188   \o Qt.LeftToRight (default) - Items will be laid out from left to right.
2189   \o Qt.RightToLeft - Items will be laid out from right to let.
2190   \endlist
2191
2192   When using the attached property \l {LayoutMirroring::enabled} for locale layouts,
2193   the layout direction of the horizontal list will be mirrored. However, the actual property
2194   \c layoutDirection will remain unchanged. You can use the property
2195   \l {LayoutMirroring::enabled} to determine whether the direction has been mirrored.
2196
2197   \sa {LayoutMirroring}{LayoutMirroring}
2198 */
2199
2200 Qt::LayoutDirection QDeclarativeListView::layoutDirection() const
2201 {
2202     Q_D(const QDeclarativeListView);
2203     return d->layoutDirection;
2204 }
2205
2206 void QDeclarativeListView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
2207 {
2208     Q_D(QDeclarativeListView);
2209     if (d->layoutDirection != layoutDirection) {
2210         d->layoutDirection = layoutDirection;
2211         d->regenerate();
2212         emit layoutDirectionChanged();
2213     }
2214 }
2215
2216 Qt::LayoutDirection QDeclarativeListView::effectiveLayoutDirection() const
2217 {
2218     Q_D(const QDeclarativeListView);
2219     if (d->effectiveLayoutMirror)
2220         return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
2221     else
2222         return d->layoutDirection;
2223 }
2224
2225 /*!
2226     \qmlproperty bool ListView::keyNavigationWraps
2227     This property holds whether the list wraps key navigation. 
2228
2229     If this is true, key navigation that would move the current item selection
2230     past the end of the list instead wraps around and moves the selection to
2231     the start of the list, and vice-versa.
2232
2233     By default, key navigation is not wrapped.
2234 */
2235 bool QDeclarativeListView::isWrapEnabled() const
2236 {
2237     Q_D(const QDeclarativeListView);
2238     return d->wrap;
2239 }
2240
2241 void QDeclarativeListView::setWrapEnabled(bool wrap)
2242 {
2243     Q_D(QDeclarativeListView);
2244     if (d->wrap == wrap)
2245         return;
2246     d->wrap = wrap;
2247     emit keyNavigationWrapsChanged();
2248 }
2249
2250 /*!
2251     \qmlproperty int ListView::cacheBuffer
2252     This property determines whether delegates are retained outside the
2253     visible area of the view.
2254
2255     If this value is non-zero, the view keeps as many delegates
2256     instantiated as it can fit within the buffer specified.  For example,
2257     if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
2258     set to 40, then up to 2 delegates above and 2 delegates below the visible
2259     area may be retained.
2260
2261     Note that cacheBuffer is not a pixel buffer - it only maintains additional
2262     instantiated delegates.
2263
2264     Setting this value can improve the smoothness of scrolling behavior at the expense
2265     of additional memory usage.  It is not a substitute for creating efficient
2266     delegates; the fewer elements in a delegate, the faster a view can be
2267     scrolled.
2268 */
2269 int QDeclarativeListView::cacheBuffer() const
2270 {
2271     Q_D(const QDeclarativeListView);
2272     return d->buffer;
2273 }
2274
2275 void QDeclarativeListView::setCacheBuffer(int b)
2276 {
2277     Q_D(QDeclarativeListView);
2278     if (d->buffer != b) {
2279         d->buffer = b;
2280         if (isComponentComplete()) {
2281             d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter;
2282             refill();
2283         }
2284         emit cacheBufferChanged();
2285     }
2286 }
2287
2288 /*!
2289     \qmlproperty string ListView::section.property
2290     \qmlproperty enumeration ListView::section.criteria
2291     \qmlproperty Component ListView::section.delegate
2292
2293     These properties hold the expression to be evaluated for the \l section attached property.
2294
2295     The \l section attached property enables a ListView to be visually
2296     separated into different parts. These properties determine how sections
2297     are created.
2298     
2299     \c section.property holds the name of the property that is the basis
2300     of each section.
2301
2302     \c section.criteria holds the criteria for forming each section based on
2303     \c section.property. This value can be one of:
2304
2305     \list
2306     \o ViewSection.FullString (default) - sections are created based on the 
2307     \c section.property value.
2308     \o ViewSection.FirstCharacter - sections are created based on the first
2309     character of the \c section.property value (for example, 'A', 'B', 'C' 
2310     sections, etc. for an address book)
2311     \endlist
2312
2313     \c section.delegate holds the delegate component for each section.
2314
2315     Each item in the list has attached properties named \c ListView.section,
2316     \c ListView.previousSection and \c ListView.nextSection.  These may be
2317     used to place a section header for related items.
2318
2319     For example, here is a ListView that displays a list of animals, separated 
2320     into sections. Each item in the ListView is placed in a different section 
2321     depending on the "size" property of the model item. The \c sectionHeading
2322     delegate component provides the light blue bar that marks the beginning of
2323     each section.
2324
2325        
2326     \snippet examples/declarative/modelviews/listview/sections.qml 0
2327
2328     \image qml-listview-sections-example.png
2329
2330     \note Adding sections to a ListView does not automatically re-order the
2331     list items by the section criteria.
2332     If the model is not ordered by section, then it is possible that
2333     the sections created will not be unique; each boundary between
2334     differing sections will result in a section header being created
2335     even if that section exists elsewhere.
2336
2337     \sa {declarative/modelviews/listview}{ListView examples}
2338 */
2339 QDeclarativeViewSection *QDeclarativeListView::sectionCriteria()
2340 {
2341     Q_D(QDeclarativeListView);
2342     if (!d->sectionCriteria) {
2343         d->sectionCriteria = new QDeclarativeViewSection(this);
2344         connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2345     }
2346     return d->sectionCriteria;
2347 }
2348
2349 /*!
2350     \qmlproperty string ListView::currentSection
2351     This property holds the section that is currently at the beginning of the view.
2352 */
2353 QString QDeclarativeListView::currentSection() const
2354 {
2355     Q_D(const QDeclarativeListView);
2356     return d->currentSection;
2357 }
2358
2359 /*!
2360     \qmlproperty real ListView::highlightMoveSpeed
2361     \qmlproperty int ListView::highlightMoveDuration
2362     \qmlproperty real ListView::highlightResizeSpeed
2363     \qmlproperty int ListView::highlightResizeDuration
2364
2365     These properties hold the move and resize animation speed of the highlight delegate.
2366
2367     \l highlightFollowsCurrentItem must be true for these properties
2368     to have effect.
2369
2370     The default value for the speed properties is 400 pixels/second.
2371     The default value for the duration properties is -1, i.e. the
2372     highlight will take as much time as necessary to move at the set speed.
2373
2374     These properties have the same characteristics as a SmoothedAnimation.
2375
2376     \sa highlightFollowsCurrentItem
2377 */
2378 qreal QDeclarativeListView::highlightMoveSpeed() const
2379 {
2380     Q_D(const QDeclarativeListView);\
2381     return d->highlightMoveSpeed;
2382 }
2383
2384 void QDeclarativeListView::setHighlightMoveSpeed(qreal speed)
2385 {
2386     Q_D(QDeclarativeListView);\
2387     if (d->highlightMoveSpeed != speed) {
2388         d->highlightMoveSpeed = speed;
2389         if (d->highlightPosAnimator)
2390             d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
2391         emit highlightMoveSpeedChanged();
2392     }
2393 }
2394
2395 int QDeclarativeListView::highlightMoveDuration() const
2396 {
2397     Q_D(const QDeclarativeListView);
2398     return d->highlightMoveDuration;
2399 }
2400
2401 void QDeclarativeListView::setHighlightMoveDuration(int duration)
2402 {
2403     Q_D(QDeclarativeListView);\
2404     if (d->highlightMoveDuration != duration) {
2405         d->highlightMoveDuration = duration;
2406         if (d->highlightPosAnimator)
2407             d->highlightPosAnimator->userDuration = d->highlightMoveDuration;
2408         emit highlightMoveDurationChanged();
2409     }
2410 }
2411
2412 qreal QDeclarativeListView::highlightResizeSpeed() const
2413 {
2414     Q_D(const QDeclarativeListView);\
2415     return d->highlightResizeSpeed;
2416 }
2417
2418 void QDeclarativeListView::setHighlightResizeSpeed(qreal speed)
2419 {
2420     Q_D(QDeclarativeListView);\
2421     if (d->highlightResizeSpeed != speed) {
2422         d->highlightResizeSpeed = speed;
2423         if (d->highlightSizeAnimator)
2424             d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
2425         emit highlightResizeSpeedChanged();
2426     }
2427 }
2428
2429 int QDeclarativeListView::highlightResizeDuration() const
2430 {
2431     Q_D(const QDeclarativeListView);
2432     return d->highlightResizeDuration;
2433 }
2434
2435 void QDeclarativeListView::setHighlightResizeDuration(int duration)
2436 {
2437     Q_D(QDeclarativeListView);\
2438     if (d->highlightResizeDuration != duration) {
2439         d->highlightResizeDuration = duration;
2440         if (d->highlightSizeAnimator)
2441             d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2442         emit highlightResizeDurationChanged();
2443     }
2444 }
2445
2446 /*!
2447     \qmlproperty enumeration ListView::snapMode
2448
2449     This property determines how the view scrolling will settle following a drag or flick.
2450     The possible values are:
2451
2452     \list
2453     \o ListView.NoSnap (default) - the view stops anywhere within the visible area.
2454     \o ListView.SnapToItem - the view settles with an item aligned with the start of
2455     the view.
2456     \o ListView.SnapOneItem - the view settles no more than one item away from the first
2457     visible item at the time the mouse button is released.  This mode is particularly
2458     useful for moving one page at a time.
2459     \endlist
2460
2461     \c snapMode does not affect the \l currentIndex.  To update the
2462     \l currentIndex as the list is moved, set \l highlightRangeMode
2463     to \c ListView.StrictlyEnforceRange.
2464
2465     \sa highlightRangeMode
2466 */
2467 QDeclarativeListView::SnapMode QDeclarativeListView::snapMode() const
2468 {
2469     Q_D(const QDeclarativeListView);
2470     return d->snapMode;
2471 }
2472
2473 void QDeclarativeListView::setSnapMode(SnapMode mode)
2474 {
2475     Q_D(QDeclarativeListView);
2476     if (d->snapMode != mode) {
2477         d->snapMode = mode;
2478         emit snapModeChanged();
2479     }
2480 }
2481
2482 /*!
2483     \qmlproperty Component ListView::footer
2484     This property holds the component to use as the footer.
2485
2486     An instance of the footer component is created for each view.  The
2487     footer is positioned at the end of the view, after any items.
2488
2489     \sa header
2490 */
2491 QDeclarativeComponent *QDeclarativeListView::footer() const
2492 {
2493     Q_D(const QDeclarativeListView);
2494     return d->footerComponent;
2495 }
2496
2497 void QDeclarativeListView::setFooter(QDeclarativeComponent *footer)
2498 {
2499     Q_D(QDeclarativeListView);
2500     if (d->footerComponent != footer) {
2501         if (d->footer) {
2502             if (scene())
2503                 scene()->removeItem(d->footer->item);
2504             d->footer->item->deleteLater();
2505             delete d->footer;
2506             d->footer = 0;
2507         }
2508         d->footerComponent = footer;
2509         d->minExtentDirty = true;
2510         d->maxExtentDirty = true;
2511         if (isComponentComplete()) {
2512             d->updateFooter();
2513             d->updateViewport();
2514             d->fixupPosition();
2515         }
2516         emit footerChanged();
2517     }
2518 }
2519
2520 /*!
2521     \qmlproperty Component ListView::header
2522     This property holds the component to use as the header.
2523
2524     An instance of the header component is created for each view.  The
2525     header is positioned at the beginning of the view, before any items.
2526
2527     \sa footer
2528 */
2529 QDeclarativeComponent *QDeclarativeListView::header() const
2530 {
2531     Q_D(const QDeclarativeListView);
2532     return d->headerComponent;
2533 }
2534
2535 void QDeclarativeListView::setHeader(QDeclarativeComponent *header)
2536 {
2537     Q_D(QDeclarativeListView);
2538     if (d->headerComponent != header) {
2539         if (d->header) {
2540             if (scene())
2541                 scene()->removeItem(d->header->item);
2542             d->header->item->deleteLater();
2543             delete d->header;
2544             d->header = 0;
2545         }
2546         d->headerComponent = header;
2547         d->minExtentDirty = true;
2548         d->maxExtentDirty = true;
2549         if (isComponentComplete()) {
2550             d->updateHeader();
2551             d->updateFooter();
2552             d->updateViewport();
2553             d->fixupPosition();
2554         }
2555         emit headerChanged();
2556     }
2557 }
2558
2559 void QDeclarativeListView::setContentX(qreal pos)
2560 {
2561     Q_D(QDeclarativeListView);
2562     // Positioning the view manually should override any current movement state
2563     d->moveReason = QDeclarativeListViewPrivate::Other;
2564     QDeclarativeFlickable::setContentX(pos);
2565 }
2566
2567 void QDeclarativeListView::setContentY(qreal pos)
2568 {
2569     Q_D(QDeclarativeListView);
2570     // Positioning the view manually should override any current movement state
2571     d->moveReason = QDeclarativeListViewPrivate::Other;
2572     QDeclarativeFlickable::setContentY(pos);
2573 }
2574
2575 bool QDeclarativeListView::event(QEvent *event)
2576 {
2577     Q_D(QDeclarativeListView);
2578     if (event->type() == QEvent::User) {
2579         if (d->layoutScheduled)
2580             d->layout();
2581         return true;
2582     }
2583
2584     return QDeclarativeFlickable::event(event);
2585 }
2586
2587 void QDeclarativeListView::viewportMoved()
2588 {
2589     Q_D(QDeclarativeListView);
2590     QDeclarativeFlickable::viewportMoved();
2591     if (!d->itemCount)
2592         return;
2593     // Recursion can occur due to refill changing the content size.
2594     if (d->inViewportMoved)
2595         return;
2596     d->inViewportMoved = true;
2597     d->lazyRelease = true;
2598     refill();
2599     if (d->hData.flicking || d->vData.flicking || d->hData.moving || d->vData.moving)
2600         d->moveReason = QDeclarativeListViewPrivate::Mouse;
2601     if (d->moveReason != QDeclarativeListViewPrivate::SetIndex) {
2602         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2603             // reposition highlight
2604             qreal pos = d->highlight->position();
2605             qreal viewPos;
2606             qreal highlightStart;
2607             qreal highlightEnd;
2608             if (d->isRightToLeft()) {
2609                 // Handle Right-To-Left exceptions
2610                 viewPos = -d->position()-d->size();
2611                 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
2612                 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
2613             } else {
2614                 viewPos = d->position();
2615                 highlightStart = d->highlightRangeStart;
2616                 highlightEnd = d->highlightRangeEnd;
2617             }
2618             if (pos > viewPos + highlightEnd - d->highlight->size())
2619                 pos = viewPos + highlightEnd - d->highlight->size();
2620             if (pos < viewPos + highlightStart)
2621                 pos = viewPos + highlightStart;
2622             d->highlightPosAnimator->stop();
2623             d->highlight->setPosition(qRound(pos));
2624
2625             // update current index
2626             if (FxListItem *snapItem = d->snapItemAt(d->highlight->position())) {
2627                 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2628                     d->updateCurrent(snapItem->index);
2629             }
2630         }
2631     }
2632
2633     if ((d->hData.flicking || d->vData.flicking) && d->correctFlick && !d->inFlickCorrection) {
2634         d->inFlickCorrection = true;
2635         // Near an end and it seems that the extent has changed?
2636         // Recalculate the flick so that we don't end up in an odd position.
2637         if (yflick() && !d->vData.inOvershoot) {
2638             if (d->vData.velocity > 0) {
2639                 const qreal minY = minYExtent();
2640                 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2641                     && minY != d->vData.flickTarget)
2642                     d->flickY(-d->vData.smoothVelocity.value());
2643                 d->bufferMode = QDeclarativeListViewPrivate::BufferBefore;
2644             } else if (d->vData.velocity < 0) {
2645                 const qreal maxY = maxYExtent();
2646                 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2647                     && maxY != d->vData.flickTarget)
2648                     d->flickY(-d->vData.smoothVelocity.value());
2649                 d->bufferMode = QDeclarativeListViewPrivate::BufferAfter;
2650             }
2651         }
2652
2653         if (xflick() && !d->hData.inOvershoot) {
2654             if (d->hData.velocity > 0) {
2655                 const qreal minX = minXExtent();
2656                 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2657                     && minX != d->hData.flickTarget)
2658                     d->flickX(-d->hData.smoothVelocity.value());
2659                 d->bufferMode = d->isRightToLeft()
2660                         ? QDeclarativeListViewPrivate::BufferAfter : QDeclarativeListViewPrivate::BufferBefore;
2661             } else if (d->hData.velocity < 0) {
2662                 const qreal maxX = maxXExtent();
2663                 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2664                     && maxX != d->hData.flickTarget)
2665                     d->flickX(-d->hData.smoothVelocity.value());
2666                 d->bufferMode = d->isRightToLeft()
2667                         ? QDeclarativeListViewPrivate::BufferBefore : QDeclarativeListViewPrivate::BufferAfter;
2668             }
2669         }
2670         d->inFlickCorrection = false;
2671     }
2672     d->inViewportMoved = false;
2673 }
2674
2675 qreal QDeclarativeListView::minYExtent() const
2676 {
2677     Q_D(const QDeclarativeListView);
2678     if (d->orient == QDeclarativeListView::Horizontal)
2679         return QDeclarativeFlickable::minYExtent();
2680     if (d->minExtentDirty) {
2681         d->minExtent = -d->startPosition();
2682         if (d->header && d->visibleItems.count())
2683             d->minExtent += d->header->size();
2684         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2685             d->minExtent += d->highlightRangeStart;
2686             if (d->sectionCriteria) {
2687                 if (d->visibleItem(0))
2688                     d->minExtent -= d->visibleItem(0)->sectionSize();
2689             }
2690             d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1));
2691         }
2692         d->minExtentDirty = false;
2693     }
2694
2695     return d->minExtent;
2696 }
2697
2698 qreal QDeclarativeListView::maxYExtent() const
2699 {
2700     Q_D(const QDeclarativeListView);
2701     if (d->orient == QDeclarativeListView::Horizontal)
2702         return height();
2703     if (d->maxExtentDirty) {
2704         if (!d->model || !d->model->count()) {
2705             d->maxExtent = d->header ? -d->header->size() : 0;
2706             d->maxExtent += height();
2707         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2708             d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
2709             if (d->highlightRangeEnd != d->highlightRangeStart)
2710                 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd + 1));
2711         } else {
2712             d->maxExtent = -(d->endPosition() - height() + 1);
2713         }
2714         if (d->footer)
2715             d->maxExtent -= d->footer->size();
2716         qreal minY = minYExtent();
2717         if (d->maxExtent > minY)
2718             d->maxExtent = minY;
2719         d->maxExtentDirty = false;
2720     }
2721     return d->maxExtent;
2722 }
2723
2724 qreal QDeclarativeListView::minXExtent() const
2725 {
2726     Q_D(const QDeclarativeListView);
2727     if (d->orient == QDeclarativeListView::Vertical)
2728         return QDeclarativeFlickable::minXExtent();
2729     if (d->minExtentDirty) {
2730         d->minExtent = -d->startPosition();
2731
2732         qreal highlightStart;
2733         qreal highlightEnd;
2734         qreal endPositionFirstItem = 0;
2735         if (d->isRightToLeft()) {
2736             if (d->model && d->model->count())
2737                 endPositionFirstItem = d->positionAt(d->model->count()-1);
2738             else if (d->header)
2739                 d->minExtent += d->header->size();
2740             highlightStart = d->highlightRangeStartValid
2741                     ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
2742                     : d->size() - (d->lastPosition()-endPositionFirstItem);
2743             highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
2744             if (d->footer)
2745                 d->minExtent += d->footer->size();
2746             qreal maxX = maxXExtent();
2747             if (d->minExtent < maxX)
2748                 d->minExtent = maxX;
2749         } else {
2750             endPositionFirstItem = d->endPositionAt(0);
2751             highlightStart = d->highlightRangeStart;
2752             highlightEnd = d->highlightRangeEnd;
2753             if (d->header && d->visibleItems.count())
2754                 d->minExtent += d->header->size();
2755         }
2756         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2757             d->minExtent += highlightStart;
2758             d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd + 1));
2759         }
2760         d->minExtentDirty = false;
2761     }
2762
2763     return d->minExtent;
2764 }
2765
2766 qreal QDeclarativeListView::maxXExtent() const
2767 {
2768     Q_D(const QDeclarativeListView);
2769     if (d->orient == QDeclarativeListView::Vertical)
2770         return width();
2771     if (d->maxExtentDirty) {
2772         qreal highlightStart;
2773         qreal highlightEnd;
2774         qreal lastItemPosition = 0;
2775         d->maxExtent = 0;
2776         if (d->isRightToLeft()) {
2777             highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
2778             highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
2779             lastItemPosition = d->endPosition();
2780         } else {
2781             highlightStart = d->highlightRangeStart;
2782             highlightEnd = d->highlightRangeEnd;
2783             if (d->model && d->model->count())
2784                 lastItemPosition = d->positionAt(d->model->count()-1);
2785         }
2786         if (!d->model || !d->model->count()) {
2787             if (!d->isRightToLeft())
2788                 d->maxExtent = d->header ? -d->header->size() : 0;
2789             d->maxExtent += width();
2790         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2791             d->maxExtent = -(lastItemPosition - highlightStart);
2792             if (highlightEnd != highlightStart) {
2793                 d->maxExtent = d->isRightToLeft()
2794                         ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd + 1))
2795                         : qMin(d->maxExtent, -(d->endPosition() - highlightEnd + 1));
2796             }
2797         } else {
2798             d->maxExtent = -(d->endPosition() - width() + 1);
2799         }
2800         if (d->isRightToLeft()) {
2801             if (d->header && d->visibleItems.count())
2802                 d->maxExtent -= d->header->size();
2803         } else {
2804             if (d->footer)
2805                 d->maxExtent -= d->footer->size();
2806             qreal minX = minXExtent();
2807             if (d->maxExtent > minX)
2808                 d->maxExtent = minX;
2809         }
2810         d->maxExtentDirty = false;
2811     }
2812     return d->maxExtent;
2813 }
2814
2815 void QDeclarativeListView::keyPressEvent(QKeyEvent *event)
2816 {
2817     Q_D(QDeclarativeListView);
2818     keyPressPreHandler(event);
2819     if (event->isAccepted())
2820         return;
2821
2822     if (d->model && d->model->count() && d->interactive) {
2823         if ((d->orient == QDeclarativeListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2824                     || (d->orient == QDeclarativeListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2825                     || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Up)) {
2826             if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2827                 decrementCurrentIndex();
2828                 event->accept();
2829                 return;
2830             } else if (d->wrap) {
2831                 event->accept();
2832                 return;
2833             }
2834         } else if ((d->orient == QDeclarativeListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2835                     || (d->orient == QDeclarativeListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2836                     || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Down)) {
2837             if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2838                 incrementCurrentIndex();
2839                 event->accept();
2840                 return;
2841             } else if (d->wrap) {
2842                 event->accept();
2843                 return;
2844             }
2845         }
2846     }
2847     event->ignore();
2848     QDeclarativeFlickable::keyPressEvent(event);
2849 }
2850
2851 void QDeclarativeListView::geometryChanged(const QRectF &newGeometry,
2852                              const QRectF &oldGeometry)
2853 {
2854     Q_D(QDeclarativeListView);
2855     d->maxExtentDirty = true;
2856     d->minExtentDirty = true;
2857     if (d->isRightToLeft() && d->orient == QDeclarativeListView::Horizontal) {
2858         // maintain position relative to the right edge
2859         int dx = newGeometry.width() - oldGeometry.width();
2860         setContentX(contentX() - dx);
2861     }
2862     QDeclarativeFlickable::geometryChanged(newGeometry, oldGeometry);
2863 }
2864
2865
2866 /*!
2867     \qmlmethod ListView::incrementCurrentIndex()
2868
2869     Increments the current index.  The current index will wrap
2870     if keyNavigationWraps is true and it is currently at the end.
2871     This method has no effect if the \l count is zero.
2872
2873     \bold Note: methods should only be called after the Component has completed.
2874 */
2875 void QDeclarativeListView::incrementCurrentIndex()
2876 {
2877     Q_D(QDeclarativeListView);
2878     int count = d->model ? d->model->count() : 0;
2879     if (count && (currentIndex() < count - 1 || d->wrap)) {
2880         d->moveReason = QDeclarativeListViewPrivate::SetIndex;
2881         int index = currentIndex()+1;
2882         setCurrentIndex((index >= 0 && index < count) ? index : 0);
2883     }
2884 }
2885
2886 /*!
2887     \qmlmethod ListView::decrementCurrentIndex()
2888
2889     Decrements the current index.  The current index will wrap
2890     if keyNavigationWraps is true and it is currently at the beginning.
2891     This method has no effect if the \l count is zero.
2892
2893     \bold Note: methods should only be called after the Component has completed.
2894 */
2895 void QDeclarativeListView::decrementCurrentIndex()
2896 {
2897     Q_D(QDeclarativeListView);
2898     int count = d->model ? d->model->count() : 0;
2899     if (count && (currentIndex() > 0 || d->wrap)) {
2900         d->moveReason = QDeclarativeListViewPrivate::SetIndex;
2901         int index = currentIndex()-1;
2902         setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2903     }
2904 }
2905
2906 void QDeclarativeListViewPrivate::positionViewAtIndex(int index, int mode)
2907 {
2908     Q_Q(QDeclarativeListView);
2909     if (!isValid())
2910         return;
2911     if (mode < QDeclarativeListView::Beginning || mode > QDeclarativeListView::Contain)
2912         return;
2913     int idx = qMax(qMin(index, model->count()-1), 0);
2914
2915     if (layoutScheduled)
2916         layout();
2917     qreal pos = isRightToLeft() ? -position() - size() : position();
2918     FxListItem *item = visibleItem(idx);
2919     qreal maxExtent;
2920     if (orient == QDeclarativeListView::Vertical)
2921         maxExtent = -q->maxYExtent();
2922     else
2923         maxExtent = isRightToLeft() ? q->minXExtent()-size(): -q->maxXExtent();
2924
2925     if (!item) {
2926         int itemPos = positionAt(idx);
2927         // save the currently visible items in case any of them end up visible again
2928         QList<FxListItem*> oldVisible = visibleItems;
2929         visibleItems.clear();
2930         visiblePos = itemPos;
2931         visibleIndex = idx;
2932         setPosition(qMin(qreal(itemPos), maxExtent));
2933         // now release the reference to all the old visible items.
2934         for (int i = 0; i < oldVisible.count(); ++i)
2935             releaseItem(oldVisible.at(i));
2936         item = visibleItem(idx);
2937     }
2938     if (item) {
2939         const qreal itemPos = item->position();
2940         switch (mode) {
2941         case QDeclarativeListView::Beginning:
2942             pos = itemPos;
2943             if (index < 0 && header)
2944                 pos -= header->size();
2945             break;
2946         case QDeclarativeListView::Center:
2947             pos = itemPos - (size() - item->size())/2;
2948             break;
2949         case QDeclarativeListView::End:
2950             pos = itemPos - size() + item->size();
2951             if (index >= model->count() && footer)
2952                 pos += footer->size();
2953             break;
2954         case QDeclarativeListView::Visible:
2955             if (itemPos > pos + size())
2956                 pos = itemPos - size() + item->size();
2957             else if (item->endPosition() < pos)
2958                 pos = itemPos;
2959             break;
2960         case QDeclarativeListView::Contain:
2961             if (item->endPosition() > pos + size())
2962                 pos = itemPos - size() + item->size();
2963             if (itemPos < pos)
2964                 pos = itemPos;
2965         }
2966         pos = qMin(pos, maxExtent);
2967         qreal minExtent;
2968         if (orient == QDeclarativeListView::Vertical) {
2969             minExtent = -q->minYExtent();
2970         } else {
2971             minExtent = isRightToLeft() ? q->maxXExtent()-size(): -q->minXExtent();
2972         }
2973         pos = qMax(pos, minExtent);
2974         moveReason = QDeclarativeListViewPrivate::Other;
2975         q->cancelFlick();
2976         setPosition(pos);
2977         if (highlight) {
2978             if (autoHighlight) {
2979                 highlight->setPosition(currentItem->itemPosition());
2980                 highlight->setSize(currentItem->itemSize());
2981             }
2982             updateHighlight();
2983         }
2984     }
2985     fixupPosition();
2986 }
2987
2988 /*!
2989     \qmlmethod ListView::positionViewAtIndex(int index, PositionMode mode)
2990
2991     Positions the view such that the \a index is at the position specified by
2992     \a mode:
2993
2994     \list
2995     \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
2996     \o ListView.Center - position item in the center of the view.
2997     \o ListView.End - position item at bottom (or right for horizontal orientation) of the view.
2998     \o ListView.Visible - if any part of the item is visible then take no action, otherwise
2999     bring the item into view.
3000     \o ListView.Contain - ensure the entire item is visible.  If the item is larger than
3001     the view the item is positioned at the top (or left for horizontal orientation) of the view.
3002     \endlist
3003
3004     If positioning the view at \a index would cause empty space to be displayed at
3005     the beginning or end of the view, the view will be positioned at the boundary.
3006
3007     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3008     at a particular index.  This is unreliable since removing items from the start
3009     of the list does not cause all other items to be repositioned, and because
3010     the actual start of the view can vary based on the size of the delegates.
3011     The correct way to bring an item into view is with \c positionViewAtIndex.
3012
3013     \bold Note: methods should only be called after the Component has completed.  To position
3014     the view at startup, this method should be called by Component.onCompleted.  For
3015     example, to position the view at the end:
3016
3017     \code
3018     Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
3019     \endcode
3020 */
3021 void QDeclarativeListView::positionViewAtIndex(int index, int mode)
3022 {
3023     Q_D(QDeclarativeListView);
3024     if (!d->isValid() || index < 0 || index >= d->model->count())
3025         return;
3026     d->positionViewAtIndex(index, mode);
3027 }
3028
3029 /*!
3030     \qmlmethod ListView::positionViewAtBeginning()
3031     \qmlmethod ListView::positionViewAtEnd()
3032     \since QtQuick 1.1
3033
3034     Positions the view at the beginning or end, taking into account any header or footer.
3035
3036     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3037     at a particular index.  This is unreliable since removing items from the start
3038     of the list does not cause all other items to be repositioned, and because
3039     the actual start of the view can vary based on the size of the delegates.
3040
3041     \bold Note: methods should only be called after the Component has completed.  To position
3042     the view at startup, this method should be called by Component.onCompleted.  For
3043     example, to position the view at the end on startup:
3044
3045     \code
3046     Component.onCompleted: positionViewAtEnd()
3047     \endcode
3048 */
3049 void QDeclarativeListView::positionViewAtBeginning()
3050 {
3051     Q_D(QDeclarativeListView);
3052     if (!d->isValid())
3053         return;
3054     d->positionViewAtIndex(-1, Beginning);
3055 }
3056
3057 void QDeclarativeListView::positionViewAtEnd()
3058 {
3059     Q_D(QDeclarativeListView);
3060     if (!d->isValid())
3061         return;
3062     d->positionViewAtIndex(d->model->count(), End);
3063 }
3064
3065 /*!
3066     \qmlmethod int ListView::indexAt(int x, int y)
3067
3068     Returns the index of the visible item containing the point \a x, \a y in content
3069     coordinates.  If there is no item at the point specified, or the item is
3070     not visible -1 is returned.
3071
3072     If the item is outside the visible area, -1 is returned, regardless of
3073     whether an item will exist at that point when scrolled into view.
3074
3075     \bold Note: methods should only be called after the Component has completed.
3076 */
3077 int QDeclarativeListView::indexAt(qreal x, qreal y) const
3078 {
3079     Q_D(const QDeclarativeListView);
3080     for (int i = 0; i < d->visibleItems.count(); ++i) {
3081         const FxListItem *listItem = d->visibleItems.at(i);
3082         if(listItem->contains(x, y))
3083             return listItem->index;
3084     }
3085
3086     return -1;
3087 }
3088
3089 void QDeclarativeListView::componentComplete()
3090 {
3091     Q_D(QDeclarativeListView);
3092     QDeclarativeFlickable::componentComplete();
3093     updateSections();
3094     d->updateHeader();
3095     d->updateFooter();
3096     if (d->isValid()) {
3097         refill();
3098         d->moveReason = QDeclarativeListViewPrivate::SetIndex;
3099         if (d->currentIndex < 0 && !d->currentIndexCleared)
3100             d->updateCurrent(0);
3101         else
3102             d->updateCurrent(d->currentIndex);
3103         if (d->highlight && d->currentItem) {
3104             if (d->autoHighlight)
3105                 d->highlight->setPosition(d->currentItem->position());
3106             d->updateTrackedItem();
3107         }
3108         d->moveReason = QDeclarativeListViewPrivate::Other;
3109         d->fixupPosition();
3110     }
3111 }
3112
3113 void QDeclarativeListView::updateSections()
3114 {
3115     Q_D(QDeclarativeListView);
3116     if (isComponentComplete() && d->model) {
3117         QList<QByteArray> roles;
3118         if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
3119             roles << d->sectionCriteria->property().toUtf8();
3120         d->model->setWatchedRoles(roles);
3121         d->updateSections();
3122         if (d->itemCount)
3123             d->layout();
3124     }
3125 }
3126
3127 void QDeclarativeListView::refill()
3128 {
3129     Q_D(QDeclarativeListView);
3130     if (d->isRightToLeft())
3131         d->refill(-d->position()-d->size()+1, -d->position());
3132     else
3133         d->refill(d->position(), d->position()+d->size()-1);
3134 }
3135
3136 void QDeclarativeListView::trackedPositionChanged()
3137 {
3138     Q_D(QDeclarativeListView);
3139     if (!d->trackedItem || !d->currentItem)
3140         return;
3141     if (d->moveReason == QDeclarativeListViewPrivate::SetIndex) {
3142         qreal trackedPos = qCeil(d->trackedItem->position());
3143         qreal trackedSize = d->trackedItem->size();
3144         if (d->trackedItem != d->currentItem) {
3145             trackedPos -= d->currentItem->sectionSize();
3146             trackedSize += d->currentItem->sectionSize();
3147         }
3148         qreal viewPos;
3149         qreal highlightStart;
3150         qreal highlightEnd;
3151         if (d->isRightToLeft()) {
3152             viewPos = -d->position()-d->size();
3153             highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
3154             highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
3155         } else {
3156             viewPos = d->position();
3157             highlightStart = d->highlightRangeStart;
3158             highlightEnd = d->highlightRangeEnd;
3159         }
3160         qreal pos = viewPos;
3161         if (d->haveHighlightRange) {
3162             if (d->highlightRange == StrictlyEnforceRange) {
3163                 if (trackedPos > pos + highlightEnd - d->trackedItem->size())
3164                     pos = trackedPos - highlightEnd + d->trackedItem->size();
3165                 if (trackedPos < pos + highlightStart)
3166                     pos = trackedPos - highlightStart;
3167             } else {
3168                 if (trackedPos < d->startPosition() + highlightStart) {
3169                     pos = d->startPosition();
3170                 } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + highlightEnd) {
3171                     pos = d->endPosition() - d->size() + 1;
3172                     if (pos < d->startPosition())
3173                         pos = d->startPosition();
3174                 } else {
3175                     if (trackedPos < viewPos + highlightStart) {
3176                         pos = trackedPos - highlightStart;
3177                     } else if (trackedPos > viewPos + highlightEnd - trackedSize) {
3178                         pos = trackedPos - highlightEnd + trackedSize;
3179                     }
3180   &nb