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