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