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