Initial import from qtquick2.
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsglistview.cpp
1 // Commit: ce38c6e3a9b7eb336cbd9cd1e9520a5000c8f8ac
2 /****************************************************************************
3 **
4 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
5 ** All rights reserved.
6 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 **
8 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 **
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** No Commercial Usage
12 ** This file contains pre-release code and may not be distributed.
13 ** You may use this file in accordance with the terms and conditions
14 ** contained in the Technology Preview License Agreement accompanying
15 ** this package.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Nokia gives you certain additional
26 ** rights.  These rights are described in the Nokia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** If you have questions regarding the use of this file, please contact
30 ** Nokia at qt-info@nokia.com.
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 **
39 ** $QT_END_LICENSE$
40 **
41 ****************************************************************************/
42
43 #include "qsglistview_p.h"
44 #include "qsgflickable_p_p.h"
45 #include "qsgvisualitemmodel_p.h"
46
47 #include <QtDeclarative/qdeclarativeexpression.h>
48 #include <QtDeclarative/qdeclarativeengine.h>
49 #include <QtDeclarative/qdeclarativeinfo.h>
50 #include <QtGui/qevent.h>
51 #include <QtCore/qmath.h>
52 #include <QtCore/qcoreapplication.h>
53
54 #include <private/qdeclarativesmoothedanimation_p_p.h>
55 #include <private/qlistmodelinterface_p.h>
56
57 QT_BEGIN_NAMESPACE
58
59 void QSGViewSection::setProperty(const QString &property)
60 {
61     if (property != m_property) {
62         m_property = property;
63         emit propertyChanged();
64     }
65 }
66
67 void QSGViewSection::setCriteria(QSGViewSection::SectionCriteria criteria)
68 {
69     if (criteria != m_criteria) {
70         m_criteria = criteria;
71         emit criteriaChanged();
72     }
73 }
74
75 void QSGViewSection::setDelegate(QDeclarativeComponent *delegate)
76 {
77     if (delegate != m_delegate) {
78         m_delegate = delegate;
79         emit delegateChanged();
80     }
81 }
82
83 QString QSGViewSection::sectionString(const QString &value)
84 {
85     if (m_criteria == FirstCharacter)
86         return value.isEmpty() ? QString() : value.at(0);
87     else
88         return value;
89 }
90
91 //----------------------------------------------------------------------------
92
93 class FxListItemSG
94 {
95 public:
96     FxListItemSG(QSGItem *i, QSGListView *v) : item(i), section(0), view(v) {
97         attached = static_cast<QSGListViewAttached*>(qmlAttachedPropertiesObject<QSGListView>(item));
98         if (attached)
99             attached->setView(view);
100     }
101     ~FxListItemSG() {}
102     qreal position() const {
103         if (section) {
104             if (view->orientation() == QSGListView::Vertical)
105                 return section->y();
106             else
107                 return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -section->width()-section->x() : section->x());
108         } else {
109             return itemPosition();
110         }
111     }
112     qreal itemPosition() const {
113         if (view->orientation() == QSGListView::Vertical)
114             return item->y();
115         else
116             return (view->effectiveLayoutDirection() == Qt::RightToLeft ? -item->width()-item->x() : item->x());
117     }
118     qreal size() const {
119         if (section)
120             return (view->orientation() == QSGListView::Vertical ? item->height()+section->height() : item->width()+section->width());
121         else
122             return (view->orientation() == QSGListView::Vertical ? item->height() : item->width());
123     }
124     qreal itemSize() const {
125         return (view->orientation() == QSGListView::Vertical ? item->height() : item->width());
126     }
127     qreal sectionSize() const {
128         if (section)
129             return (view->orientation() == QSGListView::Vertical ? section->height() : section->width());
130         return 0.0;
131     }
132     qreal endPosition() const {
133         if (view->orientation() == QSGListView::Vertical) {
134             return item->y() + (item->height() >= 1.0 ? item->height() : 1) - 1;
135         } else {
136             return (view->effectiveLayoutDirection() == Qt::RightToLeft
137                     ? -item->width()-item->x() + (item->width() >= 1.0 ? item->width() : 1)
138                     : item->x() + (item->width() >= 1.0 ? item->width() : 1)) - 1;
139         }
140     }
141     void setPosition(qreal pos) {
142         if (view->orientation() == QSGListView::Vertical) {
143             if (section) {
144                 section->setY(pos);
145                 pos += section->height();
146             }
147             item->setY(pos);
148         } else {
149             if (view->effectiveLayoutDirection() == Qt::RightToLeft) {
150                 if (section) {
151                     section->setX(-section->width()-pos);
152                     pos += section->width();
153                 }
154                 item->setX(-item->width()-pos);
155             } else {
156                 if (section) {
157                     section->setX(pos);
158                     pos += section->width();
159                 }
160                 item->setX(pos);
161             }
162         }
163     }
164     void setSize(qreal size) {
165         if (view->orientation() == QSGListView::Vertical)
166             item->setHeight(size);
167         else
168             item->setWidth(size);
169     }
170     bool contains(qreal x, qreal y) const {
171         return (x >= item->x() && x < item->x() + item->width() &&
172                 y >= item->y() && y < item->y() + item->height());
173     }
174
175     QSGItem *item;
176     QSGItem *section;
177     QSGListView *view;
178     QSGListViewAttached *attached;
179     int index;
180 };
181
182 //----------------------------------------------------------------------------
183
184 class QSGListViewPrivate : public QSGFlickablePrivate
185 {
186     Q_DECLARE_PUBLIC(QSGListView)
187
188 public:
189     QSGListViewPrivate()
190         : currentItem(0), orient(QSGListView::Vertical), layoutDirection(Qt::LeftToRight)
191         , visiblePos(0), visibleIndex(0)
192         , averageSize(100.0), currentIndex(-1), requestedIndex(-1)
193         , itemCount(0), highlightRangeStart(0), highlightRangeEnd(0)
194         , highlightComponent(0), highlight(0), trackedItem(0)
195         , moveReason(Other), buffer(0), highlightPosAnimator(0), highlightSizeAnimator(0)
196         , sectionCriteria(0), spacing(0.0)
197         , highlightMoveSpeed(400), highlightMoveDuration(-1)
198         , highlightResizeSpeed(400), highlightResizeDuration(-1), highlightRange(QSGListView::NoHighlightRange)
199         , snapMode(QSGListView::NoSnap), overshootDist(0.0)
200         , footerComponent(0), footer(0), headerComponent(0), header(0)
201         , bufferMode(BufferBefore | BufferAfter)
202         , ownModel(false), wrap(false), autoHighlight(true), haveHighlightRange(false)
203         , correctFlick(false), inFlickCorrection(false), lazyRelease(false)
204         , deferredRelease(false), layoutScheduled(false), currentIndexCleared(false)
205         , inViewportMoved(false)
206         , highlightRangeStartValid(false), highlightRangeEndValid(false)
207         , minExtentDirty(true), maxExtentDirty(true)
208     {}
209
210     void init();
211     void clear();
212     FxListItemSG *createItem(int modelIndex);
213     void releaseItem(FxListItemSG *item);
214
215     FxListItemSG *visibleItem(int modelIndex) const {
216         if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
217             for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
218                 FxListItemSG *item = visibleItems.at(i);
219                 if (item->index == modelIndex)
220                     return item;
221             }
222         }
223         return 0;
224     }
225
226     FxListItemSG *firstVisibleItem() const {
227         const qreal pos = isRightToLeft() ? -position()-size() : position();
228         for (int i = 0; i < visibleItems.count(); ++i) {
229             FxListItemSG *item = visibleItems.at(i);
230             if (item->index != -1 && item->endPosition() > pos)
231                 return item;
232         }
233         return visibleItems.count() ? visibleItems.first() : 0;
234     }
235
236     FxListItemSG *nextVisibleItem() const {
237         const qreal pos = isRightToLeft() ? -position()-size() : position();
238         bool foundFirst = false;
239         for (int i = 0; i < visibleItems.count(); ++i) {
240             FxListItemSG *item = visibleItems.at(i);
241             if (item->index != -1) {
242                 if (foundFirst)
243                     return item;
244                 else if (item->position() < pos && item->endPosition() > pos)
245                     foundFirst = true;
246             }
247         }
248         return 0;
249     }
250
251     // Returns the item before modelIndex, if created.
252     // May return an item marked for removal.
253     FxListItemSG *itemBefore(int modelIndex) const {
254         if (modelIndex < visibleIndex)
255             return 0;
256         int idx = 1;
257         int lastIndex = -1;
258         while (idx < visibleItems.count()) {
259             FxListItemSG *item = visibleItems.at(idx);
260             if (item->index != -1)
261                 lastIndex = item->index;
262             if (item->index == modelIndex)
263                 return visibleItems.at(idx-1);
264             ++idx;
265         }
266         if (lastIndex == modelIndex-1)
267             return visibleItems.last();
268         return 0;
269     }
270
271     void regenerate() {
272         Q_Q(QSGListView);
273         if (q->isComponentComplete()) {
274             if (header) {
275                 // XXX todo - the original did scene()->removeItem().  Why?
276                 header->item->setParentItem(0);
277                 header->item->deleteLater();
278                 delete header;
279                 header = 0;
280             }
281             if (footer) {
282                 // XXX todo - the original did scene()->removeItem().  Why?
283                 footer->item->setParentItem(0);
284                 footer->item->deleteLater();
285                 delete footer;
286                 footer = 0;
287             }
288             updateHeader();
289             updateFooter();
290             clear();
291             setPosition(0);
292             q->refill();
293             updateCurrent(currentIndex);
294         }
295     }
296
297     void mirrorChange() {
298         Q_Q(QSGListView);
299         regenerate();
300         emit q->effectiveLayoutDirectionChanged();
301     }
302
303     bool isRightToLeft() const {
304         Q_Q(const QSGListView);
305         return orient == QSGListView::Horizontal && q->effectiveLayoutDirection() == Qt::RightToLeft;
306     }
307
308     qreal position() const {
309         Q_Q(const QSGListView);
310         return orient == QSGListView::Vertical ? q->contentY() : q->contentX();
311     }
312     void setPosition(qreal pos) {
313         Q_Q(QSGListView);
314         if (orient == QSGListView::Vertical) {
315             q->QSGFlickable::setContentY(pos);
316         } else {
317             if (isRightToLeft())
318                 q->QSGFlickable::setContentX(-pos-size());
319             else
320                 q->QSGFlickable::setContentX(pos);
321         }
322     }
323     qreal size() const {
324         Q_Q(const QSGListView);
325         return orient == QSGListView::Vertical ? q->height() : q->width();
326     }
327
328     qreal originPosition() const {
329         qreal pos = 0;
330         if (!visibleItems.isEmpty()) {
331             pos = (*visibleItems.constBegin())->position();
332             if (visibleIndex > 0)
333                 pos -= visibleIndex * (averageSize + spacing);
334         }
335         return pos;
336     }
337
338     qreal lastPosition() const {
339         qreal pos = 0;
340         if (!visibleItems.isEmpty()) {
341             int invisibleCount = visibleItems.count() - visibleIndex;
342             for (int i = visibleItems.count()-1; i >= 0; --i) {
343                 if (visibleItems.at(i)->index != -1) {
344                     invisibleCount = model->count() - visibleItems.at(i)->index - 1;
345                     break;
346                 }
347             }
348             pos = (*(--visibleItems.constEnd()))->endPosition() + invisibleCount * (averageSize + spacing);
349         } else if (model && model->count()) {
350             pos = model->count() * averageSize + (model->count()-1) * spacing;
351         }
352         return pos;
353     }
354
355     qreal startPosition() const {
356         return isRightToLeft() ? -lastPosition()-1 : originPosition();
357     }
358
359     qreal endPosition() const {
360         return isRightToLeft() ? -originPosition()-1 : lastPosition();
361     }
362
363     qreal positionAt(int modelIndex) const {
364         if (FxListItemSG *item = visibleItem(modelIndex))
365             return item->position();
366         if (!visibleItems.isEmpty()) {
367             if (modelIndex < visibleIndex) {
368                 int count = visibleIndex - modelIndex;
369                 qreal cs = 0;
370                 if (modelIndex == currentIndex && currentItem) {
371                     cs = currentItem->size() + spacing;
372                     --count;
373                 }
374                 return (*visibleItems.constBegin())->position() - count * (averageSize + spacing) - cs;
375             } else {
376                 int idx = visibleItems.count() - 1;
377                 while (idx >= 0 && visibleItems.at(idx)->index == -1)
378                     --idx;
379                 if (idx < 0)
380                     idx = visibleIndex;
381                 else
382                     idx = visibleItems.at(idx)->index;
383                 int count = modelIndex - idx - 1;
384                 return (*(--visibleItems.constEnd()))->endPosition() + spacing + count * (averageSize + spacing) + 1;
385             }
386         }
387         return 0;
388     }
389
390     qreal endPositionAt(int modelIndex) const {
391         if (FxListItemSG *item = visibleItem(modelIndex))
392             return item->endPosition();
393         if (!visibleItems.isEmpty()) {
394             if (modelIndex < visibleIndex) {
395                 int count = visibleIndex - modelIndex;
396                 return (*visibleItems.constBegin())->position() - (count - 1) * (averageSize + spacing) - spacing - 1;
397             } else {
398                 int idx = visibleItems.count() - 1;
399                 while (idx >= 0 && visibleItems.at(idx)->index == -1)
400                     --idx;
401                 if (idx < 0)
402                     idx = visibleIndex;
403                 else
404                     idx = visibleItems.at(idx)->index;
405                 int count = modelIndex - idx - 1;
406                 return (*(--visibleItems.constEnd()))->endPosition() + count * (averageSize + spacing);
407             }
408         }
409         return 0;
410     }
411
412     QString sectionAt(int modelIndex) {
413         if (FxListItemSG *item = visibleItem(modelIndex))
414             return item->attached->section();
415
416         QString section;
417         if (sectionCriteria) {
418             QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
419             section = sectionCriteria->sectionString(propValue);
420         }
421
422         return section;
423     }
424
425     bool isValid() const {
426         return model && model->count() && model->isValid();
427     }
428
429     qreal snapPosAt(qreal pos) {
430         if (FxListItemSG *snapItem = snapItemAt(pos))
431             return snapItem->position();
432         if (visibleItems.count()) {
433             qreal firstPos = visibleItems.first()->position();
434             qreal endPos = visibleItems.last()->position();
435             if (pos < firstPos) {
436                 return firstPos - qRound((firstPos - pos) / averageSize) * averageSize;
437             } else if (pos > endPos)
438                 return endPos + qRound((pos - endPos) / averageSize) * averageSize;
439         }
440         return qRound((pos - originPosition()) / averageSize) * averageSize + originPosition();
441     }
442
443     FxListItemSG *snapItemAt(qreal pos) {
444         FxListItemSG *snapItem = 0;
445         for (int i = 0; i < visibleItems.count(); ++i) {
446             FxListItemSG *item = visibleItems[i];
447             if (item->index == -1)
448                 continue;
449             qreal itemTop = item->position();
450             if (highlight && itemTop >= pos && item->endPosition() <= pos + highlight->size() - 1)
451                 return item;
452             if (itemTop+item->size()/2 >= pos && itemTop-item->size()/2 < pos)
453                 snapItem = item;
454         }
455         return snapItem;
456     }
457
458     int lastVisibleIndex() const {
459         int lastIndex = -1;
460         for (int i = visibleItems.count()-1; i >= 0; --i) {
461             FxListItemSG *listItem = visibleItems.at(i);
462             if (listItem->index != -1) {
463                 lastIndex = listItem->index;
464                 break;
465             }
466         }
467         return lastIndex;
468     }
469
470     // map a model index to visibleItems index.
471     int mapFromModel(int modelIndex) const {
472         if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
473             return -1;
474         for (int i = 0; i < visibleItems.count(); ++i) {
475             FxListItemSG *listItem = visibleItems.at(i);
476             if (listItem->index == modelIndex)
477                 return i;
478             if (listItem->index > modelIndex)
479                 return -1;
480         }
481         return -1; // Not in visibleList
482     }
483
484     void updateViewport() {
485         Q_Q(QSGListView);
486         if (orient == QSGListView::Vertical) {
487             q->setContentHeight(endPosition() - startPosition() + 1);
488         } else {
489             q->setContentWidth(endPosition() - startPosition() + 1);
490         }
491     }
492
493     void itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) {
494         Q_Q(QSGListView);
495         QSGFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
496         if (!q->isComponentComplete())
497             return;
498         if (item != contentItem && (!highlight || item != highlight->item)) {
499             if ((orient == QSGListView::Vertical && newGeometry.height() != oldGeometry.height())
500                 || (orient == QSGListView::Horizontal && newGeometry.width() != oldGeometry.width())) {
501                 scheduleLayout();
502             }
503         }
504         if ((header && header->item == item) || (footer && footer->item == item)) {
505             if (header)
506                 updateHeader();
507             if (footer)
508                 updateFooter();
509         }
510         if (currentItem && currentItem->item == item)
511             updateHighlight();
512         if (trackedItem && trackedItem->item == item)
513             q->trackedPositionChanged();
514     }
515
516     // for debugging only
517     void checkVisible() const {
518         int skip = 0;
519         for (int i = 0; i < visibleItems.count(); ++i) {
520             FxListItemSG *listItem = visibleItems.at(i);
521             if (listItem->index == -1) {
522                 ++skip;
523             } else if (listItem->index != visibleIndex + i - skip) {
524                 qFatal("index %d %d %d", visibleIndex, i, listItem->index);
525             }
526         }
527     }
528
529     void refill(qreal from, qreal to, bool doBuffer = false);
530     void scheduleLayout();
531     void layout();
532     void updateUnrequestedIndexes();
533     void updateUnrequestedPositions();
534     void updateTrackedItem();
535     void createHighlight();
536     void updateHighlight();
537     void createSection(FxListItemSG *);
538     void updateSections();
539     void updateCurrentSection();
540     void updateCurrent(int);
541     void updateAverage();
542     void updateHeader();
543     void updateFooter();
544     void fixupPosition();
545     void positionViewAtIndex(int index, int mode);
546     virtual void fixup(AxisData &data, qreal minExtent, qreal maxExtent);
547     virtual void flick(QSGFlickablePrivate::AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
548                         QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity);
549
550     QDeclarativeGuard<QSGVisualModel> model;
551     QVariant modelVariant;
552     QList<FxListItemSG*> visibleItems;
553     QHash<QSGItem*,int> unrequestedItems;
554     FxListItemSG *currentItem;
555     QSGListView::Orientation orient;
556     Qt::LayoutDirection layoutDirection;
557     qreal visiblePos;
558     int visibleIndex;
559     qreal averageSize;
560     int currentIndex;
561     int requestedIndex;
562     int itemCount;
563     qreal highlightRangeStart;
564     qreal highlightRangeEnd;
565     QDeclarativeComponent *highlightComponent;
566     FxListItemSG *highlight;
567     FxListItemSG *trackedItem;
568     enum MovementReason { Other, SetIndex, Mouse };
569     MovementReason moveReason;
570     int buffer;
571     QSmoothedAnimation *highlightPosAnimator;
572     QSmoothedAnimation *highlightSizeAnimator;
573     QSGViewSection *sectionCriteria;
574     QString currentSection;
575     static const int sectionCacheSize = 4;
576     QSGItem *sectionCache[sectionCacheSize];
577     qreal spacing;
578     qreal highlightMoveSpeed;
579     int highlightMoveDuration;
580     qreal highlightResizeSpeed;
581     int highlightResizeDuration;
582     QSGListView::HighlightRangeMode highlightRange;
583     QSGListView::SnapMode snapMode;
584     qreal overshootDist;
585     QDeclarativeComponent *footerComponent;
586     FxListItemSG *footer;
587     QDeclarativeComponent *headerComponent;
588     FxListItemSG *header;
589     enum BufferMode { NoBuffer = 0x00, BufferBefore = 0x01, BufferAfter = 0x02 };
590     int bufferMode;
591     mutable qreal minExtent;
592     mutable qreal maxExtent;
593
594     bool ownModel : 1;
595     bool wrap : 1;
596     bool autoHighlight : 1;
597     bool haveHighlightRange : 1;
598     bool correctFlick : 1;
599     bool inFlickCorrection : 1;
600     bool lazyRelease : 1;
601     bool deferredRelease : 1;
602     bool layoutScheduled : 1;
603     bool currentIndexCleared : 1;
604     bool inViewportMoved : 1;
605     bool highlightRangeStartValid : 1;
606     bool highlightRangeEndValid : 1;
607     mutable bool minExtentDirty : 1;
608     mutable bool maxExtentDirty : 1;
609 };
610
611 void QSGListViewPrivate::init()
612 {
613     Q_Q(QSGListView);
614     q->setFlag(QSGItem::ItemIsFocusScope);
615     addItemChangeListener(this, Geometry);
616     QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
617     q->setFlickableDirection(QSGFlickable::VerticalFlick);
618     ::memset(sectionCache, 0, sizeof(QSGItem*) * sectionCacheSize);
619 }
620
621 void QSGListViewPrivate::clear()
622 {
623     timeline.clear();
624     for (int i = 0; i < visibleItems.count(); ++i)
625         releaseItem(visibleItems.at(i));
626     visibleItems.clear();
627     for (int i = 0; i < sectionCacheSize; ++i) {
628         delete sectionCache[i];
629         sectionCache[i] = 0;
630     }
631     visiblePos = header ? header->size() : 0;
632     visibleIndex = 0;
633     releaseItem(currentItem);
634     currentItem = 0;
635     createHighlight();
636     trackedItem = 0;
637     minExtentDirty = true;
638     maxExtentDirty = true;
639     itemCount = 0;
640 }
641
642 FxListItemSG *QSGListViewPrivate::createItem(int modelIndex)
643 {
644     Q_Q(QSGListView);
645     // create object
646     requestedIndex = modelIndex;
647     FxListItemSG *listItem = 0;
648     if (QSGItem *item = model->item(modelIndex, false)) {
649         listItem = new FxListItemSG(item, q);
650         listItem->index = modelIndex;
651         // initialise attached properties
652         if (sectionCriteria) {
653             QString propValue = model->stringValue(modelIndex, sectionCriteria->property());
654             listItem->attached->m_section = sectionCriteria->sectionString(propValue);
655             if (modelIndex > 0) {
656                 if (FxListItemSG *item = itemBefore(modelIndex))
657                     listItem->attached->m_prevSection = item->attached->section();
658                 else
659                     listItem->attached->m_prevSection = sectionAt(modelIndex-1);
660             }
661             if (modelIndex < model->count()-1) {
662                 if (FxListItemSG *item = visibleItem(modelIndex+1))
663                     listItem->attached->m_nextSection = item->attached->section();
664                 else
665                     listItem->attached->m_nextSection = sectionAt(modelIndex+1);
666             }
667         }
668         if (model->completePending()) {
669             // complete
670             listItem->item->setZ(1);
671             listItem->item->setParentItem(q->contentItem());
672             model->completeItem();
673         } else {
674             listItem->item->setParentItem(q->contentItem());
675         }
676         QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
677         itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
678         if (sectionCriteria && sectionCriteria->delegate()) {
679             if (listItem->attached->m_prevSection != listItem->attached->m_section)
680                 createSection(listItem);
681         }
682         unrequestedItems.remove(listItem->item);
683     }
684     requestedIndex = -1;
685
686     return listItem;
687 }
688
689 void QSGListViewPrivate::releaseItem(FxListItemSG *item)
690 {
691     Q_Q(QSGListView);
692     if (!item || !model)
693         return;
694     if (trackedItem == item)
695         trackedItem = 0;
696     QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item->item);
697     itemPrivate->removeItemChangeListener(this, QSGItemPrivate::Geometry);
698     if (model->release(item->item) == 0) {
699         // item was not destroyed, and we no longer reference it.
700         unrequestedItems.insert(item->item, model->indexOf(item->item, q));
701     }
702     if (item->section) {
703         int i = 0;
704         do {
705             if (!sectionCache[i]) {
706                 sectionCache[i] = item->section;
707                 sectionCache[i]->setVisible(false);
708                 item->section = 0;
709                 break;
710             }
711             ++i;
712         } while (i < sectionCacheSize);
713         delete item->section;
714     }
715     delete item;
716 }
717
718 void QSGListViewPrivate::refill(qreal from, qreal to, bool doBuffer)
719 {
720     Q_Q(QSGListView);
721     if (!isValid() || !q->isComponentComplete())
722         return;
723     itemCount = model->count();
724     qreal bufferFrom = from - buffer;
725     qreal bufferTo = to + buffer;
726     qreal fillFrom = from;
727     qreal fillTo = to;
728     if (doBuffer && (bufferMode & BufferAfter))
729         fillTo = bufferTo;
730     if (doBuffer && (bufferMode & BufferBefore))
731         fillFrom = bufferFrom;
732
733     int modelIndex = visibleIndex;
734     qreal itemEnd = visiblePos-1;
735     if (!visibleItems.isEmpty()) {
736         visiblePos = (*visibleItems.constBegin())->position();
737         itemEnd = (*(--visibleItems.constEnd()))->endPosition() + spacing;
738         int i = visibleItems.count() - 1;
739         while (i > 0 && visibleItems.at(i)->index == -1)
740             --i;
741         if (visibleItems.at(i)->index != -1)
742             modelIndex = visibleItems.at(i)->index + 1;
743     }
744
745     bool changed = false;
746     FxListItemSG *item = 0;
747     qreal pos = itemEnd + 1;
748     while (modelIndex < model->count() && pos <= fillTo) {
749 //        qDebug() << "refill: append item" << modelIndex << "pos" << pos;
750         if (!(item = createItem(modelIndex)))
751             break;
752         item->setPosition(pos);
753         pos += item->size() + spacing;
754         visibleItems.append(item);
755         ++modelIndex;
756         changed = true;
757         if (doBuffer) // never buffer more than one item per frame
758             break;
759     }
760     while (visibleIndex > 0 && visibleIndex <= model->count() && visiblePos-1 >= fillFrom) {
761 //        qDebug() << "refill: prepend item" << visibleIndex-1 << "current top pos" << visiblePos;
762         if (!(item = createItem(visibleIndex-1)))
763             break;
764         --visibleIndex;
765         visiblePos -= item->size() + spacing;
766         item->setPosition(visiblePos);
767         visibleItems.prepend(item);
768         changed = true;
769         if (doBuffer) // never buffer more than one item per frame
770             break;
771     }
772
773     if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create
774         while (visibleItems.count() > 1 && (item = visibleItems.first()) && item->endPosition() < bufferFrom) {
775             if (item->attached->delayRemove())
776                 break;
777 //            qDebug() << "refill: remove first" << visibleIndex << "top end pos" << item->endPosition();
778             if (item->index != -1)
779                 visibleIndex++;
780             visibleItems.removeFirst();
781             releaseItem(item);
782             changed = true;
783         }
784         while (visibleItems.count() > 1 && (item = visibleItems.last()) && item->position() > bufferTo) {
785             if (item->attached->delayRemove())
786                 break;
787 //            qDebug() << "refill: remove last" << visibleIndex+visibleItems.count()-1 << item->position();
788             visibleItems.removeLast();
789             releaseItem(item);
790             changed = true;
791         }
792         deferredRelease = false;
793     } else {
794         deferredRelease = true;
795     }
796     if (changed) {
797         minExtentDirty = true;
798         maxExtentDirty = true;
799         if (visibleItems.count())
800             visiblePos = (*visibleItems.constBegin())->position();
801         updateAverage();
802         if (currentIndex >= 0 && currentItem && !visibleItem(currentIndex)) {
803             currentItem->setPosition(positionAt(currentIndex));
804             updateHighlight();
805         }
806
807         if (sectionCriteria)
808             updateCurrentSection();
809         if (header)
810             updateHeader();
811         if (footer)
812             updateFooter();
813         updateViewport();
814         updateUnrequestedPositions();
815     } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
816         refill(from, to, true);
817     }
818     lazyRelease = false;
819 }
820
821 void QSGListViewPrivate::scheduleLayout()
822 {
823     Q_Q(QSGListView);
824     if (!layoutScheduled) {
825         layoutScheduled = true;
826         q->polish();
827     }
828 }
829
830 void QSGListViewPrivate::layout()
831 {
832     Q_Q(QSGListView);
833     layoutScheduled = false;
834     if (!isValid() && !visibleItems.count()) {
835         clear();
836         setPosition(0);
837         return;
838     }
839     if (!visibleItems.isEmpty()) {
840         bool fixedCurrent = currentItem && visibleItems.first()->item == currentItem->item;
841         qreal sum = visibleItems.first()->size();
842         qreal pos = visibleItems.first()->position() + visibleItems.first()->size() + spacing;
843         for (int i=1; i < visibleItems.count(); ++i) {
844             FxListItemSG *item = visibleItems.at(i);
845             item->setPosition(pos);
846             pos += item->size() + spacing;
847             sum += item->size();
848             fixedCurrent = fixedCurrent || (currentItem && item->item == currentItem->item);
849         }
850         averageSize = qRound(sum / visibleItems.count());
851         // move current item if it is not a visible item.
852         if (currentIndex >= 0 && currentItem && !fixedCurrent)
853             currentItem->setPosition(positionAt(currentIndex));
854     }
855     q->refill();
856     minExtentDirty = true;
857     maxExtentDirty = true;
858     updateHighlight();
859     if (!q->isMoving() && !q->isFlicking()) {
860         fixupPosition();
861         q->refill();
862     }
863     if (header)
864         updateHeader();
865     if (footer)
866         updateFooter();
867     updateViewport();
868 }
869
870 void QSGListViewPrivate::updateUnrequestedIndexes()
871 {
872     Q_Q(QSGListView);
873     QHash<QSGItem*,int>::iterator it;
874     for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
875         *it = model->indexOf(it.key(), q);
876 }
877
878 void QSGListViewPrivate::updateUnrequestedPositions()
879 {
880     Q_Q(QSGListView);
881     if (unrequestedItems.count()) {
882         qreal pos = position();
883         QHash<QSGItem*,int>::const_iterator it;
884         for (it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) {
885             QSGItem *item = it.key();
886             if (orient == QSGListView::Vertical) {
887                 if (item->y() + item->height() > pos && item->y() < pos + q->height())
888                     item->setY(positionAt(*it));
889             } else {
890                 if (item->x() + item->width() > pos && item->x() < pos + q->width()) {
891                     if (isRightToLeft())
892                         item->setX(-positionAt(*it)-item->width());
893                     else
894                         item->setX(positionAt(*it));
895                 }
896             }
897         }
898     }
899 }
900
901 void QSGListViewPrivate::updateTrackedItem()
902 {
903     Q_Q(QSGListView);
904     FxListItemSG *item = currentItem;
905     if (highlight)
906         item = highlight;
907     trackedItem = item;
908     if (trackedItem)
909         q->trackedPositionChanged();
910 }
911
912 void QSGListViewPrivate::createHighlight()
913 {
914     Q_Q(QSGListView);
915     bool changed = false;
916     if (highlight) {
917         if (trackedItem == highlight)
918             trackedItem = 0;
919         delete highlight->item;
920         delete highlight;
921         highlight = 0;
922         delete highlightPosAnimator;
923         delete highlightSizeAnimator;
924         highlightPosAnimator = 0;
925         highlightSizeAnimator = 0;
926         changed = true;
927     }
928
929     if (currentItem) {
930         QSGItem *item = 0;
931         if (highlightComponent) {
932             QDeclarativeContext *highlightContext = new QDeclarativeContext(qmlContext(q));
933             QObject *nobj = highlightComponent->create(highlightContext);
934             if (nobj) {
935                 QDeclarative_setParent_noEvent(highlightContext, nobj);
936                 item = qobject_cast<QSGItem *>(nobj);
937                 if (!item)
938                     delete nobj;
939             } else {
940                 delete highlightContext;
941             }
942         } else {
943             item = new QSGItem;
944         }
945         if (item) {
946             QDeclarative_setParent_noEvent(item, q->contentItem());
947             item->setParentItem(q->contentItem());
948             highlight = new FxListItemSG(item, q);
949             if (currentItem && autoHighlight) {
950                 if (orient == QSGListView::Vertical) {
951                     highlight->item->setHeight(currentItem->item->height());
952                 } else {
953                     highlight->item->setWidth(currentItem->item->width());
954                 }
955                 highlight->setPosition(currentItem->itemPosition());
956             }
957             QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
958             itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
959             const QLatin1String posProp(orient == QSGListView::Vertical ? "y" : "x");
960             highlightPosAnimator = new QSmoothedAnimation(q);
961             highlightPosAnimator->target = QDeclarativeProperty(highlight->item, posProp);
962             highlightPosAnimator->velocity = highlightMoveSpeed;
963             highlightPosAnimator->userDuration = highlightMoveDuration;
964             const QLatin1String sizeProp(orient == QSGListView::Vertical ? "height" : "width");
965             highlightSizeAnimator = new QSmoothedAnimation(q);
966             highlightSizeAnimator->velocity = highlightResizeSpeed;
967             highlightSizeAnimator->userDuration = highlightResizeDuration;
968             highlightSizeAnimator->target = QDeclarativeProperty(highlight->item, sizeProp);
969             if (autoHighlight) {
970                 highlightPosAnimator->restart();
971                 highlightSizeAnimator->restart();
972             }
973             changed = true;
974         }
975     }
976     if (changed)
977         emit q->highlightItemChanged();
978 }
979
980 void QSGListViewPrivate::updateHighlight()
981 {
982     if ((!currentItem && highlight) || (currentItem && !highlight))
983         createHighlight();
984     if (currentItem && autoHighlight && highlight && !movingHorizontally && !movingVertically) {
985         // auto-update highlight
986         highlightPosAnimator->to = isRightToLeft()
987                 ? -currentItem->itemPosition()-currentItem->itemSize()
988                 : currentItem->itemPosition();
989         highlightSizeAnimator->to = currentItem->itemSize();
990         if (orient == QSGListView::Vertical) {
991             if (highlight->item->width() == 0)
992                 highlight->item->setWidth(currentItem->item->width());
993         } else {
994             if (highlight->item->height() == 0)
995                 highlight->item->setHeight(currentItem->item->height());
996         }
997         highlightPosAnimator->restart();
998         highlightSizeAnimator->restart();
999     }
1000     updateTrackedItem();
1001 }
1002
1003 void QSGListViewPrivate::createSection(FxListItemSG *listItem)
1004 {
1005     Q_Q(QSGListView);
1006     if (!sectionCriteria || !sectionCriteria->delegate())
1007         return;
1008     if (listItem->attached->m_prevSection != listItem->attached->m_section) {
1009         if (!listItem->section) {
1010             qreal pos = listItem->position();
1011             int i = sectionCacheSize-1;
1012             while (i >= 0 && !sectionCache[i])
1013                 --i;
1014             if (i >= 0) {
1015                 listItem->section = sectionCache[i];
1016                 sectionCache[i] = 0;
1017                 listItem->section->setVisible(true);
1018                 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
1019                 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1020             } else {
1021                 QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1022                 context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1023                 QObject *nobj = sectionCriteria->delegate()->beginCreate(context);
1024                 if (nobj) {
1025                     QDeclarative_setParent_noEvent(context, nobj);
1026                     listItem->section = qobject_cast<QSGItem *>(nobj);
1027                     if (!listItem->section) {
1028                         delete nobj;
1029                     } else {
1030                         listItem->section->setZ(1);
1031                         QDeclarative_setParent_noEvent(listItem->section, q->contentItem());
1032                         listItem->section->setParentItem(q->contentItem());
1033                     }
1034                 } else {
1035                     delete context;
1036                 }
1037                 sectionCriteria->delegate()->completeCreate();
1038             }
1039             listItem->setPosition(pos);
1040         } else {
1041             QDeclarativeContext *context = QDeclarativeEngine::contextForObject(listItem->section)->parentContext();
1042             context->setContextProperty(QLatin1String("section"), listItem->attached->m_section);
1043         }
1044     } else if (listItem->section) {
1045         qreal pos = listItem->position();
1046         int i = 0;
1047         do {
1048             if (!sectionCache[i]) {
1049                 sectionCache[i] = listItem->section;
1050                 sectionCache[i]->setVisible(false);
1051                 listItem->section = 0;
1052                 return;
1053             }
1054             ++i;
1055         } while (i < sectionCacheSize);
1056         delete listItem->section;
1057         listItem->section = 0;
1058         listItem->setPosition(pos);
1059     }
1060 }
1061
1062 void QSGListViewPrivate::updateSections()
1063 {
1064     if (sectionCriteria && !visibleItems.isEmpty()) {
1065         QString prevSection;
1066         if (visibleIndex > 0)
1067             prevSection = sectionAt(visibleIndex-1);
1068         QSGListViewAttached *prevAtt = 0;
1069         int idx = -1;
1070         for (int i = 0; i < visibleItems.count(); ++i) {
1071             QSGListViewAttached *attached = visibleItems.at(i)->attached;
1072             attached->setPrevSection(prevSection);
1073             if (visibleItems.at(i)->index != -1) {
1074                 QString propValue = model->stringValue(visibleItems.at(i)->index, sectionCriteria->property());
1075                 attached->setSection(sectionCriteria->sectionString(propValue));
1076                 idx = visibleItems.at(i)->index;
1077             }
1078             createSection(visibleItems.at(i));
1079             if (prevAtt)
1080                 prevAtt->setNextSection(attached->section());
1081             prevSection = attached->section();
1082             prevAtt = attached;
1083         }
1084         if (prevAtt) {
1085             if (idx > 0 && idx < model->count()-1)
1086                 prevAtt->setNextSection(sectionAt(idx+1));
1087             else
1088                 prevAtt->setNextSection(QString());
1089         }
1090     }
1091 }
1092
1093 void QSGListViewPrivate::updateCurrentSection()
1094 {
1095     Q_Q(QSGListView);
1096     if (!sectionCriteria || visibleItems.isEmpty()) {
1097         if (!currentSection.isEmpty()) {
1098             currentSection.clear();
1099             emit q->currentSectionChanged();
1100         }
1101         return;
1102     }
1103     int index = 0;
1104     while (index < visibleItems.count() && visibleItems.at(index)->endPosition() < position())
1105         ++index;
1106
1107     QString newSection = currentSection;
1108     if (index < visibleItems.count())
1109         newSection = visibleItems.at(index)->attached->section();
1110     else
1111         newSection = visibleItems.first()->attached->section();
1112     if (newSection != currentSection) {
1113         currentSection = newSection;
1114         emit q->currentSectionChanged();
1115     }
1116 }
1117
1118 void QSGListViewPrivate::updateCurrent(int modelIndex)
1119 {
1120     Q_Q(QSGListView);
1121     if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1122         if (currentItem) {
1123             currentItem->attached->setIsCurrentItem(false);
1124             releaseItem(currentItem);
1125             currentItem = 0;
1126             currentIndex = modelIndex;
1127             emit q->currentIndexChanged();
1128             updateHighlight();
1129         } else if (currentIndex != modelIndex) {
1130             currentIndex = modelIndex;
1131             emit q->currentIndexChanged();
1132         }
1133         return;
1134     }
1135
1136     if (currentItem && currentIndex == modelIndex) {
1137         updateHighlight();
1138         return;
1139     }
1140     FxListItemSG *oldCurrentItem = currentItem;
1141     currentIndex = modelIndex;
1142     currentItem = createItem(modelIndex);
1143     if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1144         oldCurrentItem->attached->setIsCurrentItem(false);
1145     if (currentItem) {
1146         if (modelIndex == visibleIndex - 1 && visibleItems.count()) {
1147             // We can calculate exact postion in this case
1148             currentItem->setPosition(visibleItems.first()->position() - currentItem->size() - spacing);
1149         } else {
1150             // Create current item now and position as best we can.
1151             // Its position will be corrected when it becomes visible.
1152             currentItem->setPosition(positionAt(modelIndex));
1153         }
1154         currentItem->item->setFocus(true);
1155         currentItem->attached->setIsCurrentItem(true);
1156         // Avoid showing section delegate twice.  We still need the section heading so that
1157         // currentItem positioning works correctly.
1158         // This is slightly sub-optimal, but section heading caching minimizes the impact.
1159         if (currentItem->section)
1160             currentItem->section->setVisible(false);
1161         if (visibleItems.isEmpty())
1162             averageSize = currentItem->size();
1163     }
1164     updateHighlight();
1165     emit q->currentIndexChanged();
1166     // Release the old current item
1167     releaseItem(oldCurrentItem);
1168 }
1169
1170 void QSGListViewPrivate::updateAverage()
1171 {
1172     if (!visibleItems.count())
1173         return;
1174     qreal sum = 0.0;
1175     for (int i = 0; i < visibleItems.count(); ++i)
1176         sum += visibleItems.at(i)->size();
1177     averageSize = qRound(sum / visibleItems.count());
1178 }
1179
1180 void QSGListViewPrivate::updateFooter()
1181 {
1182     Q_Q(QSGListView);
1183     if (!footer && footerComponent) {
1184         QSGItem *item = 0;
1185         QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1186         QObject *nobj = footerComponent->create(context);
1187         if (nobj) {
1188             QDeclarative_setParent_noEvent(context, nobj);
1189             item = qobject_cast<QSGItem *>(nobj);
1190             if (!item)
1191                 delete nobj;
1192         } else {
1193             delete context;
1194         }
1195         if (item) {
1196             QDeclarative_setParent_noEvent(item, q->contentItem());
1197             item->setParentItem(q->contentItem());
1198             item->setZ(1);
1199             QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1200             itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
1201             footer = new FxListItemSG(item, q);
1202         }
1203     }
1204     if (footer) {
1205         if (visibleItems.count()) {
1206             qreal endPos = lastPosition() + 1;
1207             if (lastVisibleIndex() == model->count()-1) {
1208                 footer->setPosition(endPos);
1209             } else {
1210                 qreal visiblePos = position() + q->height();
1211                 if (endPos <= visiblePos || footer->position() < endPos)
1212                     footer->setPosition(endPos);
1213             }
1214         } else {
1215             footer->setPosition(visiblePos);
1216         }
1217     }
1218 }
1219
1220 void QSGListViewPrivate::updateHeader()
1221 {
1222     Q_Q(QSGListView);
1223     if (!header && headerComponent) {
1224         QSGItem *item = 0;
1225         QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1226         QObject *nobj = headerComponent->create(context);
1227         if (nobj) {
1228             QDeclarative_setParent_noEvent(context, nobj);
1229             item = qobject_cast<QSGItem *>(nobj);
1230             if (!item)
1231                 delete nobj;
1232         } else {
1233             delete context;
1234         }
1235         if (item) {
1236             QDeclarative_setParent_noEvent(item, q->contentItem());
1237             item->setParentItem(q->contentItem());
1238             item->setZ(1);
1239             QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1240             itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
1241             header = new FxListItemSG(item, q);
1242         }
1243     }
1244     if (header) {
1245         if (visibleItems.count()) {
1246             qreal startPos = originPosition();
1247             if (visibleIndex == 0) {
1248                 header->setPosition(startPos - header->size());
1249             } else {
1250                 if (position() <= startPos || header->position() > startPos - header->size())
1251                     header->setPosition(startPos - header->size());
1252             }
1253         } else {
1254             if (itemCount == 0)
1255                 visiblePos = header->size();
1256             header->setPosition(0);
1257         }
1258     }
1259 }
1260
1261 void QSGListViewPrivate::fixupPosition()
1262 {
1263     if ((haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange)
1264         || snapMode != QSGListView::NoSnap)
1265         moveReason = Other;
1266     if (orient == QSGListView::Vertical)
1267         fixupY();
1268     else
1269         fixupX();
1270 }
1271
1272 void QSGListViewPrivate::fixup(AxisData &data, qreal minExtent, qreal maxExtent)
1273 {
1274     if ((orient == QSGListView::Horizontal && &data == &vData)
1275         || (orient == QSGListView::Vertical && &data == &hData))
1276         return;
1277
1278     correctFlick = false;
1279     fixupMode = moveReason == Mouse ? fixupMode : Immediate;
1280
1281     qreal highlightStart;
1282     qreal highlightEnd;
1283     qreal viewPos;
1284     if (isRightToLeft()) {
1285         // Handle Right-To-Left exceptions
1286         viewPos = -position()-size();
1287         highlightStart = highlightRangeStartValid ? size() - highlightRangeEnd : highlightRangeStart;
1288         highlightEnd = highlightRangeEndValid ? size() - highlightRangeStart : highlightRangeEnd;
1289     } else {
1290         viewPos = position();
1291         highlightStart = highlightRangeStart;
1292         highlightEnd = highlightRangeEnd;
1293     }
1294
1295     if (currentItem && haveHighlightRange && highlightRange == QSGListView::StrictlyEnforceRange
1296             && moveReason != QSGListViewPrivate::SetIndex) {
1297         updateHighlight();
1298         qreal pos = currentItem->itemPosition();
1299         if (viewPos < pos + currentItem->itemSize() - highlightEnd)
1300             viewPos = pos + currentItem->itemSize() - highlightEnd;
1301         if (viewPos > pos - highlightStart)
1302             viewPos = pos - highlightStart;
1303         if (isRightToLeft())
1304             viewPos = -viewPos-size();
1305
1306         timeline.reset(data.move);
1307         if (viewPos != position()) {
1308             if (fixupMode != Immediate) {
1309                 timeline.move(data.move, -viewPos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1310                 data.fixingUp = true;
1311             } else {
1312                 timeline.set(data.move, -viewPos);
1313             }
1314         }
1315         vTime = timeline.time();
1316     } else if (snapMode != QSGListView::NoSnap && moveReason != QSGListViewPrivate::SetIndex) {
1317         qreal tempPosition = isRightToLeft() ? -position()-size() : position();
1318         FxListItemSG *topItem = snapItemAt(tempPosition+highlightStart);
1319         FxListItemSG *bottomItem = snapItemAt(tempPosition+highlightEnd);
1320         qreal pos;
1321         bool isInBounds = -position() > maxExtent && -position() < minExtent;
1322         if (topItem && isInBounds) {
1323             if (topItem->index == 0 && header && tempPosition+highlightStart < header->position()+header->size()/2) {
1324                 pos = isRightToLeft() ? - header->position() + highlightStart - size() : header->position() - highlightStart;
1325             } else {
1326                 if (isRightToLeft())
1327                     pos = qMax(qMin(-topItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1328                 else
1329                     pos = qMax(qMin(topItem->position() - highlightStart, -maxExtent), -minExtent);
1330             }
1331         } else if (bottomItem && isInBounds) {
1332             if (isRightToLeft())
1333                 pos = qMax(qMin(-bottomItem->position() + highlightStart - size(), -maxExtent), -minExtent);
1334             else
1335                 pos = qMax(qMin(bottomItem->position() - highlightStart, -maxExtent), -minExtent);
1336         } else {
1337             QSGFlickablePrivate::fixup(data, minExtent, maxExtent);
1338             return;
1339         }
1340
1341         qreal dist = qAbs(data.move + pos);
1342         if (dist > 0) {
1343             timeline.reset(data.move);
1344             if (fixupMode != Immediate) {
1345                 timeline.move(data.move, -pos, QEasingCurve(QEasingCurve::InOutQuad), fixupDuration/2);
1346                 data.fixingUp = true;
1347             } else {
1348                 timeline.set(data.move, -pos);
1349             }
1350             vTime = timeline.time();
1351         }
1352     } else {
1353         QSGFlickablePrivate::fixup(data, minExtent, maxExtent);
1354     }
1355     fixupMode = Normal;
1356 }
1357
1358 void QSGListViewPrivate::flick(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize,
1359                                         QDeclarativeTimeLineCallback::Callback fixupCallback, qreal velocity)
1360 {
1361     Q_Q(QSGListView);
1362
1363     data.fixingUp = false;
1364     moveReason = Mouse;
1365     if ((!haveHighlightRange || highlightRange != QSGListView::StrictlyEnforceRange) && snapMode == QSGListView::NoSnap) {
1366         correctFlick = true;
1367         QSGFlickablePrivate::flick(data, minExtent, maxExtent, vSize, fixupCallback, velocity);
1368         return;
1369     }
1370     qreal maxDistance = 0;
1371     qreal dataValue = isRightToLeft() ? -data.move.value()+size() : data.move.value();
1372     // -ve velocity means list is moving up/left
1373     if (velocity > 0) {
1374         if (data.move.value() < minExtent) {
1375             if (snapMode == QSGListView::SnapOneItem) {
1376                 if (FxListItemSG *item = isRightToLeft() ? nextVisibleItem() : firstVisibleItem())
1377                     maxDistance = qAbs(item->position() + dataValue);
1378             } else {
1379                 maxDistance = qAbs(minExtent - data.move.value());
1380             }
1381         }
1382         if (snapMode == QSGListView::NoSnap && highlightRange != QSGListView::StrictlyEnforceRange)
1383             data.flickTarget = minExtent;
1384     } else {
1385         if (data.move.value() > maxExtent) {
1386             if (snapMode == QSGListView::SnapOneItem) {
1387                 if (FxListItemSG *item = isRightToLeft() ? firstVisibleItem() : nextVisibleItem())
1388                     maxDistance = qAbs(item->position() + dataValue);
1389             } else {
1390                 maxDistance = qAbs(maxExtent - data.move.value());
1391             }
1392         }
1393         if (snapMode == QSGListView::NoSnap && highlightRange != QSGListView::StrictlyEnforceRange)
1394             data.flickTarget = maxExtent;
1395     }
1396     bool overShoot = boundsBehavior == QSGFlickable::DragAndOvershootBounds;
1397     qreal highlightStart = isRightToLeft() && highlightRangeStartValid ? size()-highlightRangeEnd : highlightRangeStart;
1398     if (maxDistance > 0 || overShoot) {
1399         // These modes require the list to stop exactly on an item boundary.
1400         // The initial flick will estimate the boundary to stop on.
1401         // Since list items can have variable sizes, the boundary will be
1402         // reevaluated and adjusted as we approach the boundary.
1403         qreal v = velocity;
1404         if (maxVelocity != -1 && maxVelocity < qAbs(v)) {
1405             if (v < 0)
1406                 v = -maxVelocity;
1407             else
1408                 v = maxVelocity;
1409         }
1410         if (!flickingHorizontally && !flickingVertically) {
1411             // the initial flick - estimate boundary
1412             qreal accel = deceleration;
1413             qreal v2 = v * v;
1414             overshootDist = 0.0;
1415             // + averageSize/4 to encourage moving at least one item in the flick direction
1416             qreal dist = v2 / (accel * 2.0) + averageSize/4;
1417             if (maxDistance > 0)
1418                 dist = qMin(dist, maxDistance);
1419             if (v > 0)
1420                 dist = -dist;
1421             if ((maxDistance > 0.0 && v2 / (2.0f * maxDistance) < accel) || snapMode == QSGListView::SnapOneItem) {
1422                 qreal distTemp = isRightToLeft() ? -dist : dist;
1423                 data.flickTarget = -snapPosAt(-(dataValue - highlightStart) + distTemp) + highlightStart;
1424                 data.flickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1425                 if (overShoot) {
1426                     if (data.flickTarget >= minExtent) {
1427                         overshootDist = overShootDistance(v, vSize);
1428                         data.flickTarget += overshootDist;
1429                     } else if (data.flickTarget <= maxExtent) {
1430                         overshootDist = overShootDistance(v, vSize);
1431                         data.flickTarget -= overshootDist;
1432                     }
1433                 }
1434                 qreal adjDist = -data.flickTarget + data.move.value();
1435                 if (qAbs(adjDist) > qAbs(dist)) {
1436                     // Prevent painfully slow flicking - adjust velocity to suit flickDeceleration
1437                     qreal adjv2 = accel * 2.0f * qAbs(adjDist);
1438                     if (adjv2 > v2) {
1439                         v2 = adjv2;
1440                         v = qSqrt(v2);
1441                         if (dist > 0)
1442                             v = -v;
1443                     }
1444                 }
1445                 dist = adjDist;
1446                 accel = v2 / (2.0f * qAbs(dist));
1447             } else if (overShoot) {
1448                 data.flickTarget = data.move.value() - dist;
1449                 if (data.flickTarget >= minExtent) {
1450                     overshootDist = overShootDistance(v, vSize);
1451                     data.flickTarget += overshootDist;
1452                 } else if (data.flickTarget <= maxExtent) {
1453                     overshootDist = overShootDistance(v, vSize);
1454                     data.flickTarget -= overshootDist;
1455                 }
1456             }
1457             timeline.reset(data.move);
1458             timeline.accel(data.move, v, accel, maxDistance + overshootDist);
1459             timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1460             if (!flickingHorizontally && q->xflick()) {
1461                 flickingHorizontally = true;
1462                 emit q->flickingChanged();
1463                 emit q->flickingHorizontallyChanged();
1464                 emit q->flickStarted();
1465             }
1466             if (!flickingVertically && q->yflick()) {
1467                 flickingVertically = true;
1468                 emit q->flickingChanged();
1469                 emit q->flickingVerticallyChanged();
1470                 emit q->flickStarted();
1471             }
1472             correctFlick = true;
1473         } else {
1474             // reevaluate the target boundary.
1475             qreal newtarget = data.flickTarget;
1476             if (snapMode != QSGListView::NoSnap || highlightRange == QSGListView::StrictlyEnforceRange) {
1477                 qreal tempFlickTarget = isRightToLeft() ? -data.flickTarget+size() : data.flickTarget;
1478                 newtarget = -snapPosAt(-(tempFlickTarget - highlightStart)) + highlightStart;
1479                 newtarget = isRightToLeft() ? -newtarget+size() : newtarget;
1480             }
1481             if (velocity < 0 && newtarget <= maxExtent)
1482                 newtarget = maxExtent - overshootDist;
1483             else if (velocity > 0 && newtarget >= minExtent)
1484                 newtarget = minExtent + overshootDist;
1485             if (newtarget == data.flickTarget) { // boundary unchanged - nothing to do
1486                 if (qAbs(velocity) < MinimumFlickVelocity)
1487                     correctFlick = false;
1488                 return;
1489             }
1490             data.flickTarget = newtarget;
1491             qreal dist = -newtarget + data.move.value();
1492             if ((v < 0 && dist < 0) || (v > 0 && dist > 0)) {
1493                 correctFlick = false;
1494                 timeline.reset(data.move);
1495                 fixup(data, minExtent, maxExtent);
1496                 return;
1497             }
1498             timeline.reset(data.move);
1499             timeline.accelDistance(data.move, v, -dist);
1500             timeline.callback(QDeclarativeTimeLineCallback(&data.move, fixupCallback, this));
1501         }
1502     } else {
1503         correctFlick = false;
1504         timeline.reset(data.move);
1505         fixup(data, minExtent, maxExtent);
1506     }
1507 }
1508
1509 //----------------------------------------------------------------------------
1510
1511 QSGListView::QSGListView(QSGItem *parent)
1512     : QSGFlickable(*(new QSGListViewPrivate), parent)
1513 {
1514     Q_D(QSGListView);
1515     d->init();
1516 }
1517
1518 QSGListView::~QSGListView()
1519 {
1520     Q_D(QSGListView);
1521     d->clear();
1522     if (d->ownModel)
1523         delete d->model;
1524     delete d->header;
1525     delete d->footer;
1526 }
1527
1528 QVariant QSGListView::model() const
1529 {
1530     Q_D(const QSGListView);
1531     return d->modelVariant;
1532 }
1533
1534 void QSGListView::setModel(const QVariant &model)
1535 {
1536     Q_D(QSGListView);
1537     if (d->modelVariant == model)
1538         return;
1539     if (d->model) {
1540         disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1541         disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1542         disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1543         disconnect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1544         disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1545         disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
1546         disconnect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*)));
1547     }
1548     d->clear();
1549     QSGVisualModel *oldModel = d->model;
1550     d->model = 0;
1551     d->setPosition(0);
1552     d->modelVariant = model;
1553     QObject *object = qvariant_cast<QObject*>(model);
1554     QSGVisualModel *vim = 0;
1555     if (object && (vim = qobject_cast<QSGVisualModel *>(object))) {
1556         if (d->ownModel) {
1557             delete oldModel;
1558             d->ownModel = false;
1559         }
1560         d->model = vim;
1561     } else {
1562         if (!d->ownModel) {
1563             d->model = new QSGVisualDataModel(qmlContext(this), this);
1564             d->ownModel = true;
1565         } else {
1566             d->model = oldModel;
1567         }
1568         if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
1569             dataModel->setModel(model);
1570     }
1571     if (d->model) {
1572         d->bufferMode = QSGListViewPrivate::BufferBefore | QSGListViewPrivate::BufferAfter;
1573         if (isComponentComplete()) {
1574             updateSections();
1575             refill();
1576             if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
1577                 setCurrentIndex(0);
1578             } else {
1579                 d->moveReason = QSGListViewPrivate::SetIndex;
1580                 d->updateCurrent(d->currentIndex);
1581                 if (d->highlight && d->currentItem) {
1582                     if (d->autoHighlight)
1583                         d->highlight->setPosition(d->currentItem->position());
1584                     d->updateTrackedItem();
1585                 }
1586             }
1587             d->updateViewport();
1588         }
1589         connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
1590         connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
1591         connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
1592         connect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
1593         connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
1594         connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
1595         connect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*)));
1596         emit countChanged();
1597     }
1598     emit modelChanged();
1599 }
1600
1601 QDeclarativeComponent *QSGListView::delegate() const
1602 {
1603     Q_D(const QSGListView);
1604     if (d->model) {
1605         if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
1606             return dataModel->delegate();
1607     }
1608
1609     return 0;
1610 }
1611
1612 void QSGListView::setDelegate(QDeclarativeComponent *delegate)
1613 {
1614     Q_D(QSGListView);
1615     if (delegate == this->delegate())
1616         return;
1617     if (!d->ownModel) {
1618         d->model = new QSGVisualDataModel(qmlContext(this));
1619         d->ownModel = true;
1620     }
1621     if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model)) {
1622         dataModel->setDelegate(delegate);
1623         if (isComponentComplete()) {
1624             for (int i = 0; i < d->visibleItems.count(); ++i)
1625                 d->releaseItem(d->visibleItems.at(i));
1626             d->visibleItems.clear();
1627             d->releaseItem(d->currentItem);
1628             d->currentItem = 0;
1629             updateSections();
1630             refill();
1631             d->moveReason = QSGListViewPrivate::SetIndex;
1632             d->updateCurrent(d->currentIndex);
1633             if (d->highlight && d->currentItem) {
1634                 if (d->autoHighlight)
1635                     d->highlight->setPosition(d->currentItem->position());
1636                 d->updateTrackedItem();
1637             }
1638             d->updateViewport();
1639         }
1640     }
1641     emit delegateChanged();
1642 }
1643
1644 int QSGListView::currentIndex() const
1645 {
1646     Q_D(const QSGListView);
1647     return d->currentIndex;
1648 }
1649
1650 void QSGListView::setCurrentIndex(int index)
1651 {
1652     Q_D(QSGListView);
1653     if (d->requestedIndex >= 0)  // currently creating item
1654         return;
1655     d->currentIndexCleared = (index == -1);
1656     if (index == d->currentIndex)
1657         return;
1658     if (isComponentComplete() && d->isValid()) {
1659         d->moveReason = QSGListViewPrivate::SetIndex;
1660         d->updateCurrent(index);
1661     } else if (d->currentIndex != index) {
1662         d->currentIndex = index;
1663         emit currentIndexChanged();
1664     }
1665 }
1666
1667 QSGItem *QSGListView::currentItem()
1668 {
1669     Q_D(QSGListView);
1670     if (!d->currentItem)
1671         return 0;
1672     return d->currentItem->item;
1673 }
1674
1675 QSGItem *QSGListView::highlightItem()
1676 {
1677     Q_D(QSGListView);
1678     if (!d->highlight)
1679         return 0;
1680     return d->highlight->item;
1681 }
1682
1683 int QSGListView::count() const
1684 {
1685     Q_D(const QSGListView);
1686     if (d->model)
1687         return d->model->count();
1688     return 0;
1689 }
1690
1691 QDeclarativeComponent *QSGListView::highlight() const
1692 {
1693     Q_D(const QSGListView);
1694     return d->highlightComponent;
1695 }
1696
1697 void QSGListView::setHighlight(QDeclarativeComponent *highlight)
1698 {
1699     Q_D(QSGListView);
1700     if (highlight != d->highlightComponent) {
1701         d->highlightComponent = highlight;
1702         d->createHighlight();
1703         if (d->currentItem)
1704             d->updateHighlight();
1705         emit highlightChanged();
1706     }
1707 }
1708
1709 bool QSGListView::highlightFollowsCurrentItem() const
1710 {
1711     Q_D(const QSGListView);
1712     return d->autoHighlight;
1713 }
1714
1715 void QSGListView::setHighlightFollowsCurrentItem(bool autoHighlight)
1716 {
1717     Q_D(QSGListView);
1718     if (d->autoHighlight != autoHighlight) {
1719         d->autoHighlight = autoHighlight;
1720         if (autoHighlight) {
1721             d->updateHighlight();
1722         } else {
1723             if (d->highlightPosAnimator)
1724                 d->highlightPosAnimator->stop();
1725             if (d->highlightSizeAnimator)
1726                 d->highlightSizeAnimator->stop();
1727         }
1728         emit highlightFollowsCurrentItemChanged();
1729     }
1730 }
1731
1732 //###Possibly rename these properties, since they are very useful even without a highlight?
1733 qreal QSGListView::preferredHighlightBegin() const
1734 {
1735     Q_D(const QSGListView);
1736     return d->highlightRangeStart;
1737 }
1738
1739 void QSGListView::setPreferredHighlightBegin(qreal start)
1740 {
1741     Q_D(QSGListView);
1742     d->highlightRangeStartValid = true;
1743     if (d->highlightRangeStart == start)
1744         return;
1745     d->highlightRangeStart = start;
1746     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1747     emit preferredHighlightBeginChanged();
1748 }
1749
1750 void QSGListView::resetPreferredHighlightBegin()
1751 {
1752     Q_D(QSGListView);
1753     d->highlightRangeStartValid = false;
1754     if (d->highlightRangeStart == 0)
1755         return;
1756     d->highlightRangeStart = 0;
1757     emit preferredHighlightBeginChanged();
1758 }
1759
1760 qreal QSGListView::preferredHighlightEnd() const
1761 {
1762     Q_D(const QSGListView);
1763     return d->highlightRangeEnd;
1764 }
1765
1766 void QSGListView::setPreferredHighlightEnd(qreal end)
1767 {
1768     Q_D(QSGListView);
1769     d->highlightRangeEndValid = true;
1770     if (d->highlightRangeEnd == end)
1771         return;
1772     d->highlightRangeEnd = end;
1773     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1774     emit preferredHighlightEndChanged();
1775 }
1776
1777 void QSGListView::resetPreferredHighlightEnd()
1778 {
1779     Q_D(QSGListView);
1780     d->highlightRangeEndValid = false;
1781     if (d->highlightRangeEnd == 0)
1782         return;
1783     d->highlightRangeEnd = 0;
1784     emit preferredHighlightEndChanged();
1785 }
1786
1787 QSGListView::HighlightRangeMode QSGListView::highlightRangeMode() const
1788 {
1789     Q_D(const QSGListView);
1790     return d->highlightRange;
1791 }
1792
1793 void QSGListView::setHighlightRangeMode(HighlightRangeMode mode)
1794 {
1795     Q_D(QSGListView);
1796     if (d->highlightRange == mode)
1797         return;
1798     d->highlightRange = mode;
1799     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
1800     emit highlightRangeModeChanged();
1801 }
1802
1803 qreal QSGListView::spacing() const
1804 {
1805     Q_D(const QSGListView);
1806     return d->spacing;
1807 }
1808
1809 void QSGListView::setSpacing(qreal spacing)
1810 {
1811     Q_D(QSGListView);
1812     if (spacing != d->spacing) {
1813         d->spacing = spacing;
1814         d->layout();
1815         emit spacingChanged();
1816     }
1817 }
1818
1819 QSGListView::Orientation QSGListView::orientation() const
1820 {
1821     Q_D(const QSGListView);
1822     return d->orient;
1823 }
1824
1825 void QSGListView::setOrientation(QSGListView::Orientation orientation)
1826 {
1827     Q_D(QSGListView);
1828     if (d->orient != orientation) {
1829         d->orient = orientation;
1830         if (d->orient == QSGListView::Vertical) {
1831             setContentWidth(-1);
1832             setFlickableDirection(VerticalFlick);
1833         } else {
1834             setContentHeight(-1);
1835             setFlickableDirection(HorizontalFlick);
1836         }
1837         d->regenerate();
1838         emit orientationChanged();
1839     }
1840 }
1841
1842 Qt::LayoutDirection QSGListView::layoutDirection() const
1843 {
1844     Q_D(const QSGListView);
1845     return d->layoutDirection;
1846 }
1847
1848 void QSGListView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
1849 {
1850     Q_D(QSGListView);
1851     if (d->layoutDirection != layoutDirection) {
1852         d->layoutDirection = layoutDirection;
1853         d->regenerate();
1854         emit layoutDirectionChanged();
1855         emit effectiveLayoutDirectionChanged();
1856     }
1857 }
1858
1859 Qt::LayoutDirection QSGListView::effectiveLayoutDirection() const
1860 {
1861     Q_D(const QSGListView);
1862     if (d->effectiveLayoutMirror)
1863         return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
1864     else
1865         return d->layoutDirection;
1866 }
1867
1868 bool QSGListView::isWrapEnabled() const
1869 {
1870     Q_D(const QSGListView);
1871     return d->wrap;
1872 }
1873
1874 void QSGListView::setWrapEnabled(bool wrap)
1875 {
1876     Q_D(QSGListView);
1877     if (d->wrap == wrap)
1878         return;
1879     d->wrap = wrap;
1880     emit keyNavigationWrapsChanged();
1881 }
1882
1883 int QSGListView::cacheBuffer() const
1884 {
1885     Q_D(const QSGListView);
1886     return d->buffer;
1887 }
1888
1889 void QSGListView::setCacheBuffer(int b)
1890 {
1891     Q_D(QSGListView);
1892     if (d->buffer != b) {
1893         d->buffer = b;
1894         if (isComponentComplete()) {
1895             d->bufferMode = QSGListViewPrivate::BufferBefore | QSGListViewPrivate::BufferAfter;
1896             refill();
1897         }
1898         emit cacheBufferChanged();
1899     }
1900 }
1901
1902 QSGViewSection *QSGListView::sectionCriteria()
1903 {
1904     Q_D(QSGListView);
1905     if (!d->sectionCriteria) {
1906         d->sectionCriteria = new QSGViewSection(this);
1907         connect(d->sectionCriteria, SIGNAL(propertyChanged()), this, SLOT(updateSections()));
1908     }
1909     return d->sectionCriteria;
1910 }
1911
1912 QString QSGListView::currentSection() const
1913 {
1914     Q_D(const QSGListView);
1915     return d->currentSection;
1916 }
1917
1918 qreal QSGListView::highlightMoveSpeed() const
1919 {
1920     Q_D(const QSGListView);\
1921     return d->highlightMoveSpeed;
1922 }
1923
1924 void QSGListView::setHighlightMoveSpeed(qreal speed)
1925 {
1926     Q_D(QSGListView);\
1927     if (d->highlightMoveSpeed != speed) {
1928         d->highlightMoveSpeed = speed;
1929         if (d->highlightPosAnimator)
1930             d->highlightPosAnimator->velocity = d->highlightMoveSpeed;
1931         emit highlightMoveSpeedChanged();
1932     }
1933 }
1934
1935 int QSGListView::highlightMoveDuration() const
1936 {
1937     Q_D(const QSGListView);
1938     return d->highlightMoveDuration;
1939 }
1940
1941 void QSGListView::setHighlightMoveDuration(int duration)
1942 {
1943     Q_D(QSGListView);\
1944     if (d->highlightMoveDuration != duration) {
1945         d->highlightMoveDuration = duration;
1946         if (d->highlightPosAnimator)
1947             d->highlightPosAnimator->userDuration = d->highlightMoveDuration;
1948         emit highlightMoveDurationChanged();
1949     }
1950 }
1951
1952 qreal QSGListView::highlightResizeSpeed() const
1953 {
1954     Q_D(const QSGListView);\
1955     return d->highlightResizeSpeed;
1956 }
1957
1958 void QSGListView::setHighlightResizeSpeed(qreal speed)
1959 {
1960     Q_D(QSGListView);\
1961     if (d->highlightResizeSpeed != speed) {
1962         d->highlightResizeSpeed = speed;
1963         if (d->highlightSizeAnimator)
1964             d->highlightSizeAnimator->velocity = d->highlightResizeSpeed;
1965         emit highlightResizeSpeedChanged();
1966     }
1967 }
1968
1969 int QSGListView::highlightResizeDuration() const
1970 {
1971     Q_D(const QSGListView);
1972     return d->highlightResizeDuration;
1973 }
1974
1975 void QSGListView::setHighlightResizeDuration(int duration)
1976 {
1977     Q_D(QSGListView);\
1978     if (d->highlightResizeDuration != duration) {
1979         d->highlightResizeDuration = duration;
1980         if (d->highlightSizeAnimator)
1981             d->highlightSizeAnimator->userDuration = d->highlightResizeDuration;
1982         emit highlightResizeDurationChanged();
1983     }
1984 }
1985
1986 QSGListView::SnapMode QSGListView::snapMode() const
1987 {
1988     Q_D(const QSGListView);
1989     return d->snapMode;
1990 }
1991
1992 void QSGListView::setSnapMode(SnapMode mode)
1993 {
1994     Q_D(QSGListView);
1995     if (d->snapMode != mode) {
1996         d->snapMode = mode;
1997         emit snapModeChanged();
1998     }
1999 }
2000
2001 QDeclarativeComponent *QSGListView::footer() const
2002 {
2003     Q_D(const QSGListView);
2004     return d->footerComponent;
2005 }
2006
2007 void QSGListView::setFooter(QDeclarativeComponent *footer)
2008 {
2009     Q_D(QSGListView);
2010     if (d->footerComponent != footer) {
2011         if (d->footer) {
2012             // XXX todo - the original did scene()->removeItem().  Why?
2013             d->footer->item->setParentItem(0);
2014             d->footer->item->deleteLater();
2015             delete d->footer;
2016             d->footer = 0;
2017         }
2018         d->footerComponent = footer;
2019         d->minExtentDirty = true;
2020         d->maxExtentDirty = true;
2021         if (isComponentComplete()) {
2022             d->updateFooter();
2023             d->updateViewport();
2024             d->fixupPosition();
2025         }
2026         emit footerChanged();
2027     }
2028 }
2029
2030 QDeclarativeComponent *QSGListView::header() const
2031 {
2032     Q_D(const QSGListView);
2033     return d->headerComponent;
2034 }
2035
2036 void QSGListView::setHeader(QDeclarativeComponent *header)
2037 {
2038     Q_D(QSGListView);
2039     if (d->headerComponent != header) {
2040         if (d->header) {
2041             // XXX todo - the original did scene()->removeItem().  Why?
2042             d->header->item->setParentItem(0);
2043             d->header->item->deleteLater();
2044             delete d->header;
2045             d->header = 0;
2046         }
2047         d->headerComponent = header;
2048         d->minExtentDirty = true;
2049         d->maxExtentDirty = true;
2050         if (isComponentComplete()) {
2051             d->updateHeader();
2052             d->updateFooter();
2053             d->updateViewport();
2054             d->fixupPosition();
2055         }
2056         emit headerChanged();
2057     }
2058 }
2059
2060 void QSGListView::setContentX(qreal pos)
2061 {
2062     Q_D(QSGListView);
2063     // Positioning the view manually should override any current movement state
2064     d->moveReason = QSGListViewPrivate::Other;
2065     QSGFlickable::setContentX(pos);
2066 }
2067
2068 void QSGListView::setContentY(qreal pos)
2069 {
2070     Q_D(QSGListView);
2071     // Positioning the view manually should override any current movement state
2072     d->moveReason = QSGListViewPrivate::Other;
2073     QSGFlickable::setContentY(pos);
2074 }
2075
2076 void QSGListView::updatePolish()
2077 {
2078     Q_D(QSGListView);
2079     QSGFlickable::updatePolish();
2080     d->layout();
2081 }
2082
2083 void QSGListView::viewportMoved()
2084 {
2085     Q_D(QSGListView);
2086     QSGFlickable::viewportMoved();
2087     if (!d->itemCount)
2088         return;
2089     // Recursion can occur due to refill changing the content size.
2090     if (d->inViewportMoved)
2091         return;
2092     d->inViewportMoved = true;
2093     d->lazyRelease = true;
2094     refill();
2095     if (d->flickingHorizontally || d->flickingVertically || d->movingHorizontally || d->movingVertically)
2096         d->moveReason = QSGListViewPrivate::Mouse;
2097     if (d->moveReason != QSGListViewPrivate::SetIndex) {
2098         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange && d->highlight) {
2099             // reposition highlight
2100             qreal pos = d->highlight->position();
2101             qreal viewPos;
2102             qreal highlightStart;
2103             qreal highlightEnd;
2104             if (d->isRightToLeft()) {
2105                 // Handle Right-To-Left exceptions
2106                 viewPos = -d->position()-d->size();
2107                 highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
2108                 highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
2109             } else {
2110                 viewPos = d->position();
2111                 highlightStart = d->highlightRangeStart;
2112                 highlightEnd = d->highlightRangeEnd;
2113             }
2114             if (pos > viewPos + highlightEnd - d->highlight->size())
2115                 pos = viewPos + highlightEnd - d->highlight->size();
2116             if (pos < viewPos + highlightStart)
2117                 pos = viewPos + highlightStart;
2118             d->highlightPosAnimator->stop();
2119             d->highlight->setPosition(qRound(pos));
2120
2121             // update current index
2122             if (FxListItemSG *snapItem = d->snapItemAt(d->highlight->position())) {
2123                 if (snapItem->index >= 0 && snapItem->index != d->currentIndex)
2124                     d->updateCurrent(snapItem->index);
2125             }
2126         }
2127     }
2128
2129     if ((d->flickingHorizontally || d->flickingVertically) && d->correctFlick && !d->inFlickCorrection) {
2130         d->inFlickCorrection = true;
2131         // Near an end and it seems that the extent has changed?
2132         // Recalculate the flick so that we don't end up in an odd position.
2133         if (yflick()) {
2134             if (d->vData.velocity > 0) {
2135                 const qreal minY = minYExtent();
2136                 if ((minY - d->vData.move.value() < height()/2 || d->vData.flickTarget - d->vData.move.value() < height()/2)
2137                     && minY != d->vData.flickTarget)
2138                     d->flickY(-d->vData.smoothVelocity.value());
2139                 d->bufferMode = QSGListViewPrivate::BufferBefore;
2140             } else if (d->vData.velocity < 0) {
2141                 const qreal maxY = maxYExtent();
2142                 if ((d->vData.move.value() - maxY < height()/2 || d->vData.move.value() - d->vData.flickTarget < height()/2)
2143                     && maxY != d->vData.flickTarget)
2144                     d->flickY(-d->vData.smoothVelocity.value());
2145                 d->bufferMode = QSGListViewPrivate::BufferAfter;
2146             }
2147         }
2148
2149         if (xflick()) {
2150             if (d->hData.velocity > 0) {
2151                 const qreal minX = minXExtent();
2152                 if ((minX - d->hData.move.value() < width()/2 || d->hData.flickTarget - d->hData.move.value() < width()/2)
2153                     && minX != d->hData.flickTarget)
2154                     d->flickX(-d->hData.smoothVelocity.value());
2155                 d->bufferMode = d->isRightToLeft() ? QSGListViewPrivate::BufferAfter : QSGListViewPrivate::BufferBefore;
2156             } else if (d->hData.velocity < 0) {
2157                 const qreal maxX = maxXExtent();
2158                 if ((d->hData.move.value() - maxX < width()/2 || d->hData.move.value() - d->hData.flickTarget < width()/2)
2159                     && maxX != d->hData.flickTarget)
2160                     d->flickX(-d->hData.smoothVelocity.value());
2161                 d->bufferMode = d->isRightToLeft() ? QSGListViewPrivate::BufferBefore : QSGListViewPrivate::BufferAfter;
2162             }
2163         }
2164         d->inFlickCorrection = false;
2165     }
2166     d->inViewportMoved = false;
2167 }
2168
2169 qreal QSGListView::minYExtent() const
2170 {
2171     Q_D(const QSGListView);
2172     if (d->orient == QSGListView::Horizontal)
2173         return QSGFlickable::minYExtent();
2174     if (d->minExtentDirty) {
2175         d->minExtent = -d->startPosition();
2176         if (d->header && d->visibleItems.count())
2177             d->minExtent += d->header->size();
2178         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2179             d->minExtent += d->highlightRangeStart;
2180             if (d->sectionCriteria) {
2181                 if (d->visibleItem(0))
2182                     d->minExtent -= d->visibleItem(0)->sectionSize();
2183             }
2184             d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd + 1));
2185         }
2186         d->minExtentDirty = false;
2187     }
2188
2189     return d->minExtent;
2190 }
2191
2192 qreal QSGListView::maxYExtent() const
2193 {
2194     Q_D(const QSGListView);
2195     if (d->orient == QSGListView::Horizontal)
2196         return height();
2197     if (d->maxExtentDirty) {
2198         if (!d->model || !d->model->count()) {
2199             d->maxExtent = d->header ? -d->header->size() : 0;
2200             d->maxExtent += height();
2201         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2202             d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
2203             if (d->highlightRangeEnd != d->highlightRangeStart)
2204                 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd + 1));
2205         } else {
2206             d->maxExtent = -(d->endPosition() - height() + 1);
2207         }
2208         if (d->footer)
2209             d->maxExtent -= d->footer->size();
2210         qreal minY = minYExtent();
2211         if (d->maxExtent > minY)
2212             d->maxExtent = minY;
2213         d->maxExtentDirty = false;
2214     }
2215     return d->maxExtent;
2216 }
2217
2218 qreal QSGListView::minXExtent() const
2219 {
2220     Q_D(const QSGListView);
2221     if (d->orient == QSGListView::Vertical)
2222         return QSGFlickable::minXExtent();
2223     if (d->minExtentDirty) {
2224         d->minExtent = -d->startPosition();
2225         qreal highlightStart;
2226         qreal highlightEnd;
2227         qreal endPositionFirstItem = 0;
2228         if (d->isRightToLeft()) {
2229             if (d->model && d->model->count())
2230                 endPositionFirstItem = d->positionAt(d->model->count()-1);
2231             else if (d->header)
2232                 d->minExtent += d->header->size();
2233             highlightStart = d->highlightRangeStartValid
2234                     ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
2235                     : d->size() - (d->lastPosition()-endPositionFirstItem);
2236             highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
2237             if (d->footer)
2238                 d->minExtent += d->footer->size();
2239             qreal maxX = maxXExtent();
2240             if (d->minExtent < maxX)
2241                 d->minExtent = maxX;
2242         } else {
2243             endPositionFirstItem = d->endPositionAt(0);
2244             highlightStart = d->highlightRangeStart;
2245             highlightEnd = d->highlightRangeEnd;
2246             if (d->header && d->visibleItems.count())
2247                 d->minExtent += d->header->size();
2248         }
2249         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2250             d->minExtent += highlightStart;
2251             d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd + 1));
2252         }
2253         d->minExtentDirty = false;
2254     }
2255
2256     return d->minExtent;
2257 }
2258
2259 qreal QSGListView::maxXExtent() const
2260 {
2261     Q_D(const QSGListView);
2262     if (d->orient == QSGListView::Vertical)
2263         return width();
2264     if (d->maxExtentDirty) {
2265         qreal highlightStart;
2266         qreal highlightEnd;
2267         qreal lastItemPosition = 0;
2268         d->maxExtent = 0;
2269         if (d->isRightToLeft()) {
2270             highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
2271             highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
2272             lastItemPosition = d->endPosition();
2273         } else {
2274             highlightStart = d->highlightRangeStart;
2275             highlightEnd = d->highlightRangeEnd;
2276             if (d->model && d->model->count())
2277                 lastItemPosition = d->positionAt(d->model->count()-1);
2278         }
2279         if (!d->model || !d->model->count()) {
2280             if (!d->isRightToLeft())
2281                 d->maxExtent = d->header ? -d->header->size() : 0;
2282             d->maxExtent += width();
2283         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
2284             d->maxExtent = -(lastItemPosition - highlightStart);
2285             if (highlightEnd != highlightStart) {
2286                 d->maxExtent = d->isRightToLeft()
2287                         ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd + 1))
2288                         : qMin(d->maxExtent, -(d->endPosition() - highlightEnd + 1));
2289             }
2290         } else {
2291             d->maxExtent = -(d->endPosition() - width() + 1);
2292         }
2293         if (d->isRightToLeft()) {
2294             if (d->header && d->visibleItems.count())
2295                 d->maxExtent -= d->header->size();
2296         } else {
2297             if (d->footer)
2298                 d->maxExtent -= d->footer->size();
2299             qreal minX = minXExtent();
2300             if (d->maxExtent > minX)
2301                 d->maxExtent = minX;
2302         }
2303         d->maxExtentDirty = false;
2304     }
2305
2306     return d->maxExtent;
2307 }
2308
2309 void QSGListView::keyPressEvent(QKeyEvent *event)
2310 {
2311     Q_D(QSGListView);
2312     if (d->model && d->model->count() && d->interactive) {
2313         if ((!d->isRightToLeft() && event->key() == Qt::Key_Left)
2314                     || (d->orient == QSGListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Right)
2315                     || (d->orient == QSGListView::Vertical && event->key() == Qt::Key_Up)) {
2316             if (currentIndex() > 0 || (d->wrap && !event->isAutoRepeat())) {
2317                 decrementCurrentIndex();
2318                 event->accept();
2319                 return;
2320             } else if (d->wrap) {
2321                 event->accept();
2322                 return;
2323             }
2324         } else if ((!d->isRightToLeft() && event->key() == Qt::Key_Right)
2325                     || (d->orient == QSGListView::Horizontal && d->isRightToLeft() && event->key() == Qt::Key_Left)
2326                     || (d->orient == QSGListView::Vertical && event->key() == Qt::Key_Down)) {
2327             if (currentIndex() < d->model->count() - 1 || (d->wrap && !event->isAutoRepeat())) {
2328                 incrementCurrentIndex();
2329                 event->accept();
2330                 return;
2331             } else if (d->wrap) {
2332                 event->accept();
2333                 return;
2334             }
2335         }
2336     }
2337     event->ignore();
2338     QSGFlickable::keyPressEvent(event);
2339 }
2340
2341 void QSGListView::geometryChanged(const QRectF &newGeometry,
2342                                   const QRectF &oldGeometry)
2343 {
2344     Q_D(QSGListView);
2345     d->maxExtentDirty = true;
2346     d->minExtentDirty = true;
2347     if (d->isRightToLeft() && d->orient == QSGListView::Horizontal) {
2348         // maintain position relative to the right edge
2349         int dx = newGeometry.width() - oldGeometry.width();
2350         setContentX(contentX() - dx);
2351     }
2352     QSGFlickable::geometryChanged(newGeometry, oldGeometry);
2353 }
2354
2355
2356 void QSGListView::incrementCurrentIndex()
2357 {
2358     Q_D(QSGListView);
2359     int count = d->model ? d->model->count() : 0;
2360     if (count && (currentIndex() < count - 1 || d->wrap)) {
2361         d->moveReason = QSGListViewPrivate::SetIndex;
2362         int index = currentIndex()+1;
2363         setCurrentIndex((index >= 0 && index < count) ? index : 0);
2364     }
2365 }
2366
2367 void QSGListView::decrementCurrentIndex()
2368 {
2369     Q_D(QSGListView);
2370     int count = d->model ? d->model->count() : 0;
2371     if (count && (currentIndex() > 0 || d->wrap)) {
2372         d->moveReason = QSGListViewPrivate::SetIndex;
2373         int index = currentIndex()-1;
2374         setCurrentIndex((index >= 0 && index < count) ? index : count-1);
2375     }
2376 }
2377
2378 void QSGListViewPrivate::positionViewAtIndex(int index, int mode)
2379 {
2380     Q_Q(QSGListView);
2381     if (!isValid())
2382         return;
2383     if (mode < QSGListView::Beginning || mode > QSGListView::Contain)
2384         return;
2385     int idx = qMax(qMin(index, model->count()-1), 0);
2386
2387     if (layoutScheduled)
2388         layout();
2389     qreal pos = isRightToLeft() ? -position() - size() : position();
2390     FxListItemSG *item = visibleItem(idx);
2391     qreal maxExtent;
2392     if (orient == QSGListView::Vertical)
2393         maxExtent = -q->maxYExtent();
2394     else
2395         maxExtent = isRightToLeft() ? q->minXExtent()-size(): -q->maxXExtent();
2396     if (!item) {
2397         int itemPos = positionAt(idx);
2398         // save the currently visible items in case any of them end up visible again
2399         QList<FxListItemSG*> oldVisible = visibleItems;
2400         visibleItems.clear();
2401         visiblePos = itemPos;
2402         visibleIndex = idx;
2403         setPosition(qMin(qreal(itemPos), maxExtent));
2404         // now release the reference to all the old visible items.
2405         for (int i = 0; i < oldVisible.count(); ++i)
2406             releaseItem(oldVisible.at(i));
2407         item = visibleItem(idx);
2408     }
2409     if (item) {
2410         const qreal itemPos = item->position();
2411         switch (mode) {
2412         case QSGListView::Beginning:
2413             pos = itemPos;
2414             if (index < 0 && header)
2415                 pos -= header->size();
2416             break;
2417         case QSGListView::Center:
2418             pos = itemPos - (size() - item->size())/2;
2419             break;
2420         case QSGListView::End:
2421             pos = itemPos - size() + item->size();
2422             if (index >= model->count() && footer)
2423                 pos += footer->size();
2424             break;
2425         case QSGListView::Visible:
2426             if (itemPos > pos + size())
2427                 pos = itemPos - size() + item->size();
2428             else if (item->endPosition() < pos)
2429                 pos = itemPos;
2430             break;
2431         case QSGListView::Contain:
2432             if (item->endPosition() > pos + size())
2433                 pos = itemPos - size() + item->size();
2434             if (itemPos < pos)
2435                 pos = itemPos;
2436         }
2437         pos = qMin(pos, maxExtent);
2438         qreal minExtent;
2439         if (orient == QSGListView::Vertical) {
2440             minExtent = -q->minYExtent();
2441         } else {
2442             minExtent = isRightToLeft() ? q->maxXExtent()-size(): -q->minXExtent();
2443         }
2444         pos = qMax(pos, minExtent);
2445         moveReason = QSGListViewPrivate::Other;
2446         q->cancelFlick();
2447         setPosition(pos);
2448         if (highlight) {
2449             if (autoHighlight) {
2450                 highlight->setPosition(currentItem->itemPosition());
2451                 highlight->setSize(currentItem->itemSize());
2452             }
2453             updateHighlight();
2454         }
2455     }
2456     fixupPosition();
2457 }
2458
2459 void QSGListView::positionViewAtIndex(int index, int mode)
2460 {
2461     Q_D(QSGListView);
2462     if (!d->isValid() || index < 0 || index >= d->model->count())
2463         return;
2464     d->positionViewAtIndex(index, mode);
2465 }
2466
2467 void QSGListView::positionViewAtBeginning()
2468 {
2469     Q_D(QSGListView);
2470     if (!d->isValid())
2471         return;
2472     d->positionViewAtIndex(-1, Beginning);
2473 }
2474
2475 void QSGListView::positionViewAtEnd()
2476 {
2477     Q_D(QSGListView);
2478     if (!d->isValid())
2479         return;
2480     d->positionViewAtIndex(d->model->count(), End);
2481 }
2482
2483 int QSGListView::indexAt(qreal x, qreal y) const
2484 {
2485     Q_D(const QSGListView);
2486     for (int i = 0; i < d->visibleItems.count(); ++i) {
2487         const FxListItemSG *listItem = d->visibleItems.at(i);
2488         if(listItem->contains(x, y))
2489             return listItem->index;
2490     }
2491
2492     return -1;
2493 }
2494
2495 void QSGListView::componentComplete()
2496 {
2497     Q_D(QSGListView);
2498     QSGFlickable::componentComplete();
2499     updateSections();
2500     d->updateHeader();
2501     d->updateFooter();
2502     if (d->isValid()) {
2503         refill();
2504         d->moveReason = QSGListViewPrivate::SetIndex;
2505         if (d->currentIndex < 0 && !d->currentIndexCleared)
2506             d->updateCurrent(0);
2507         else
2508             d->updateCurrent(d->currentIndex);
2509         if (d->highlight && d->currentItem) {
2510             if (d->autoHighlight)
2511                 d->highlight->setPosition(d->currentItem->position());
2512             d->updateTrackedItem();
2513         }
2514         d->moveReason = QSGListViewPrivate::Other;
2515         d->fixupPosition();
2516     }
2517 }
2518
2519 void QSGListView::updateSections()
2520 {
2521     Q_D(QSGListView);
2522     if (isComponentComplete() && d->model) {
2523         QList<QByteArray> roles;
2524         if (d->sectionCriteria && !d->sectionCriteria->property().isEmpty())
2525             roles << d->sectionCriteria->property().toUtf8();
2526         d->model->setWatchedRoles(roles);
2527         d->updateSections();
2528         if (d->itemCount)
2529             d->layout();
2530     }
2531 }
2532
2533 void QSGListView::refill()
2534 {
2535     Q_D(QSGListView);
2536     if (d->isRightToLeft())
2537         d->refill(-d->position()-d->size()+1, -d->position());
2538     else
2539         d->refill(d->position(), d->position()+d->size()-1);
2540 }
2541
2542 void QSGListView::trackedPositionChanged()
2543 {
2544     Q_D(QSGListView);
2545     if (!d->trackedItem || !d->currentItem)
2546         return;
2547     if (d->moveReason == QSGListViewPrivate::SetIndex) {
2548         qreal trackedPos = qCeil(d->trackedItem->position());
2549         qreal trackedSize = d->trackedItem->size();
2550         if (d->trackedItem != d->currentItem) {
2551             trackedPos -= d->currentItem->sectionSize();
2552             trackedSize += d->currentItem->sectionSize();
2553         }
2554         qreal viewPos;
2555         qreal highlightStart;
2556         qreal highlightEnd;
2557         if (d->isRightToLeft()) {
2558             viewPos = -d->position()-d->size();
2559             highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
2560             highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
2561         } else {
2562             viewPos = d->position();
2563             highlightStart = d->highlightRangeStart;
2564             highlightEnd = d->highlightRangeEnd;
2565         }
2566         qreal pos = viewPos;
2567         if (d->haveHighlightRange) {
2568             if (d->highlightRange == StrictlyEnforceRange) {
2569                 if (trackedPos > pos + highlightEnd - d->trackedItem->size())
2570                     pos = trackedPos - highlightEnd + d->trackedItem->size();
2571                 if (trackedPos < pos + highlightStart)
2572                     pos = trackedPos - highlightStart;
2573             } else {
2574                 if (trackedPos < d->startPosition() + highlightStart) {
2575                     pos = d->startPosition();
2576                 } else if (d->trackedItem->endPosition() > d->endPosition() - d->size() + highlightEnd) {
2577                     pos = d->endPosition() - d->size() + 1;
2578                     if (pos < d->startPosition())
2579                         pos = d->startPosition();
2580                 } else {
2581                     if (trackedPos < viewPos + highlightStart) {
2582                         pos = trackedPos - highlightStart;
2583                     } else if (trackedPos > viewPos + highlightEnd - trackedSize) {
2584                         pos = trackedPos - highlightEnd + trackedSize;
2585                     }
2586                 }
2587             }
2588         } else {
2589             if (trackedPos < viewPos && d->currentItem->position() < viewPos) {
2590                 pos = d->currentItem->position() < trackedPos ? trackedPos : d->currentItem->position();
2591             } else if (d->trackedItem->endPosition() >= viewPos + d->size()
2592                         && d->currentItem->endPosition() >= viewPos + d->size()) {
2593                 if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) {
2594                     pos = d->trackedItem->endPosition() - d->size() + 1;
2595                      if (trackedSize > d->size())
2596                         pos = trackedPos;
2597                 } else {
2598                     pos = d->currentItem->endPosition() - d->size() + 1;
2599                     if (d->currentItem->size() > d->size())
2600                         pos = d->currentItem->position();
2601                 }
2602             }
2603         }
2604         if (viewPos != pos) {
2605             cancelFlick();
2606             d->calcVelocity = true;
2607             d->setPosition(pos);
2608             d->calcVelocity = false;
2609         }
2610     }
2611 }
2612
2613 void QSGListView::itemsInserted(int modelIndex, int count)
2614 {
2615     Q_D(QSGListView);
2616     if (!isComponentComplete())
2617         return;
2618     d->updateUnrequestedIndexes();
2619     d->moveReason = QSGListViewPrivate::Other;
2620
2621     qreal tempPos = d->isRightToLeft() ? -d->position()-d->size() : d->position();
2622     int index = d->visibleItems.count() ? d->mapFromModel(modelIndex) : 0;
2623     if (index < 0) {
2624         int i = d->visibleItems.count() - 1;
2625         while (i > 0 && d->visibleItems.at(i)->index == -1)
2626             --i;
2627         if (i == 0 && d->visibleItems.first()->index == -1) {
2628             // there are no visible items except items marked for removal
2629             index = d->visibleItems.count();
2630         } else if (d->visibleItems.at(i)->index + 1 == modelIndex
2631             && d->visibleItems.at(i)->endPosition() < d->buffer+tempPos+d->size()-1) {
2632             // Special case of appending an item to the model.
2633             index = d->visibleItems.count();
2634         } else {
2635             if (modelIndex < d->visibleIndex) {
2636                 // Insert before visible items
2637                 d->visibleIndex += count;
2638                 for (int i = 0; i < d->visibleItems.count(); ++i) {
2639                     FxListItemSG *listItem = d->visibleItems.at(i);
2640                     if (listItem->index != -1 && listItem->index >= modelIndex)
2641                         listItem->index += count;
2642                 }
2643             }
2644             if (d->currentIndex >= modelIndex) {
2645                 // adjust current item index
2646                 d->currentIndex += count;
2647                 if (d->currentItem)
2648                     d->currentItem->index = d->currentIndex;
2649                 emit currentIndexChanged();
2650             }
2651             d->scheduleLayout();
2652             d->itemCount += count;
2653             emit countChanged();
2654             return;
2655         }
2656     }
2657
2658     // index can be the next item past the end of the visible items list (i.e. appended)
2659     int pos = 0;
2660     if (d->visibleItems.count()) {
2661         pos = index < d->visibleItems.count() ? d->visibleItems.at(index)->position()
2662                                                 : d->visibleItems.last()->endPosition()+d->spacing+1;
2663     } else if (d->itemCount == 0 && d->header) {
2664         pos = d->header->size();
2665     }
2666
2667     int initialPos = pos;
2668     int diff = 0;
2669     QList<FxListItemSG*> added;
2670     bool addedVisible = false;
2671     FxListItemSG *firstVisible = d->firstVisibleItem();
2672     if (firstVisible && pos < firstVisible->position()) {
2673         // Insert items before the visible item.
2674         int insertionIdx = index;
2675         int i = 0;
2676         int from = tempPos - d->buffer;
2677         for (i = count-1; i >= 0 && pos > from; --i) {
2678             if (!addedVisible) {
2679                 d->scheduleLayout();
2680                 addedVisible = true;
2681             }
2682             FxListItemSG *item = d->createItem(modelIndex + i);
2683             d->visibleItems.insert(insertionIdx, item);
2684             pos -= item->size() + d->spacing;
2685             item->setPosition(pos);
2686             index++;
2687         }
2688         if (i >= 0) {
2689             // If we didn't insert all our new items - anything
2690             // before the current index is not visible - remove it.
2691             while (insertionIdx--) {
2692                 FxListItemSG *item = d->visibleItems.takeFirst();
2693                 if (item->index != -1)
2694                     d->visibleIndex++;
2695                 d->releaseItem(item);
2696             }
2697         } else {
2698             // adjust pos of items before inserted items.
2699             for (int i = insertionIdx-1; i >= 0; i--) {
2700                 FxListItemSG *listItem = d->visibleItems.at(i);
2701                 listItem->setPosition(listItem->position() - (initialPos - pos));
2702             }
2703         }
2704     } else {
2705         int i = 0;
2706         int to = d->buffer+tempPos+d->size()-1;
2707         for (i = 0; i < count && pos <= to; ++i) {
2708             if (!addedVisible) {
2709                 d->scheduleLayout();
2710                 addedVisible = true;
2711             }
2712             FxListItemSG *item = d->createItem(modelIndex + i);
2713             d->visibleItems.insert(index, item);
2714             item->setPosition(pos);
2715             added.append(item);
2716             pos += item->size() + d->spacing;
2717             ++index;
2718         }
2719         if (i != count) {
2720             // We didn't insert all our new items, which means anything
2721             // beyond the current index is not visible - remove it.
2722             while (d->visibleItems.count() > index)
2723                 d->releaseItem(d->visibleItems.takeLast());
2724         }
2725         diff = pos - initialPos;
2726     }
2727     if (d->itemCount && d->currentIndex >= modelIndex) {
2728         // adjust current item index
2729         d->currentIndex += count;
2730         if (d->currentItem) {
2731             d->currentItem->index = d->currentIndex;
2732             d->currentItem->setPosition(d->currentItem->position() + diff);
2733         }
2734         emit currentIndexChanged();
2735     } else if (!d->itemCount && (!d->currentIndex || (d->currentIndex < 0 && !d->currentIndexCleared))) {
2736         d->updateCurrent(0);
2737     }
2738     // Update the indexes of the following visible items.
2739     for (; index < d->visibleItems.count(); ++index) {
2740         FxListItemSG *listItem = d->visibleItems.at(index);
2741         if (d->currentItem && listItem->item != d->currentItem->item)
2742             listItem->setPosition(listItem->position() + diff);
2743         if (listItem->index != -1)
2744             listItem->index += count;
2745     }
2746     // everything is in order now - emit add() signal
2747     for (int j = 0; j < added.count(); ++j)
2748         added.at(j)->attached->emitAdd();
2749
2750     d->updateSections();
2751     d->itemCount += count;
2752     emit countChanged();
2753 }
2754
2755 void QSGListView::itemsRemoved(int modelIndex, int count)
2756 {
2757     Q_D(QSGListView);
2758     if (!isComponentComplete())
2759         return;
2760     d->moveReason = QSGListViewPrivate::Other;
2761     d->updateUnrequestedIndexes();
2762     d->itemCount -= count;
2763
2764     FxListItemSG *firstVisible = d->firstVisibleItem();
2765     int preRemovedSize = 0;
2766     bool removedVisible = false;
2767     // Remove the items from the visible list, skipping anything already marked for removal
2768     QList<FxListItemSG*>::Iterator it = d->visibleItems.begin();
2769     while (it != d->visibleItems.end()) {
2770         FxListItemSG *item = *it;
2771         if (item->index == -1 || item->index < modelIndex) {
2772             // already removed, or before removed items
2773             ++it;
2774         } else if (item->index >= modelIndex + count) {
2775             // after removed items
2776             item->index -= count;
2777             ++it;
2778         } else {
2779             // removed item
2780             if (!removedVisible) {
2781                 d->scheduleLayout();
2782                 removedVisible = true;
2783             }
2784             item->attached->emitRemove();
2785             if (item->attached->delayRemove()) {
2786                 item->index = -1;
2787                 connect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()), Qt::QueuedConnection);
2788                 ++it;
2789             } else {
2790                 if (item == firstVisible)
2791                     firstVisible = 0;
2792                 if (firstVisible && item->position() < firstVisible->position())
2793                     preRemovedSize += item->size();
2794                 it = d->visibleItems.erase(it);
2795                 d->releaseItem(item);
2796             }
2797         }
2798     }
2799
2800     if (firstVisible && d->visibleItems.first() != firstVisible)
2801         d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + preRemovedSize);
2802
2803     // fix current
2804     if (d->currentIndex >= modelIndex + count) {
2805         d->currentIndex -= count;
2806         if (d->currentItem)
2807             d->currentItem->index -= count;
2808         emit currentIndexChanged();
2809     } else if (d->currentIndex >= modelIndex && d->currentIndex < modelIndex + count) {
2810         // current item has been removed.
2811         if (d->currentItem) {
2812             d->currentItem->attached->setIsCurrentItem(false);
2813             d->releaseItem(d->currentItem);
2814             d->currentItem = 0;
2815         }
2816         d->currentIndex = -1;
2817         if (d->itemCount)
2818             d->updateCurrent(qMin(modelIndex, d->itemCount-1));
2819         else
2820             emit currentIndexChanged();
2821     }
2822
2823     // update visibleIndex
2824     bool haveVisibleIndex = false;
2825     for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
2826         if ((*it)->index != -1) {
2827             d->visibleIndex = (*it)->index;
2828             haveVisibleIndex = true;
2829             break;
2830         }
2831     }
2832
2833     if (removedVisible && !haveVisibleIndex) {
2834         d->timeline.clear();
2835         if (d->itemCount == 0) {
2836             d->visibleIndex = 0;
2837             d->visiblePos = d->header ? d->header->size() : 0;
2838             d->setPosition(0);
2839             d->updateHeader();
2840             d->updateFooter();
2841         } else {
2842             if (modelIndex < d->visibleIndex)
2843                 d->visibleIndex = modelIndex+1;
2844             d->visibleIndex = qMax(qMin(d->visibleIndex, d->itemCount-1), 0);
2845         }
2846     }
2847
2848     d->updateSections();
2849     emit countChanged();
2850 }
2851
2852 void QSGListView::destroyRemoved()
2853 {
2854     Q_D(QSGListView);
2855     for (QList<FxListItemSG*>::Iterator it = d->visibleItems.begin();
2856             it != d->visibleItems.end();) {
2857         FxListItemSG *listItem = *it;
2858         if (listItem->index == -1 && listItem->attached->delayRemove() == false) {
2859             d->releaseItem(listItem);
2860             it = d->visibleItems.erase(it);
2861         } else {
2862             ++it;
2863         }
2864     }
2865
2866     // Correct the positioning of the items
2867     d->updateSections();
2868     d->layout();
2869 }
2870
2871 void QSGListView::itemsMoved(int from, int to, int count)
2872 {
2873     Q_D(QSGListView);
2874     if (!isComponentComplete())
2875         return;
2876     d->updateUnrequestedIndexes();
2877
2878     if (d->visibleItems.isEmpty()) {
2879         refill();
2880         return;
2881     }
2882
2883     d->moveReason = QSGListViewPrivate::Other;
2884     FxListItemSG *firstVisible = d->firstVisibleItem();
2885     qreal firstItemPos = firstVisible->position();
2886     QHash<int,FxListItemSG*> moved;
2887     int moveBy = 0;
2888
2889     QList<FxListItemSG*>::Iterator it = d->visibleItems.begin();
2890     while (it != d->visibleItems.end()) {
2891         FxListItemSG *item = *it;
2892         if (item->index >= from && item->index < from + count) {
2893             // take the items that are moving
2894             item->index += (to-from);
2895             moved.insert(item->index, item);
2896             if (item->position() < firstItemPos)
2897                 moveBy += item->size();
2898             it = d->visibleItems.erase(it);
2899         } else {
2900             // move everything after the moved items.
2901             if (item->index > from && item->index != -1)
2902                 item->index -= count;
2903             ++it;
2904         }
2905     }
2906
2907     int remaining = count;
2908     int endIndex = d->visibleIndex;
2909     it = d->visibleItems.begin();
2910     while (it != d->visibleItems.end()) {
2911         FxListItemSG *item = *it;
2912         if (remaining && item->index >= to && item->index < to + count) {
2913             // place items in the target position, reusing any existing items
2914             FxListItemSG *movedItem = moved.take(item->index);
2915             if (!movedItem)
2916                 movedItem = d->createItem(item->index);
2917             if (item->index <= firstVisible->index)
2918                 moveBy -= movedItem->size();
2919             it = d->visibleItems.insert(it, movedItem);
2920             ++it;
2921             --remaining;
2922         } else {
2923             if (item->index != -1) {
2924                 if (item->index >= to) {
2925                     // update everything after the moved items.
2926                     item->index += count;
2927                 }
2928                 endIndex = item->index;
2929             }
2930             ++it;
2931         }
2932     }
2933
2934     // If we have moved items to the end of the visible items
2935     // then add any existing moved items that we have
2936     while (FxListItemSG *item = moved.take(endIndex+1)) {
2937         d->visibleItems.append(item);
2938         ++endIndex;
2939     }
2940
2941     // update visibleIndex
2942     for (it = d->visibleItems.begin(); it != d->visibleItems.end(); ++it) {
2943         if ((*it)->index != -1) {
2944             d->visibleIndex = (*it)->index;
2945             break;
2946         }
2947     }
2948
2949     // Fix current index
2950     if (d->currentIndex >= 0 && d->currentItem) {
2951         int oldCurrent = d->currentIndex;
2952         d->currentIndex = d->model->indexOf(d->currentItem->item, this);
2953         if (oldCurrent != d->currentIndex) {
2954             d->currentItem->index = d->currentIndex;
2955             emit currentIndexChanged();
2956         }
2957     }
2958
2959     // Whatever moved items remain are no longer visible items.
2960     while (moved.count()) {
2961         int idx = moved.begin().key();
2962         FxListItemSG *item = moved.take(idx);
2963         if (d->currentItem && item->item == d->currentItem->item)
2964             item->setPosition(d->positionAt(idx));
2965         d->releaseItem(item);
2966     }
2967
2968     // Ensure we don't cause an ugly list scroll.
2969     d->visibleItems.first()->setPosition(d->visibleItems.first()->position() + moveBy);
2970
2971     d->updateSections();
2972     d->layout();
2973 }
2974
2975 void QSGListView::itemsChanged(int, int)
2976 {
2977     Q_D(QSGListView);
2978     d->updateSections();
2979     d->layout();
2980 }
2981
2982 void QSGListView::modelReset()
2983 {
2984     Q_D(QSGListView);
2985     d->moveReason = QSGListViewPrivate::SetIndex;
2986     d->regenerate();
2987     if (d->highlight && d->currentItem) {
2988         if (d->autoHighlight)
2989             d->highlight->setPosition(d->currentItem->position());
2990         d->updateTrackedItem();
2991     }
2992     d->moveReason = QSGListViewPrivate::Other;
2993     emit countChanged();
2994 }
2995
2996 void QSGListView::createdItem(int index, QSGItem *item)
2997 {
2998     Q_D(QSGListView);
2999     if (d->requestedIndex != index) {
3000         item->setParentItem(contentItem());
3001         d->unrequestedItems.insert(item, index);
3002         if (d->orient == QSGListView::Vertical) {
3003             item->setY(d->positionAt(index));
3004         } else {
3005             if (d->isRightToLeft())
3006                 item->setX(-d->positionAt(index)-item->width());
3007             else
3008                 item->setX(d->positionAt(index));
3009         }
3010     }
3011 }
3012
3013 void QSGListView::destroyingItem(QSGItem *item)
3014 {
3015     Q_D(QSGListView);
3016     d->unrequestedItems.remove(item);
3017 }
3018
3019 void QSGListView::animStopped()
3020 {
3021     Q_D(QSGListView);
3022     d->bufferMode = QSGListViewPrivate::NoBuffer;
3023     if (d->haveHighlightRange && d->highlightRange == QSGListView::StrictlyEnforceRange)
3024         d->updateHighlight();
3025 }
3026
3027 QSGListViewAttached *QSGListView::qmlAttachedProperties(QObject *obj)
3028 {
3029     return new QSGListViewAttached(obj);
3030 }
3031
3032 QT_END_NAMESPACE