Views do not notify count changes properly
[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         int oldCount = dataModel->count();
1817         dataModel->setDelegate(delegate);
1818         if (isComponentComplete()) {
1819             for (int i = 0; i < d->visibleItems.count(); ++i)
1820                 d->releaseItem(d->visibleItems.at(i));
1821             d->visibleItems.clear();
1822             d->releaseItem(d->currentItem);
1823             d->currentItem = 0;
1824             updateSections();
1825             refill();
1826             d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1827             d->updateCurrent(d->currentIndex);
1828             if (d->highlight && d->currentItem) {
1829                 if (d->autoHighlight)
1830                     d->highlight->setPosition(d->currentItem->position());
1831                 d->updateTrackedItem();
1832             }
1833             d->updateViewport();
1834         }
1835         if (oldCount != dataModel->count())
1836             emit countChanged();
1837     }
1838     emit delegateChanged();
1839 }
1840
1841 /*!
1842     \qmlproperty int ListView::currentIndex
1843     \qmlproperty Item ListView::currentItem
1844
1845     The \c currentIndex property holds the index of the current item, and
1846     \c currentItem holds the current item.   Setting the currentIndex to -1
1847     will clear the highlight and set currentItem to null.
1848
1849     If highlightFollowsCurrentItem is \c true, setting either of these 
1850     properties will smoothly scroll the ListView so that the current 
1851     item becomes visible.
1852     
1853     Note that the position of the current item
1854     may only be approximate until it becomes visible in the view.
1855 */
1856 int QDeclarativeListView::currentIndex() const
1857 {
1858     Q_D(const QDeclarativeListView);
1859     return d->currentIndex;
1860 }
1861
1862 void QDeclarativeListView::setCurrentIndex(int index)
1863 {
1864     Q_D(QDeclarativeListView);
1865     if (d->requestedIndex >= 0)  // currently creating item
1866         return;
1867     d->currentIndexCleared = (index == -1);
1868     if (index == d->currentIndex)
1869         return;
1870     if (isComponentComplete() && d->isValid()) {
1871         d->moveReason = QDeclarativeListViewPrivate::SetIndex;
1872         d->updateCurrent(index);
1873     } else if (d->currentIndex != index) {
1874         d->currentIndex = index;
1875         emit currentIndexChanged();
1876     }
1877 }
1878
1879 QDeclarativeItem *QDeclarativeListView::currentItem()
1880 {
1881     Q_D(QDeclarativeListView);
1882     if (!d->currentItem)
1883         return 0;
1884     return d->currentItem->item;
1885 }
1886
1887 /*!
1888   \qmlproperty Item ListView::highlightItem
1889
1890     This holds the highlight item created from the \l highlight component.
1891
1892   The \c highlightItem is managed by the view unless
1893   \l highlightFollowsCurrentItem is set to false.
1894
1895   \sa highlight, highlightFollowsCurrentItem
1896 */
1897 QDeclarativeItem *QDeclarativeListView::highlightItem()
1898 {
1899     Q_D(QDeclarativeListView);
1900     if (!d->highlight)
1901         return 0;
1902     return d->highlight->item;
1903 }
1904
1905 /*!
1906   \qmlproperty int ListView::count
1907   This property holds the number of items in the view.
1908 */
1909 int QDeclarativeListView::count() const
1910 {
1911     Q_D(const QDeclarativeListView);
1912     if (d->model)
1913         return d->model->count();
1914     return 0;
1915 }
1916
1917 /*!
1918     \qmlproperty Component ListView::highlight
1919     This property holds the component to use as the highlight.
1920
1921     An instance of the highlight component is created for each list.
1922     The geometry of the resulting component instance is managed by the list
1923     so as to stay with the current item, unless the highlightFollowsCurrentItem
1924     property is false.
1925
1926     \sa highlightItem, highlightFollowsCurrentItem, {declarative/modelviews/listview}{ListView examples}
1927 */
1928 QDeclarativeComponent *QDeclarativeListView::highlight() const
1929 {
1930     Q_D(const QDeclarativeListView);
1931     return d->highlightComponent;
1932 }
1933
1934 void QDeclarativeListView::setHighlight(QDeclarativeComponent *highlight)
1935 {
1936     Q_D(QDeclarativeListView);
1937     if (highlight != d->highlightComponent) {
1938         d->highlightComponent = highlight;
1939         d->createHighlight();
1940         if (d->currentItem)
1941             d->updateHighlight();
1942         emit highlightChanged();
1943     }
1944 }
1945
1946 /*!
1947     \qmlproperty bool ListView::highlightFollowsCurrentItem
1948     This property holds whether the highlight is managed by the view.
1949
1950     If this property is true (the default value), the highlight is moved smoothly
1951     to follow the current item.  Otherwise, the
1952     highlight is not moved by the view, and any movement must be implemented
1953     by the highlight.  
1954     
1955     Here is a highlight with its motion defined by a \l {SpringAnimation} item:
1956
1957     \snippet doc/src/snippets/declarative/listview/listview.qml highlightFollowsCurrentItem
1958
1959     Note that the highlight animation also affects the way that the view
1960     is scrolled.  This is because the view moves to maintain the
1961     highlight within the preferred highlight range (or visible viewport).
1962
1963     \sa highlight, highlightMoveSpeed
1964 */
1965 bool QDeclarativeListView::highlightFollowsCurrentItem() const
1966 {
1967     Q_D(const QDeclarativeListView);
1968     return d->autoHighlight;
1969 }
1970
1971 void QDeclarativeListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1972 {
1973     Q_D(QDeclarativeListView);
1974     if (d->autoHighlight != autoHighlight) {
1975         d->autoHighlight = autoHighlight;
1976         if (autoHighlight) {
1977             d->updateHighlight();
1978         } else {
1979             if (d->highlightPosAnimator)
1980                 d->highlightPosAnimator->stop();
1981             if (d->highlightSizeAnimator)
1982                 d->highlightSizeAnimator->stop();
1983         }
1984         emit highlightFollowsCurrentItemChanged();
1985     }
1986 }
1987
1988 //###Possibly rename these properties, since they are very useful even without a highlight?
1989 /*!
1990     \qmlproperty real ListView::preferredHighlightBegin
1991     \qmlproperty real ListView::preferredHighlightEnd
1992     \qmlproperty enumeration ListView::highlightRangeMode
1993
1994     These properties define the preferred range of the highlight (for the current item)
1995     within the view. The \c preferredHighlightBegin value must be less than the
1996     \c preferredHighlightEnd value. 
1997
1998     These properties affect the position of the current item when the list is scrolled.
1999     For example, if the currently selected item should stay in the middle of the
2000     list when the view is scrolled, set the \c preferredHighlightBegin and 
2001     \c preferredHighlightEnd values to the top and bottom coordinates of where the middle 
2002     item would be. If the \c currentItem is changed programmatically, the list will
2003     automatically scroll so that the current item is in the middle of the view.
2004     Furthermore, the behavior of the current item index will occur whether or not a
2005     highlight exists.
2006
2007     Valid values for \c highlightRangeMode are:
2008
2009     \list
2010     \o ListView.ApplyRange - the view attempts to maintain the highlight within the range.
2011        However, the highlight can move outside of the range at the ends of the list or due
2012        to mouse interaction.
2013     \o ListView.StrictlyEnforceRange - the highlight never moves outside of the range.
2014        The current item changes if a keyboard or mouse action would cause the highlight to move
2015        outside of the range.
2016     \o ListView.NoHighlightRange - this is the default value.
2017     \endlist
2018 */
2019 qreal QDeclarativeListView::preferredHighlightBegin() const
2020 {
2021     Q_D(const QDeclarativeListView);
2022     return d->highlightRangeStart;
2023 }
2024
2025 void QDeclarativeListView::setPreferredHighlightBegin(qreal start)
2026 {
2027     Q_D(QDeclarativeListView);
2028     d->highlightRangeStartValid = true;
2029     if (d->highlightRangeStart == start)
2030         return;
2031     d->highlightRangeStart = start;
2032     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2033     emit preferredHighlightBeginChanged();
2034 }
2035
2036 void QDeclarativeListView::resetPreferredHighlightBegin()
2037 {
2038     Q_D(QDeclarativeListView);
2039     d->highlightRangeStartValid = false;
2040     if (d->highlightRangeStart == 0)
2041         return;
2042     d->highlightRangeStart = 0;
2043     emit preferredHighlightBeginChanged();
2044 }
2045
2046 qreal QDeclarativeListView::preferredHighlightEnd() const
2047 {
2048     Q_D(const QDeclarativeListView);
2049     return d->highlightRangeEnd;
2050 }
2051
2052 void QDeclarativeListView::setPreferredHighlightEnd(qreal end)
2053 {
2054     Q_D(QDeclarativeListView);
2055     d->highlightRangeEndValid = true;
2056     if (d->highlightRangeEnd == end)
2057         return;
2058     d->highlightRangeEnd = end;
2059     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2060     emit preferredHighlightEndChanged();
2061 }
2062
2063 void QDeclarativeListView::resetPreferredHighlightEnd()
2064 {
2065     Q_D(QDeclarativeListView);
2066     d->highlightRangeEndValid = false;
2067     if (d->highlightRangeEnd == 0)
2068         return;
2069     d->highlightRangeEnd = 0;
2070     emit preferredHighlightEndChanged();
2071 }
2072
2073 QDeclarativeListView::HighlightRangeMode QDeclarativeListView::highlightRangeMode() const
2074 {
2075     Q_D(const QDeclarativeListView);
2076     return d->highlightRange;
2077 }
2078
2079 void QDeclarativeListView::setHighlightRangeMode(HighlightRangeMode mode)
2080 {
2081     Q_D(QDeclarativeListView);
2082     if (d->highlightRange == mode)
2083         return;
2084     d->highlightRange = mode;
2085     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
2086     emit highlightRangeModeChanged();
2087 }
2088
2089 /*!
2090     \qmlproperty real ListView::spacing
2091
2092     This property holds the spacing between items.
2093
2094     The default value is 0.
2095 */
2096 qreal QDeclarativeListView::spacing() const
2097 {
2098     Q_D(const QDeclarativeListView);
2099     return d->spacing;
2100 }
2101
2102 void QDeclarativeListView::setSpacing(qreal spacing)
2103 {
2104     Q_D(QDeclarativeListView);
2105     if (spacing != d->spacing) {
2106         d->spacing = spacing;
2107         d->layout();
2108         emit spacingChanged();
2109     }
2110 }
2111
2112 /*!
2113     \qmlproperty enumeration ListView::orientation
2114     This property holds the orientation of the list.
2115
2116     Possible values:
2117
2118     \list
2119     \o ListView.Horizontal - Items are laid out horizontally
2120     \o ListView.Vertical (default) - Items are laid out vertically
2121     \endlist
2122
2123     \table
2124     \row
2125     \o Horizontal orientation:
2126     \image ListViewHorizontal.png
2127
2128     \row
2129     \o Vertical orientation:
2130     \image listview-highlight.png
2131     \endtable
2132 */
2133 QDeclarativeListView::Orientation QDeclarativeListView::orientation() const
2134 {
2135     Q_D(const QDeclarativeListView);
2136     return d->orient;
2137 }
2138
2139 void QDeclarativeListView::setOrientation(QDeclarativeListView::Orientation orientation)
2140 {
2141     Q_D(QDeclarativeListView);
2142     if (d->orient != orientation) {
2143         d->orient = orientation;
2144         if (d->orient == QDeclarativeListView::Vertical) {
2145             setContentWidth(-1);
2146             setFlickableDirection(VerticalFlick);
2147             setContentX(0);
2148         } else {
2149             setContentHeight(-1);
2150             setFlickableDirection(HorizontalFlick);
2151             setContentY(0);
2152         }
2153         d->regenerate();
2154         emit orientationChanged();
2155     }
2156 }
2157
2158 /*!
2159   \qmlproperty enumeration ListView::layoutDirection
2160   This property holds the layout direction of the horizontal list.
2161
2162   Possible values:
2163
2164   \list
2165   \o Qt.LeftToRight (default) - Items will be laid out from left to right.
2166   \o Qt.RightToLeft - Items will be laid out from right to let.
2167   \endlist
2168
2169   \sa ListView::effectiveLayoutDirection
2170 */
2171
2172 Qt::LayoutDirection QDeclarativeListView::layoutDirection() const
2173 {
2174     Q_D(const QDeclarativeListView);
2175     return d->layoutDirection;
2176 }
2177
2178 void QDeclarativeListView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
2179 {
2180     Q_D(QDeclarativeListView);
2181     if (d->layoutDirection != layoutDirection) {
2182         d->layoutDirection = layoutDirection;
2183         d->regenerate();
2184         emit layoutDirectionChanged();
2185         emit effectiveLayoutDirectionChanged();
2186     }
2187 }
2188
2189 /*!
2190     \qmlproperty enumeration ListView::effectiveLayoutDirection
2191     This property holds the effective layout direction of the horizontal list.
2192
2193     When using the attached property \l {LayoutMirroring::enabled}{LayoutMirroring::enabled} for locale layouts,
2194     the visual layout direction of the horizontal list will be mirrored. However, the
2195     property \l {ListView::layoutDirection}{layoutDirection} will remain unchanged.
2196
2197     \sa ListView::layoutDirection, {LayoutMirroring}{LayoutMirroring}
2198 */
2199
2200 Qt::LayoutDirection QDeclarativeListView::effectiveLayoutDirection() const
2201 {
2202     Q_D(const QDeclarativeListView);
2203     if (d->effectiveLayoutMirror)
2204         return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
2205     else
2206         return d->layoutDirection;
2207 }
2208
2209 /*!
2210     \qmlproperty bool ListView::keyNavigationWraps
2211     This property holds whether the list wraps key navigation. 
2212
2213     If this is true, key navigation that would move the current item selection
2214     past the end of the list instead wraps around and moves the selection to
2215     the start of the list, and vice-versa.
2216
2217     By default, key navigation is not wrapped.
2218 */
2219 bool QDeclarativeListView::isWrapEnabled() const
2220 {
2221     Q_D(const QDeclarativeListView);
2222     return d->wrap;
2223 }
2224
2225 void QDeclarativeListView::setWrapEnabled(bool wrap)
2226 {
2227     Q_D(QDeclarativeListView);
2228     if (d->wrap == wrap)
2229         return;
2230     d->wrap = wrap;
2231     emit keyNavigationWrapsChanged();
2232 }
2233
2234 /*!
2235     \qmlproperty int ListView::cacheBuffer
2236     This property determines whether delegates are retained outside the
2237     visible area of the view.
2238
2239     If this value is non-zero, the view keeps as many delegates
2240     instantiated as it can fit within the buffer specified.  For example,
2241     if in a vertical view the delegate is 20 pixels high and \c cacheBuffer is
2242     set to 40, then up to 2 delegates above and 2 delegates below the visible
2243     area may be retained.
2244
2245     Note that cacheBuffer is not a pixel buffer - it only maintains additional
2246     instantiated delegates.
2247
2248     Setting this value can improve the smoothness of scrolling behavior at the expense
2249     of additional memory usage.  It is not a substitute for creating efficient
2250     delegates; the fewer elements in a delegate, the faster a view can be
2251     scrolled.
2252 */
2253 int QDeclarativeListView::cacheBuffer() const
2254 {
2255     Q_D(const QDeclarativeListView);
2256     return d->buffer;
2257 }
2258
2259 void QDeclarativeListView::setCacheBuffer(int b)
2260 {
2261     Q_D(QDeclarativeListView);
2262     if (d->buffer != b) {
2263         d->buffer = b;
2264         if (isComponentComplete()) {
2265             d->bufferMode = QDeclarativeListViewPrivate::BufferBefore | QDeclarativeListViewPrivate::BufferAfter;
2266             refill();
2267         }
2268         emit cacheBufferChanged();
2269     }
2270 }
2271
2272 /*!
2273     \qmlproperty string ListView::section.property
2274     \qmlproperty enumeration ListView::section.criteria
2275     \qmlproperty Component ListView::section.delegate
2276
2277     These properties hold the expression to be evaluated for the \l section attached property.
2278
2279     The \l section attached property enables a ListView to be visually
2280     separated into different parts. These properties determine how sections
2281     are created.
2282     
2283     \c section.property holds the name of the property that is the basis
2284     of each section.
2285
2286     \c section.criteria holds the criteria for forming each section based on
2287     \c section.property. This value can be one of:
2288
2289     \list
2290     \o ViewSection.FullString (default) - sections are created based on the 
2291     \c section.property value.
2292     \o ViewSection.FirstCharacter - sections are created based on the first
2293     character of the \c section.property value (for example, 'A', 'B', 'C' 
2294     sections, etc. for an address book)
2295     \endlist
2296
2297     \c section.delegate holds the delegate component for each section.
2298
2299     Each item in the list has attached properties named \c ListView.section,
2300     \c ListView.previousSection and \c ListView.nextSection.  These may be
2301     used to place a section header for related items.
2302
2303     For example, here is a ListView that displays a list of animals, separated 
2304     into sections. Each item in the ListView is placed in a different section 
2305     depending on the "size" property of the model item. The \c sectionHeading
2306     delegate component provides the light blue bar that marks the beginning of
2307     each section.
2308
2309        
2310     \snippet examples/declarative/modelviews/listview/sections.qml 0
2311
2312     \image qml-listview-sections-example.png
2313
2314     \note Adding sections to a ListView does not automatically re-order the
2315     list items by the section criteria.
2316     If the model is not ordered by section, then it is possible that
2317     the sections created will not be unique; each boundary between
2318     differing sections will result in a section header being created
2319     even if that section exists elsewhere.
2320
2321     \sa {declarative/modelviews/listview}{ListView examples}
2322 */
2323 QDeclarativeViewSection *QDeclarativeListView::sectionCriteria()
2324 {
2325     Q_D(QDeclarativeListView);
2326     if (!d->sectionCriteria) {
2327         d->sectionCriteria = new QDeclarativeViewSection(this);
2328         connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
2329     }
2330     return d->sectionCriteria;
2331 }
2332
2333 /*!
2334     \qmlproperty string ListView::currentSection
2335     This property holds the section that is currently at the beginning of the view.
2336 */
2337 QString QDeclarativeListView::currentSection() const
2338 {
2339     Q_D(const QDeclarativeListView);
2340     return d->currentSection;
2341 }
2342
2343 /*!
2344     \qmlproperty real ListView::highlightMoveSpeed
2345     \qmlproperty int ListView::highlightMoveDuration
2346     \qmlproperty real ListView::highlightResizeSpeed
2347     \qmlproperty int ListView::highlightResizeDuration
2348
2349     These properties hold the move and resize animation speed of the highlight delegate.
2350
2351     \l highlightFollowsCurrentItem must be true for these properties
2352     to have effect.
2353
2354     The default value for the speed properties is 400 pixels/second.
2355     The default value for the duration properties is -1, i.e. the
2356     highlight will take as much time as necessary to move at the set speed.
2357
2358     These properties have the same characteristics as a SmoothedAnimation.
2359
2360     \sa highlightFollowsCurrentItem
2361 */
2362 qreal QDeclarativeListView::highlightMoveSpeed() const
2363 {
2364     Q_D(const QDeclarativeListView);\
2365     return d->highlightMoveSpeed;
2366 }
2367
2368 void QDeclarativeListView::setHighlightMoveSpeed(qreal speed)
2369 {
2370     Q_D(QDeclarativeListView);\
2371     if (d->highlightMoveSpeed != speed) {
2372         d->highlightMoveSpeed = speed;
2373         if (d->highlightPosAnimator)
2374             d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
2375         emit highlightMoveSpeedChanged();
2376     }
2377 }
2378
2379 int QDeclarativeListView::highlightMoveDuration() const
2380 {
2381     Q_D(const QDeclarativeListView);
2382     return d->highlightMoveDuration;
2383 }
2384
2385 void QDeclarativeListView::setHighlightMoveDuration(int duration)
2386 {
2387     Q_D(QDeclarativeListView);\
2388     if (d->highlightMoveDuration != duration) {
2389         d->highlightMoveDuration = duration;
2390         if (d->highlightPosAnimator)
2391             d->highlightPosAnimator->userDuration = d->highlightMoveDuration;
2392         emit highlightMoveDurationChanged();
2393     }
2394 }
2395
2396 qreal QDeclarativeListView::highlightResizeSpeed() const
2397 {
2398     Q_D(const QDeclarativeListView);\
2399     return d->highlightResizeSpeed;
2400 }
2401
2402 void QDeclarativeListView::setHighlightResizeSpeed(qreal speed)
2403 {
2404     Q_D(QDeclarativeListView);\
2405     if (d->highlightResizeSpeed != speed) {
2406         d->highlightResizeSpeed = speed;
2407         if (d->highlightSizeAnimator)
2408             d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
2409         emit highlightResizeSpeedChanged();
2410     }
2411 }
2412
2413 int QDeclarativeListView::highlightResizeDuration() const
2414 {
2415     Q_D(const QDeclarativeListView);
2416     return d->highlightResizeDuration;
2417 }
2418
2419 void QDeclarativeListView::setHighlightResizeDuration(int duration)
2420 {
2421     Q_D(QDeclarativeListView);\
2422     if (d->highlightResizeDuration != duration) {
2423         d->highlightResizeDuration = duration;
2424         if (d->highlightSizeAnimator)
2425             d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
2426         emit highlightResizeDurationChanged();
2427     }
2428 }
2429
2430 /*!
2431     \qmlproperty enumeration ListView::snapMode
2432
2433     This property determines how the view scrolling will settle following a drag or flick.
2434     The possible values are:
2435
2436     \list
2437     \o ListView.NoSnap (default) - the view stops anywhere within the visible area.
2438     \o ListView.SnapToItem - the view settles with an item aligned with the start of
2439     the view.
2440     \o ListView.SnapOneItem - the view settles no more than one item away from the first
2441     visible item at the time the mouse button is released.  This mode is particularly
2442     useful for moving one page at a time.
2443     \endlist
2444
2445     \c snapMode does not affect the \l currentIndex.  To update the
2446     \l currentIndex as the list is moved, set \l highlightRangeMode
2447     to \c ListView.StrictlyEnforceRange.
2448
2449     \sa highlightRangeMode
2450 */
2451 QDeclarativeListView::SnapMode QDeclarativeListView::snapMode() const
2452 {
2453     Q_D(const QDeclarativeListView);
2454     return d->snapMode;
2455 }
2456
2457 void QDeclarativeListView::setSnapMode(SnapMode mode)
2458 {
2459     Q_D(QDeclarativeListView);
2460     if (d->snapMode != mode) {
2461         d->snapMode = mode;
2462         emit snapModeChanged();
2463     }
2464 }
2465
2466 /*!
2467     \qmlproperty Component ListView::footer
2468     This property holds the component to use as the footer.
2469
2470     An instance of the footer component is created for each view.  The
2471     footer is positioned at the end of the view, after any items.
2472
2473     \sa header
2474 */
2475 QDeclarativeComponent *QDeclarativeListView::footer() const
2476 {
2477     Q_D(const QDeclarativeListView);
2478     return d->footerComponent;
2479 }
2480
2481 void QDeclarativeListView::setFooter(QDeclarativeComponent *footer)
2482 {
2483     Q_D(QDeclarativeListView);
2484     if (d->footerComponent != footer) {
2485         if (d->footer) {
2486             if (scene())
2487                 scene()->removeItem(d->footer->item);
2488             d->footer->item->deleteLater();
2489             delete d->footer;
2490             d->footer = 0;
2491         }
2492         d->footerComponent = footer;
2493         d->minExtentDirty = true;
2494         d->maxExtentDirty = true;
2495         if (isComponentComplete()) {
2496             d->updateFooter();
2497             d->updateViewport();
2498             d->fixupPosition();
2499         }
2500         emit footerChanged();
2501     }
2502 }
2503
2504 /*!
2505     \qmlproperty Component ListView::header
2506     This property holds the component to use as the header.
2507
2508     An instance of the header component is created for each view.  The
2509     header is positioned at the beginning of the view, before any items.
2510
2511     \sa footer
2512 */
2513 QDeclarativeComponent *QDeclarativeListView::header() const
2514 {
2515     Q_D(const QDeclarativeListView);
2516     return d->headerComponent;
2517 }
2518
2519 void QDeclarativeListView::setHeader(QDeclarativeComponent *header)
2520 {
2521     Q_D(QDeclarativeListView);
2522     if (d->headerComponent != header) {
2523         if (d->header) {
2524             if (scene())
2525                 scene()->removeItem(d->header->item);
2526             d->header->item->deleteLater();
2527             delete d->header;
2528             d->header = 0;
2529         }
2530         d->headerComponent = header;
2531         d->minExtentDirty = true;
2532         d->maxExtentDirty = true;
2533         if (isComponentComplete()) {
2534             d->updateHeader();
2535             d->updateFooter();
2536             d->updateViewport();
2537             d->fixupPosition();
2538         }
2539         emit headerChanged();
2540     }
2541 }
2542
2543 void QDeclarativeListView::setContentX(qreal pos)
2544 {
2545     Q_D(QDeclarativeListView);
2546     // Positioning the view manually should override any current movement state
2547     d->moveReason = QDeclarativeListViewPrivate::Other;
2548     QDeclarativeFlickable::setContentX(pos);
2549 }
2550
2551 void QDeclarativeListView::setContentY(qreal pos)
2552 {
2553     Q_D(QDeclarativeListView);
2554     // Positioning the view manually should override any current movement state
2555     d->moveReason = QDeclarativeListViewPrivate::Other;
2556     QDeclarativeFlickable::setContentY(pos);
2557 }
2558
2559 bool QDeclarativeListView::event(QEvent *event)
2560 {
2561     Q_D(QDeclarativeListView);
2562     if (event->type() == QEvent::User) {
2563         d->layout();
2564         return true;
2565     }
2566
2567     return QDeclarativeFlickable::event(event);
2568 }
2569
2570 void QDeclarativeListView::viewportMoved()
2571 {
2572     Q_D(QDeclarativeListView);
2573     QDeclarativeFlickable::viewportMoved();
2574     if (!d->itemCount)
2575         return;
2576     // Recursion can occur due to refill changing the content size.
2577     if (d->inViewportMoved)
2578         return;
2579     d->inViewportMoved = true;
2580     d->lazyRelease = true;
2581     refill();
2582     if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically)
2583         d->moveReason = QDeclarativeListViewPrivate::Mouse;
2584     if (d->moveReason != QDeclarativeListViewPrivate::SetIndex) {
2585         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2586             // reposition highlight
2587             qreal pos = d->highlight->position();
2588             qreal viewPos;
2589             qreal highlightStart;
2590             qreal highlightEnd;
2591             if (d->isRightToLeft()) {
2592                 // Handle Right-To-Left exceptions
2593                 viewPos = -d->position()-d->size();
2594                 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
2595                 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
2596             } else {
2597                 viewPos = d->position();
2598                 highlightStart = d->highlightRangeStart;
2599                 highlightEnd = d->highlightRangeEnd;
2600             }
2601             if (pos > viewPos + highlightEnd - d->highlight->size())
2602                 pos = viewPos + highlightEnd - d->highlight->size();
2603             if (pos < viewPos + highlightStart)
2604                 pos = viewPos + highlightStart;
2605             d->highlightPosAnimator->stop();
2606             d->highlight->setPosition(qRound(pos));
2607
2608             // update current index
2609             if (FxListItem *snapItem = d->snapItemAt(d->highlight->position())) {
2610                 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2611                     d->updateCurrent(snapItem->index);
2612             }
2613         }
2614     }
2615
2616     if ((d->flickingHorizontally || d->flickingVertically) && d->correctFlick && !d->inFlickCorrection) {
2617         d->inFlickCorrection = true;
2618         // Near an end and it seems that the extent has changed?
2619         // Recalculate the flick so that we don't end up in an odd position.
2620         if (yflick() && !d->vData.inOvershoot) {
2621             if (d->vData.velocity > 0) {
2622                 const qreal minY = minYExtent();
2623                 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2624                     && minY != d->vData.flickTarget)
2625                     d->flickY(-d->vData.smoothVelocity.value());
2626                 d->bufferMode = QDeclarativeListViewPrivate::BufferBefore;
2627             } else if (d->vData.velocity < 0) {
2628                 const qreal maxY = maxYExtent();
2629                 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2630                     && maxY != d->vData.flickTarget)
2631                     d->flickY(-d->vData.smoothVelocity.value());
2632                 d->bufferMode = QDeclarativeListViewPrivate::BufferAfter;
2633             }
2634         }
2635
2636         if (xflick() && !d->hData.inOvershoot) {
2637             if (d->hData.velocity > 0) {
2638                 const qreal minX = minXExtent();
2639                 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2640                     && minX != d->hData.flickTarget)
2641                     d->flickX(-d->hData.smoothVelocity.value());
2642                 d->bufferMode = d->isRightToLeft()
2643                         ? QDeclarativeListViewPrivate::BufferAfter : QDeclarativeListViewPrivate::BufferBefore;
2644             } else if (d->hData.velocity < 0) {
2645                 const qreal maxX = maxXExtent();
2646                 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2647                     && maxX != d->hData.flickTarget)
2648                     d->flickX(-d->hData.smoothVelocity.value());
2649                 d->bufferMode = d->isRightToLeft()
2650                         ? QDeclarativeListViewPrivate::BufferBefore : QDeclarativeListViewPrivate::BufferAfter;
2651             }
2652         }
2653         d->inFlickCorrection = false;
2654     }
2655     d->inViewportMoved = false;
2656 }
2657
2658 qreal QDeclarativeListView::minYExtent() const
2659 {
2660     Q_D(const QDeclarativeListView);
2661     if (d->orient == QDeclarativeListView::Horizontal)
2662         return QDeclarativeFlickable::minYExtent();
2663     if (d->minExtentDirty) {
2664         d->minExtent = -d->startPosition();
2665         if (d->header && d->visibleItems.count())
2666             d->minExtent += d->header->size();
2667         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2668             d->minExtent += d->highlightRangeStart;
2669             if (d->sectionCriteria) {
2670                 if (d->visibleItem(0))
2671                     d->minExtent -= d->visibleItem(0)->sectionSize();
2672             }
2673             d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1));
2674         }
2675         d->minExtentDirty = false;
2676     }
2677
2678     return d->minExtent;
2679 }
2680
2681 qreal QDeclarativeListView::maxYExtent() const
2682 {
2683     Q_D(const QDeclarativeListView);
2684     if (d->orient == QDeclarativeListView::Horizontal)
2685         return height();
2686     if (d->maxExtentDirty) {
2687         if (!d->model || !d->model->count()) {
2688             d->maxExtent = d->header ? -d->header->size() : 0;
2689             d->maxExtent += height();
2690         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2691             d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
2692             if (d->highlightRangeEnd != d->highlightRangeStart)
2693                 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd + 1));
2694         } else {
2695             d->maxExtent = -(d->endPosition() - height() + 1);
2696         }
2697         if (d->footer)
2698             d->maxExtent -= d->footer->size();
2699         qreal minY = minYExtent();
2700         if (d->maxExtent > minY)
2701             d->maxExtent = minY;
2702         d->maxExtentDirty = false;
2703     }
2704     return d->maxExtent;
2705 }
2706
2707 qreal QDeclarativeListView::minXExtent() const
2708 {
2709     Q_D(const QDeclarativeListView);
2710     if (d->orient == QDeclarativeListView::Vertical)
2711         return QDeclarativeFlickable::minXExtent();
2712     if (d->minExtentDirty) {
2713         d->minExtent = -d->startPosition();
2714
2715         qreal highlightStart;
2716         qreal highlightEnd;
2717         qreal endPositionFirstItem = 0;
2718         if (d->isRightToLeft()) {
2719             if (d->model && d->model->count())
2720                 endPositionFirstItem = d->positionAt(d->model->count()-1);
2721             else if (d->header)
2722                 d->minExtent += d->header->size();
2723             highlightStart = d->highlightRangeStartValid
2724                     ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
2725                     : d->size() - (d->lastPosition()-endPositionFirstItem);
2726             highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
2727             if (d->footer)
2728                 d->minExtent += d->footer->size();
2729             qreal maxX = maxXExtent();
2730             if (d->minExtent < maxX)
2731                 d->minExtent = maxX;
2732         } else {
2733             endPositionFirstItem = d->endPositionAt(0);
2734             highlightStart = d->highlightRangeStart;
2735             highlightEnd = d->highlightRangeEnd;
2736             if (d->header && d->visibleItems.count())
2737                 d->minExtent += d->header->size();
2738         }
2739         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2740             d->minExtent += highlightStart;
2741             d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd + 1));
2742         }
2743         d->minExtentDirty = false;
2744     }
2745
2746     return d->minExtent;
2747 }
2748
2749 qreal QDeclarativeListView::maxXExtent() const
2750 {
2751     Q_D(const QDeclarativeListView);
2752     if (d->orient == QDeclarativeListView::Vertical)
2753         return width();
2754     if (d->maxExtentDirty) {
2755         qreal highlightStart;
2756         qreal highlightEnd;
2757         qreal lastItemPosition = 0;
2758         d->maxExtent = 0;
2759         if (d->isRightToLeft()) {
2760             highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
2761             highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
2762             lastItemPosition = d->endPosition();
2763         } else {
2764             highlightStart = d->highlightRangeStart;
2765             highlightEnd = d->highlightRangeEnd;
2766             if (d->model && d->model->count())
2767                 lastItemPosition = d->positionAt(d->model->count()-1);
2768         }
2769         if (!d->model || !d->model->count()) {
2770             if (!d->isRightToLeft())
2771                 d->maxExtent = d->header ? -d->header->size() : 0;
2772             d->maxExtent += width();
2773         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2774             d->maxExtent = -(lastItemPosition - highlightStart);
2775             if (highlightEnd != highlightStart) {
2776                 d->maxExtent = d->isRightToLeft()
2777                         ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd + 1))
2778                         : qMin(d->maxExtent, -(d->endPosition() - highlightEnd + 1));
2779             }
2780         } else {
2781             d->maxExtent = -(d->endPosition() - width() + 1);
2782         }
2783         if (d->isRightToLeft()) {
2784             if (d->header && d->visibleItems.count())
2785                 d->maxExtent -= d->header->size();
2786         } else {
2787             if (d->footer)
2788                 d->maxExtent -= d->footer->size();
2789             qreal minX = minXExtent();
2790             if (d->maxExtent > minX)
2791                 d->maxExtent = minX;
2792         }
2793         d->maxExtentDirty = false;
2794     }
2795     return d->maxExtent;
2796 }
2797
2798 void QDeclarativeListView::keyPressEvent(QKeyEvent *event)
2799 {
2800     Q_D(QDeclarativeListView);
2801     keyPressPreHandler(event);
2802     if (event->isAccepted())
2803         return;
2804
2805     if (d->model && d->model->count() && d->interactive) {
2806         if ((d->orient == QDeclarativeListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Left)
2807                     || (d->orient == QDeclarativeListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2808                     || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Up)) {
2809             if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2810                 decrementCurrentIndex();
2811                 event->accept();
2812                 return;
2813             } else if (d->wrap) {
2814                 event->accept();
2815                 return;
2816             }
2817         } else if ((d->orient == QDeclarativeListView::Horizontal && !d->isRightToLeft() && event->key() == Qt::Key_Right)
2818                     || (d->orient == QDeclarativeListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2819                     || (d->orient == QDeclarativeListView::Vertical && event->key() == Qt::Key_Down)) {
2820             if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2821                 incrementCurrentIndex();
2822                 event->accept();
2823                 return;
2824             } else if (d->wrap) {
2825                 event->accept();
2826                 return;
2827             }
2828         }
2829     }
2830     event->ignore();
2831     QDeclarativeFlickable::keyPressEvent(event);
2832 }
2833
2834 void QDeclarativeListView::geometryChanged(const QRectF &newGeometry,
2835                              const QRectF &oldGeometry)
2836 {
2837     Q_D(QDeclarativeListView);
2838     d->maxExtentDirty = true;
2839     d->minExtentDirty = true;
2840     if (d->isRightToLeft() && d->orient == QDeclarativeListView::Horizontal) {
2841         // maintain position relative to the right edge
2842         int dx = newGeometry.width() - oldGeometry.width();
2843         setContentX(contentX() - dx);
2844     }
2845     QDeclarativeFlickable::geometryChanged(newGeometry, oldGeometry);
2846 }
2847
2848
2849 /*!
2850     \qmlmethod ListView::incrementCurrentIndex()
2851
2852     Increments the current index.  The current index will wrap
2853     if keyNavigationWraps is true and it is currently at the end.
2854     This method has no effect if the \l count is zero.
2855
2856     \bold Note: methods should only be called after the Component has completed.
2857 */
2858 void QDeclarativeListView::incrementCurrentIndex()
2859 {
2860     Q_D(QDeclarativeListView);
2861     int count = d->model ? d->model->count() : 0;
2862     if (count && (currentIndex() < count - 1 || d->wrap)) {
2863         d->moveReason = QDeclarativeListViewPrivate::SetIndex;
2864         int index = currentIndex()+1;
2865         setCurrentIndex((index >= 0 && index < count) ? index : 0);
2866     }
2867 }
2868
2869 /*!
2870     \qmlmethod ListView::decrementCurrentIndex()
2871
2872     Decrements the current index.  The current index will wrap
2873     if keyNavigationWraps is true and it is currently at the beginning.
2874     This method has no effect if the \l count is zero.
2875
2876     \bold Note: methods should only be called after the Component has completed.
2877 */
2878 void QDeclarativeListView::decrementCurrentIndex()
2879 {
2880     Q_D(QDeclarativeListView);
2881     int count = d->model ? d->model->count() : 0;
2882     if (count && (currentIndex() > 0 || d->wrap)) {
2883         d->moveReason = QDeclarativeListViewPrivate::SetIndex;
2884         int index = currentIndex()-1;
2885         setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2886     }
2887 }
2888
2889 void QDeclarativeListViewPrivate::positionViewAtIndex(int index, int mode)
2890 {
2891     Q_Q(QDeclarativeListView);
2892     if (!isValid())
2893         return;
2894     if (mode < QDeclarativeListView::Beginning || mode > QDeclarativeListView::Contain)
2895         return;
2896     int idx = qMax(qMin(index, model->count()-1), 0);
2897
2898     if (layoutScheduled)
2899         layout();
2900     qreal pos = isRightToLeft() ? -position() - size() : position();
2901     FxListItem *item = visibleItem(idx);
2902     qreal maxExtent;
2903     if (orient == QDeclarativeListView::Vertical)
2904         maxExtent = -q->maxYExtent();
2905     else
2906         maxExtent = isRightToLeft() ? q->minXExtent()-size(): -q->maxXExtent();
2907
2908     if (!item) {
2909         int itemPos = positionAt(idx);
2910         // save the currently visible items in case any of them end up visible again
2911         QList<FxListItem*> oldVisible = visibleItems;
2912         visibleItems.clear();
2913         visiblePos = itemPos;
2914         visibleIndex = idx;
2915         setPosition(qMin(qreal(itemPos), maxExtent));
2916         // now release the reference to all the old visible items.
2917         for (int i = 0; i < oldVisible.count(); ++i)
2918             releaseItem(oldVisible.at(i));
2919         item = visibleItem(idx);
2920     }
2921     if (item) {
2922         const qreal itemPos = item->position();
2923         switch (mode) {
2924         case QDeclarativeListView::Beginning:
2925             pos = itemPos;
2926             if (index < 0 && header)
2927                 pos -= header->size();
2928             break;
2929         case QDeclarativeListView::Center:
2930             pos = itemPos - (size() - item->size())/2;
2931             break;
2932         case QDeclarativeListView::End:
2933             pos = itemPos - size() + item->size();
2934             if (index >= model->count() && footer)
2935                 pos += footer->size();
2936             break;
2937         case QDeclarativeListView::Visible:
2938             if (itemPos > pos + size())
2939                 pos = itemPos - size() + item->size();
2940             else if (item->endPosition() < pos)
2941                 pos = itemPos;
2942             break;
2943         case QDeclarativeListView::Contain:
2944             if (item->endPosition() > pos + size())
2945                 pos = itemPos - size() + item->size();
2946             if (itemPos < pos)
2947                 pos = itemPos;
2948         }
2949         pos = qMin(pos, maxExtent);
2950         qreal minExtent;
2951         if (orient == QDeclarativeListView::Vertical) {
2952             minExtent = -q->minYExtent();
2953         } else {
2954             minExtent = isRightToLeft() ? q->maxXExtent()-size(): -q->minXExtent();
2955         }
2956         pos = qMax(pos, minExtent);
2957         moveReason = QDeclarativeListViewPrivate::Other;
2958         q->cancelFlick();
2959         setPosition(pos);
2960         if (highlight) {
2961             if (autoHighlight) {
2962                 highlight->setPosition(currentItem->itemPosition());
2963                 highlight->setSize(currentItem->itemSize());
2964             }
2965             updateHighlight();
2966         }
2967     }
2968     fixupPosition();
2969 }
2970
2971 /*!
2972     \qmlmethod ListView::positionViewAtIndex(int index, PositionMode mode)
2973
2974     Positions the view such that the \a index is at the position specified by
2975     \a mode:
2976
2977     \list
2978     \o ListView.Beginning - position item at the top (or left for horizontal orientation) of the view.
2979     \o ListView.Center - position item in the center of the view.
2980     \o ListView.End - position item at bottom (or right for horizontal orientation) of the view.
2981     \o ListView.Visible - if any part of the item is visible then take no action, otherwise
2982     bring the item into view.
2983     \o ListView.Contain - ensure the entire item is visible.  If the item is larger than
2984     the view the item is positioned at the top (or left for horizontal orientation) of the view.
2985     \endlist
2986
2987     If positioning the view at \a index would cause empty space to be displayed at
2988     the beginning or end of the view, the view will be positioned at the boundary.
2989
2990     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
2991     at a particular index.  This is unreliable since removing items from the start
2992     of the list does not cause all other items to be repositioned, and because
2993     the actual start of the view can vary based on the size of the delegates.
2994     The correct way to bring an item into view is with \c positionViewAtIndex.
2995
2996     \bold Note: methods should only be called after the Component has completed.  To position
2997     the view at startup, this method should be called by Component.onCompleted.  For
2998     example, to position the view at the end:
2999
3000     \code
3001     Component.onCompleted: positionViewAtIndex(count - 1, ListView.Beginning)
3002     \endcode
3003 */
3004 void QDeclarativeListView::positionViewAtIndex(int index, int mode)
3005 {
3006     Q_D(QDeclarativeListView);
3007     if (!d->isValid() || index < 0 || index >= d->model->count())
3008         return;
3009     d->positionViewAtIndex(index, mode);
3010 }
3011
3012 /*!
3013     \qmlmethod ListView::positionViewAtBeginning()
3014     \qmlmethod ListView::positionViewAtEnd()
3015     \since Quick 1.1
3016
3017     Positions the view at the beginning or end, taking into account any header or footer.
3018
3019     It is not recommended to use \l {Flickable::}{contentX} or \l {Flickable::}{contentY} to position the view
3020     at a particular index.  This is unreliable since removing items from the start
3021     of the list does not cause all other items to be repositioned, and because
3022     the actual start of the view can vary based on the size of the delegates.
3023
3024     \bold Note: methods should only be called after the Component has completed.  To position
3025     the view at startup, this method should be called by Component.onCompleted.  For
3026     example, to position the view at the end on startup:
3027
3028     \code
3029     Component.onCompleted: positionViewAtEnd()
3030     \endcode
3031 */
3032 void QDeclarativeListView::positionViewAtBeginning()
3033 {
3034     Q_D(QDeclarativeListView);
3035     if (!d->isValid())
3036         return;
3037     d->positionViewAtIndex(-1, Beginning);
3038 }
3039
3040 void QDeclarativeListView::positionViewAtEnd()
3041 {
3042     Q_D(QDeclarativeListView);
3043     if (!d->isValid())
3044         return;
3045     d->positionViewAtIndex(d->model->count(), End);
3046 }
3047
3048 /*!
3049     \qmlmethod int ListView::indexAt(int x, int y)
3050
3051     Returns the index of the visible item containing the point \a x, \a y in content
3052     coordinates.  If there is no item at the point specified, or the item is
3053     not visible -1 is returned.
3054
3055     If the item is outside the visible area, -1 is returned, regardless of
3056     whether an item will exist at that point when scrolled into view.
3057
3058     \bold Note: methods should only be called after the Component has completed.
3059 */
3060 int QDeclarativeListView::indexAt(qreal x, qreal y) const
3061 {
3062     Q_D(const QDeclarativeListView);
3063     for (int i = 0; i < d->visibleItems.count(); ++i) {
3064         const FxListItem *listItem = d->visibleItems.at(i);
3065         if(listItem->contains(x, y))
3066             return listItem->index;
3067     }
3068
3069     return -1;
3070 }
3071
3072 void QDeclarativeListView::componentComplete()
3073 {
3074     Q_D(QDeclarativeListView);
3075     QDeclarativeFlickable::componentComplete();
3076     updateSections();
3077     d->updateHeader();
3078     d->updateFooter();
3079     if (d->isValid()) {
3080         refill();
3081         d->moveReason = QDeclarativeListViewPrivate::SetIndex;
3082         if (d->currentIndex < 0 && !d->currentIndexCleared)
3083             d->updateCurrent(0);
3084         else
3085             d->updateCurrent(d->currentIndex);
3086         if (d->highlight && d->currentItem) {
3087             if (d->autoHighlight)
3088                 d->highlight->setPosition(d->currentItem->position());
3089             d->updateTrackedItem();
3090         }
3091         d->moveReason = QDeclarativeListViewPrivate::Other;
3092         d->fixupPosition();
3093     }
3094 }
3095
3096 void QDeclarativeListView::updateSections()
3097 {
3098     Q_D(QDeclarativeListView);
3099     if (isComponentComplete() && d->model) {
3100         QList<QByteArray> roles;
3101         if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
3102             roles << d->sectionCriteria->property().toUtf8();
3103         d->model->setWatchedRoles(roles);
3104         d->updateSections();
3105         if (d->itemCount)
3106             d->layout();
3107     }
3108 }
3109
3110 void QDeclarativeListView::refill()
3111 {
3112     Q_D(QDeclarativeListView);
3113     if (d->isRightToLeft())
3114         d->refill(-d->position()-d->size()+1, -d->position());
3115     else
3116         d->refill(d->position(), d->position()+d->size()-1);
3117 }
3118
3119 void QDeclarativeListView::trackedPositionChanged()
3120 {
3121     Q_D(QDeclarativeListView);
3122     if (!d->trackedItem || !d->currentItem)
3123         return;
3124     if (d->moveReason == QDeclarativeListViewPrivate::SetIndex) {
3125         qreal trackedPos = qCeil(d->trackedItem->position());
3126         qreal trackedSize = d->trackedItem->size();
3127         if (d->trackedItem != d->currentItem) {
3128             trackedPos -= d->currentItem->sectionSize();
3129             trackedSize += d->currentItem->sectionSize();
3130         }
3131         qreal viewPos;
3132         qreal highlightStart;
3133         qreal highlightEnd;
3134         if (d->isRightToLeft()) {
3135             viewPos = -d->position()-d->size();
3136             highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
3137             highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
3138         } else {
3139             viewPos = d->position();
3140             highlightStart = d->highlightRangeStart;
3141             highlightEnd = d->highlightRangeEnd;
3142         }
3143         qreal pos = viewPos;
3144         if (d->haveHighlightRange) {
3145             if (d->highlightRange == StrictlyEnforceRange) {
3146                 if (trackedPos > pos + highlightEnd - d->trackedItem->size())
3147                     pos = trackedPos - highlightEnd + d->trackedItem->size();
3148                 if (trackedPos < pos + highlightStart)
3149                     pos = trackedPos - highlightStart;
3150             } else {
3151                 if (trackedPos < d->startPosition() + highlightStart) {
3152                     pos = d->startPosition();
3153                 } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + highlightEnd) {
3154                     pos = d->endPosition() - d->size() + 1;
3155                     if (pos < d->startPosition())
3156                         pos = d->startPosition();
3157                 } else {
3158                     if (trackedPos < viewPos + highlightStart) {
3159                         pos = trackedPos - highlightStart;
3160                     } else if (trackedPos > viewPos + highlightEnd - trackedSize) {
3161                         pos = trackedPos - highlightEnd + trackedSize;
3162                     }
3163                 }
3164             }
3165         } else {
3166             if (trackedPos < viewPos && d->currentItem->position() < viewPos) {
3167                 pos = d->currentItem->position() < trackedPos ? trackedPos : d->currentItem->position();
3168             } else if (d->trackedItem->endPosition() >= viewPos + d->size()
3169                         && d->currentItem->endPosition() >= viewPos + d->size()) {
3170                 if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) {
3171                     pos = d->trackedItem->endPosition() - d->size() + 1;
3172                      if (trackedSize > d->size())
3173                         pos = trackedPos;
3174                 } else {
3175                     pos = d->currentItem->endPosition() - d->size() + 1;
3176                     if (d->currentItem->size() > d->size())
3177                         pos = d->currentItem->position();
3178                 }
3179             }
3180         }
3181         if (viewPos != pos) {
3182             cancelFlick();
3183             d->calcVelocity = true;
3184             d->setPosition(pos);
3185             d->calcVelocity = false;
3186         }
3187     }
3188 }
3189
3190 void QDeclarativeListView::itemsInserted(int modelIndex, int count)
3191 {
3192     Q_D(QDeclarativeListView);
3193     if (!isComponentComplete())
3194         return;
3195     d->updateUnrequestedIndexes();
3196     d->moveReason = QDeclarativeListViewPrivate::Other;
3197
3198     qreal tempPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
3199     int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
3200
3201     if (index < 0) {
3202         int i = d->visibleItems.count() - 1;
3203         while (i > 0 && d->visibleItems.at(i)->index == -1)
3204             --i;
3205         if (i == 0 && d->visibleItems.first()->index == -1) {
3206             // there are no visible items except items marked for removal
3207             index = d->visibleItems.count();
3208         } else if (d->visibleItems.at(i)->index + 1 == modelIndex
3209             && d->visibleItems.at(i)->endPosition() < d->buffer+tempPos+d->size()-1) {
3210             // Special case of appending an item to the model.
3211             index = d->visibleItems.count();
3212         } else {
3213             if (modelIndex < d->visibleIndex) {
3214                 // Insert before visible items
3215                 d->visibleIndex += count;
3216                 for (int i = 0; i < d->visibleItems.count(); ++i) {
3217                     FxListItem *listItem = d->visibleItems.at(i);
3218                     if (listItem->index != -1 && listItem->index >= modelIndex)
3219                         listItem->index += count;
3220                 }
3221             }
3222             if (d->currentIndex >= modelIndex) {
3223                 // adjust current item index
3224                 d->currentIndex += count;
3225                 if (d->currentItem)
3226                     d->currentItem->index = d->currentIndex;
3227                 emit currentIndexChanged();
3228             }
3229             d->scheduleLayout();
3230             d->itemCount += count;
3231             emit countChanged();
3232             return;
3233         }
3234     }
3235
3236     // index can be the next item past the end of the visible items list (i.e. appended)
3237     int pos = 0;
3238     if (d->visibleItems.count()) {
3239         pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position()
3240                                                 : d->visibleItems.last()->endPosition()+d->spacing+1;
3241     } else if (d->itemCount == 0 && d->header) {
3242         pos = d->header->size();
3243     }
3244
3245     int initialPos = pos;
3246     int diff = 0;
3247     QList<FxListItem*> added;
3248     bool addedVisible = false;
3249     FxListItem *firstVisible = d->firstVisibleItem();
3250     if (firstVisible && pos < firstVisible->position()) {
3251         // Insert items before the visible item.
3252         int insertionIdx = index;
3253         int i = 0;
3254         int from = tempPos - d->buffer;
3255         for (i = count-1; i >= 0 && pos > from; --i) {
3256             if (!addedVisible) {
3257                 d->scheduleLayout();
3258                 addedVisible = true;
3259             }
3260             FxListItem *item = d->createItem(modelIndex + i);
3261             d->visibleItems.insert(insertionIdx, item);
3262             pos -= item->size() + d->spacing;
3263             item->setPosition(pos);
3264             index++;
3265         }
3266         if (i >= 0) {
3267             // If we didn't insert all our new items - anything
3268             // before the current index is not visible - remove it.
3269             while (insertionIdx--) {
3270                 FxListItem *item = d->visibleItems.takeFirst();
3271                 if (item->index != -1)
3272                     d->visibleIndex++;
3273                 d->releaseItem(item);
3274             }
3275         } else {
3276             // adjust pos of items before inserted items.
3277             for (int i = insertionIdx-1; i >= 0; i--) {
3278                 FxListItem *listItem = d->visibleItems.at(i);
3279                 listItem->setPosition(listItem->position() - (initialPos - pos));
3280             }
3281         }
3282     } else {
3283         int i = 0;
3284         int to = d->buffer+tempPos+d->size()-1;
3285         for (i = 0; i < count && pos <= to; ++i) {
3286             if (!addedVisible) {
3287                 d->scheduleLayout();
3288                 addedVisible = true;
3289             }
3290             FxListItem *item = d->createItem(modelIndex + i);
3291             d->visibleItems.insert(index, item);
3292             item->setPosition(pos);
3293             added.append(item);
3294             pos += item->size() + d->spacing;
3295             ++index;
3296         }
3297         if (i != count) {
3298             // We didn't insert all our new items, which means anything
3299             // beyond the current index is not visible - remove it.
3300             while (d->visibleItems.count() > index)
3301                 d->releaseItem(d->visibleItems.takeLast());
3302         }
3303         diff = pos - initialPos;
3304     }
3305     if (d->itemCount && d->currentIndex >= modelIndex) {
3306         // adjust current item index
3307         d->currentIndex += count;
3308         if (d->currentItem) {
3309             d->currentItem->index = d->currentIndex;
3310             d->currentItem->setPosition(d->currentItem->position() + diff);
3311         }
3312         emit currentIndexChanged();
3313     } else if (!d->itemCount && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
3314         d->updateCurrent(0);
3315     }
3316     // Update the indexes of the following visible items.
3317     for (; index < d->visibleItems.count(); ++index) {
3318         FxListItem *listItem = d->visibleItems.at(index);
3319         if (d->currentItem && listItem->item != d->currentItem->item)
3320             listItem->setPosition(listItem->position() + diff);
3321         if (listItem->index != -1)
3322             listItem->index += count;
3323     }
3324     // everything is in order now - emit add() signal
3325     for (int j = 0; j < added.count(); ++j)
3326         added.at(j)->attached->emitAdd();
3327
3328     d->updateSections();
3329     d->itemCount += count;
3330     emit countChanged();
3331 }
3332
3333 void QDeclarativeListView::itemsRemoved(int modelIndex, int count)
3334 {
3335     Q_D(QDeclarativeListView);
3336     if (!isComponentComplete())
3337         return;
3338     d->moveReason = QDeclarativeListViewPrivate::Other;
3339     d->updateUnrequestedIndexes();
3340     d->itemCount -= count;
3341
3342     FxListItem *firstVisible = d->firstVisibleItem();
3343     int preRemovedSize = 0;
3344     bool removedVisible = false;
3345     // Remove the items from the visible list, skipping anything already marked for removal
3346     QList<FxListItem*>::Iterator it = d->visibleItems.begin();
3347     while (it != d->visibleItems.end()) {
3348         FxListItem *item = *it;
3349         if (item->index == -1 || item->index < modelIndex) {
3350             // already removed, or before removed items
3351             ++it;
3352         } else if (item->index >= modelIndex + count) {
3353             // after removed items
3354             item->index -= count;
3355             ++it;
3356         } else {
3357             // removed item
3358             if (!removedVisible) {
3359                 d->scheduleLayout();
3360                 removedVisible = true;
3361             }
3362             item->attached->emitRemove();
3363             if (item->attached->delayRemove()) {
3364                 item->index = -1;
3365                 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
3366                 ++it;
3367             } else {
3368                 if (item == firstVisible)
3369                     firstVisible = 0;
3370                 if (firstVisible && item->position() < firstVisible->position())
3371                     preRemovedSize += item->size();
3372                 it = d->visibleItems.erase(it);
3373                 d->releaseItem(item);
3374             }
3375         }
3376     }
3377
3378     if (firstVisible && d->visibleItems.first() != firstVisible)
3379         d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + preRemovedSize);
3380
3381     // fix current
3382     if (d->currentIndex >= modelIndex + count) {
3383         d->currentIndex -= count;
3384         if (d->currentItem)
3385             d->currentItem->index -= count;
3386         emit currentIndexChanged();
3387     } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
3388         // current item has been removed.
3389         d->currentItem->attached->setIsCurrentItem(false);
3390         d->releaseItem(d->currentItem);
3391         d->currentItem = 0;
3392         d->currentIndex = -1;
3393         if (d->itemCount)
3394             d->updateCurrent(qMin(modelIndex, d->itemCount-1));
3395         else
3396             emit currentIndexChanged();
3397     }
3398
3399     // update visibleIndex
3400     bool haveVisibleIndex = false;
3401     for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3402         if ((*it)->index != -1) {
3403             d->visibleIndex = (*it)->index;
3404             haveVisibleIndex = true;
3405             break;
3406         }
3407     }
3408
3409     if (removedVisible && !haveVisibleIndex) {
3410         d->timeline.clear();
3411         if (d->itemCount == 0) {
3412             d->visibleIndex = 0;
3413             d->visiblePos = d->header ? d->header->size() : 0;
3414             d->setPosition(0);
3415             d->updateHeader();
3416             d->updateFooter();
3417             update();
3418         } else {
3419             if (modelIndex < d->visibleIndex)
3420                 d->visibleIndex = modelIndex+1;
3421             d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0);
3422         }
3423     }
3424
3425     d->updateSections();
3426     emit countChanged();
3427 }
3428
3429 void QDeclarativeListView::destroyRemoved()
3430 {
3431     Q_D(QDeclarativeListView);
3432     for (QList<FxListItem*>::Iterator it = d->visibleItems.begin();
3433             it != d->visibleItems.end();) {
3434         FxListItem *listItem = *it;
3435         if (listItem->index == -1 && listItem->attached->delayRemove() == false) {
3436             d->releaseItem(listItem);
3437             it = d->visibleItems.erase(it);
3438         } else {
3439             ++it;
3440         }
3441     }
3442
3443     // Correct the positioning of the items
3444     d->updateSections();
3445     d->layout();
3446 }
3447
3448 void QDeclarativeListView::itemsMoved(int from, int to, int count)
3449 {
3450     Q_D(QDeclarativeListView);
3451     if (!isComponentComplete())
3452         return;
3453     d->updateUnrequestedIndexes();
3454
3455     if (d->visibleItems.isEmpty()) {
3456         refill();
3457         return;
3458     }
3459
3460     d->moveReason = QDeclarativeListViewPrivate::Other;
3461     FxListItem *firstVisible = d->firstVisibleItem();
3462     qreal firstItemPos = firstVisible->position();
3463     QHash<int,FxListItem*> moved;
3464     int moveBy = 0;
3465
3466     QList<FxListItem*>::Iterator it = d->visibleItems.begin();
3467     while (it != d->visibleItems.end()) {
3468         FxListItem *item = *it;
3469         if (item->index >= from && item->index < from + count) {
3470             // take the items that are moving
3471             item->index += (to-from);
3472             moved.insert(item->index, item);
3473             if (item->position() < firstItemPos)
3474                 moveBy += item->size();
3475             it = d->visibleItems.erase(it);
3476         } else {
3477             // move everything after the moved items.
3478             if (item->index > from && item->index != -1)
3479                 item->index -= count;
3480             ++it;
3481         }
3482     }
3483
3484     int remaining = count;
3485     int endIndex = d->visibleIndex;
3486     it = d->visibleItems.begin();
3487     while (it != d->visibleItems.end()) {
3488         FxListItem *item = *it;
3489         if (remaining && item->index >= to && item->index < to + count) {
3490             // place items in the target position, reusing any existing items
3491             FxListItem *movedItem = moved.take(item->index);
3492             if (!movedItem)
3493                 movedItem = d->createItem(item->index);
3494             if (item->index <= firstVisible->index)
3495                 moveBy -= movedItem->size();
3496             it = d->visibleItems.insert(it, movedItem);
3497             ++it;
3498             --remaining;
3499         } else {
3500             if (item->index != -1) {
3501                 if (item->index >= to) {
3502                     // update everything after the moved items.
3503                     item->index += count;
3504                 }
3505                 endIndex = item->index;
3506             }
3507             ++it;
3508         }
3509     }
3510
3511     // If we have moved items to the end of the visible items
3512     // then add any existing moved items that we have
3513     while (FxListItem *item = moved.take(endIndex+1)) {
3514         d->visibleItems.append(item);
3515         ++endIndex;
3516     }
3517
3518     // update visibleIndex
3519     for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
3520         if ((*it)->index != -1) {
3521             d->visibleIndex = (*it)->index;
3522             break;
3523         }
3524     }
3525
3526     // Fix current index
3527     if (d->currentIndex >= 0 && d->currentItem) {
3528         int oldCurrent = d->currentIndex;
3529         d->currentIndex = d->model->indexOf(d->currentItem->item, this);
3530         if (oldCurrent != d->currentIndex) {
3531             d->currentItem->index = d->currentIndex;
3532             emit currentIndexChanged();
3533         }
3534     }
3535
3536     // Whatever moved items remain are no longer visible items.
3537     while (moved.count()) {
3538         int idx = moved.begin().key();
3539         FxListItem *item = moved.take(idx);
3540         if (d->currentItem && item->item == d->currentItem->item)
3541             item->setPosition(d->positionAt(idx));
3542         d->releaseItem(item);
3543     }
3544
3545     // Ensure we don't cause an ugly list scroll.
3546     d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + moveBy);
3547
3548     d->updateSections();
3549     d->layout();
3550 }
3551
3552 void QDeclarativeListView::itemsChanged(int, int)
3553 {
3554     Q_D(QDeclarativeListView);
3555     d->updateSections();
3556     d->layout();
3557 }
3558
3559 void QDeclarativeListView::modelReset()
3560 {
3561     Q_D(QDeclarativeListView);
3562     d->moveReason = QDeclarativeListViewPrivate::SetIndex;
3563     d->regenerate();
3564     if (d->highlight && d->currentItem) {
3565         if (d->autoHighlight)
3566             d->highlight->setPosition(d->currentItem->position());
3567         d->updateTrackedItem();
3568     }
3569     d->moveReason = QDeclarativeListViewPrivate::Other;
3570     emit countChanged();
3571 }
3572
3573 void QDeclarativeListView::createdItem(int index, QDeclarativeItem *item)
3574 {
3575     Q_D(QDeclarativeListView);
3576     if (d->requestedIndex != index) {
3577         item->setParentItem(contentItem());
3578         d->unrequestedItems.insert(item, index);
3579         if (d->orient == QDeclarativeListView::Vertical) {
3580             item->setY(d->positionAt(index));
3581         } else {
3582             if (d->isRightToLeft())
3583                 item->setX(-d->positionAt(index)-item->width());
3584             else
3585                 item->setX(d->positionAt(index));
3586         }
3587     }
3588 }
3589
3590 void QDeclarativeListView::destroyingItem(QDeclarativeItem *item)
3591 {
3592     Q_D(QDeclarativeListView);
3593     d->unrequestedItems.remove(item);
3594 }
3595
3596 void QDeclarativeListView::animStopped()
3597 {
3598     Q_D(QDeclarativeListView);
3599     d->bufferMode = QDeclarativeListViewPrivate::NoBuffer;
3600     if (d->haveHighlightRange && d->highlightRange == QDeclarativeListView::StrictlyEnforceRange)
3601         d->updateHighlight();
3602 }
3603
3604 QDeclarativeListViewAttached *QDeclarativeListView::qmlAttachedProperties(QObject *obj)
3605 {
3606     return new QDeclarativeListViewAttached(obj);
3607 }
3608
3609 QT_END_NAMESPACE