Ensure items in ListView and GridViews viewport are visible.
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickitemview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickitemview_p_p.h"
43
44 QT_BEGIN_NAMESPACE
45
46
47 FxViewItem::FxViewItem(QQuickItem *i, bool own)
48     : item(i), ownItem(own), index(-1)
49 {
50 }
51
52 FxViewItem::~FxViewItem()
53 {
54     if (ownItem && item) {
55         item->setParentItem(0);
56         item->deleteLater();
57         item = 0;
58     }
59 }
60
61
62 QQuickItemViewChangeSet::QQuickItemViewChangeSet()
63     : active(false)
64 {
65     reset();
66 }
67
68 bool QQuickItemViewChangeSet::hasPendingChanges() const
69 {
70     return !pendingChanges.isEmpty();
71 }
72
73 void QQuickItemViewChangeSet::applyChanges(const QDeclarativeChangeSet &changeSet)
74 {
75     pendingChanges.apply(changeSet);
76
77     int moveId = -1;
78     int moveOffset;
79
80     foreach (const QDeclarativeChangeSet::Remove &r, changeSet.removes()) {
81         itemCount -= r.count;
82         if (moveId == -1 && newCurrentIndex >= r.index + r.count) {
83             newCurrentIndex -= r.count;
84             currentChanged = true;
85         } else if (moveId == -1 && newCurrentIndex >= r.index && newCurrentIndex < r.index + r.count) {
86             // current item has been removed.
87             if (r.isMove()) {
88                 moveId = r.moveId;
89                 moveOffset = newCurrentIndex - r.index;
90             } else {
91                 currentRemoved = true;
92                 newCurrentIndex = -1;
93                 if (itemCount)
94                     newCurrentIndex = qMin(r.index, itemCount - 1);
95             }
96             currentChanged = true;
97         }
98     }
99     foreach (const QDeclarativeChangeSet::Insert &i, changeSet.inserts()) {
100         if (moveId == -1) {
101             if (itemCount && newCurrentIndex >= i.index) {
102                 newCurrentIndex += i.count;
103                 currentChanged = true;
104             } else if (newCurrentIndex < 0) {
105                 newCurrentIndex = 0;
106                 currentChanged = true;
107             } else if (newCurrentIndex == 0 && !itemCount) {
108                 // this is the first item, set the initial current index
109                 currentChanged = true;
110             }
111         } else if (moveId == i.moveId) {
112             newCurrentIndex = i.index + moveOffset;
113         }
114         itemCount += i.count;
115     }
116 }
117
118 void QQuickItemViewChangeSet::prepare(int currentIndex, int count)
119 {
120     if (active)
121         return;
122     reset();
123     active = true;
124     itemCount = count;
125     newCurrentIndex = currentIndex;
126 }
127
128 void QQuickItemViewChangeSet::reset()
129 {
130     itemCount = 0;
131     newCurrentIndex = -1;
132     pendingChanges.clear();
133     removedItems.clear();
134     active = false;
135     currentChanged = false;
136     currentRemoved = false;
137 }
138
139
140 QQuickItemView::QQuickItemView(QQuickFlickablePrivate &dd, QQuickItem *parent)
141     : QQuickFlickable(dd, parent)
142 {
143     Q_D(QQuickItemView);
144     d->init();
145 }
146
147 QQuickItemView::~QQuickItemView()
148 {
149     Q_D(QQuickItemView);
150     d->clear();
151     if (d->ownModel)
152         delete d->model;
153     delete d->header;
154     delete d->footer;
155 }
156
157
158 QQuickItem *QQuickItemView::currentItem() const
159 {
160     Q_D(const QQuickItemView);
161     if (!d->currentItem)
162         return 0;
163     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
164     return d->currentItem->item;
165 }
166
167 QVariant QQuickItemView::model() const
168 {
169     Q_D(const QQuickItemView);
170     return d->modelVariant;
171 }
172
173 void QQuickItemView::setModel(const QVariant &model)
174 {
175     Q_D(QQuickItemView);
176     if (d->modelVariant == model)
177         return;
178     if (d->model) {
179         disconnect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)),
180                 this, SLOT(modelUpdated(QDeclarativeChangeSet,bool)));
181         disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
182         disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
183         disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
184     }
185
186     QQuickVisualModel *oldModel = d->model;
187
188     d->clear();
189     d->setPosition(d->contentStartPosition());
190     d->model = 0;
191     d->modelVariant = model;
192
193     QObject *object = qvariant_cast<QObject*>(model);
194     QQuickVisualModel *vim = 0;
195     if (object && (vim = qobject_cast<QQuickVisualModel *>(object))) {
196         if (d->ownModel) {
197             delete oldModel;
198             d->ownModel = false;
199         }
200         d->model = vim;
201     } else {
202         if (!d->ownModel) {
203             d->model = new QQuickVisualDataModel(qmlContext(this), this);
204             d->ownModel = true;
205             if (isComponentComplete())
206                 static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
207         } else {
208             d->model = oldModel;
209         }
210         if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
211             dataModel->setModel(model);
212     }
213
214     if (d->model) {
215         d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
216         connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
217         connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
218         connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
219         if (isComponentComplete()) {
220             updateSections();
221             d->refill();
222             if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
223                 setCurrentIndex(0);
224             } else {
225                 d->moveReason = QQuickItemViewPrivate::SetIndex;
226                 d->updateCurrent(d->currentIndex);
227                 if (d->highlight && d->currentItem) {
228                     if (d->autoHighlight)
229                         d->resetHighlightPosition();
230                     d->updateTrackedItem();
231                 }
232                 d->moveReason = QQuickItemViewPrivate::Other;
233             }
234             d->updateViewport();
235         }
236         connect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)),
237                 this, SLOT(modelUpdated(QDeclarativeChangeSet,bool)));
238         emit countChanged();
239     }
240     emit modelChanged();
241 }
242
243 QDeclarativeComponent *QQuickItemView::delegate() const
244 {
245     Q_D(const QQuickItemView);
246     if (d->model) {
247         if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
248             return dataModel->delegate();
249     }
250
251     return 0;
252 }
253
254 void QQuickItemView::setDelegate(QDeclarativeComponent *delegate)
255 {
256     Q_D(QQuickItemView);
257     if (delegate == this->delegate())
258         return;
259     if (!d->ownModel) {
260         d->model = new QQuickVisualDataModel(qmlContext(this));
261         d->ownModel = true;
262     }
263     if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) {
264         int oldCount = dataModel->count();
265         dataModel->setDelegate(delegate);
266         if (isComponentComplete()) {
267             for (int i = 0; i < d->visibleItems.count(); ++i)
268                 d->releaseItem(d->visibleItems.at(i));
269             d->visibleItems.clear();
270             d->releaseItem(d->currentItem);
271             d->currentItem = 0;
272             updateSections();
273             d->refill();
274             d->moveReason = QQuickItemViewPrivate::SetIndex;
275             d->updateCurrent(d->currentIndex);
276             if (d->highlight && d->currentItem) {
277                 if (d->autoHighlight)
278                     d->resetHighlightPosition();
279                 d->updateTrackedItem();
280             }
281             d->moveReason = QQuickItemViewPrivate::Other;
282             d->updateViewport();
283         }
284         if (oldCount != dataModel->count())
285             emit countChanged();
286     }
287     emit delegateChanged();
288 }
289
290
291 int QQuickItemView::count() const
292 {
293     Q_D(const QQuickItemView);
294     if (!d->model)
295         return 0;
296     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
297     return d->model->count();
298 }
299
300 int QQuickItemView::currentIndex() const
301 {
302     Q_D(const QQuickItemView);
303     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
304     return d->currentIndex;
305 }
306
307 void QQuickItemView::setCurrentIndex(int index)
308 {
309     Q_D(QQuickItemView);
310     if (d->requestedIndex >= 0 && !d->requestedAsync)  // currently creating item
311         return;
312     d->currentIndexCleared = (index == -1);
313
314     d->applyPendingChanges();
315     if (index == d->currentIndex)
316         return;
317     if (isComponentComplete() && d->isValid()) {
318         d->moveReason = QQuickItemViewPrivate::SetIndex;
319         d->updateCurrent(index);
320     } else if (d->currentIndex != index) {
321         d->currentIndex = index;
322         emit currentIndexChanged();
323     }
324 }
325
326
327 bool QQuickItemView::isWrapEnabled() const
328 {
329     Q_D(const QQuickItemView);
330     return d->wrap;
331 }
332
333 void QQuickItemView::setWrapEnabled(bool wrap)
334 {
335     Q_D(QQuickItemView);
336     if (d->wrap == wrap)
337         return;
338     d->wrap = wrap;
339     emit keyNavigationWrapsChanged();
340 }
341
342 int QQuickItemView::cacheBuffer() const
343 {
344     Q_D(const QQuickItemView);
345     return d->buffer;
346 }
347
348 void QQuickItemView::setCacheBuffer(int b)
349 {
350     Q_D(QQuickItemView);
351     if (d->buffer != b) {
352         d->buffer = b;
353         if (isComponentComplete()) {
354             d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
355             d->refill();
356         }
357         emit cacheBufferChanged();
358     }
359 }
360
361
362 Qt::LayoutDirection QQuickItemView::layoutDirection() const
363 {
364     Q_D(const QQuickItemView);
365     return d->layoutDirection;
366 }
367
368 void QQuickItemView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
369 {
370     Q_D(QQuickItemView);
371     if (d->layoutDirection != layoutDirection) {
372         d->layoutDirection = layoutDirection;
373         d->regenerate();
374         emit layoutDirectionChanged();
375         emit effectiveLayoutDirectionChanged();
376     }
377 }
378
379 Qt::LayoutDirection QQuickItemView::effectiveLayoutDirection() const
380 {
381     Q_D(const QQuickItemView);
382     if (d->effectiveLayoutMirror)
383         return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
384     else
385         return d->layoutDirection;
386 }
387
388
389 QDeclarativeComponent *QQuickItemView::header() const
390 {
391     Q_D(const QQuickItemView);
392     return d->headerComponent;
393 }
394
395 QQuickItem *QQuickItemView::headerItem() const
396 {
397     Q_D(const QQuickItemView);
398     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
399     return d->header ? d->header->item : 0;
400 }
401
402 void QQuickItemView::setHeader(QDeclarativeComponent *headerComponent)
403 {
404     Q_D(QQuickItemView);
405     if (d->headerComponent != headerComponent) {
406         d->applyPendingChanges();
407         delete d->header;
408         d->header = 0;
409         d->headerComponent = headerComponent;
410
411         d->markExtentsDirty();
412
413         if (isComponentComplete()) {
414             d->updateHeader();
415             d->updateFooter();
416             d->updateViewport();
417             d->fixupPosition();
418         } else {
419             emit headerItemChanged();
420         }
421         emit headerChanged();
422     }
423 }
424
425 QDeclarativeComponent *QQuickItemView::footer() const
426 {
427     Q_D(const QQuickItemView);
428     return d->footerComponent;
429 }
430
431 QQuickItem *QQuickItemView::footerItem() const
432 {
433     Q_D(const QQuickItemView);
434     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
435     return d->footer ? d->footer->item : 0;
436 }
437
438 void QQuickItemView::setFooter(QDeclarativeComponent *footerComponent)
439 {
440     Q_D(QQuickItemView);
441     if (d->footerComponent != footerComponent) {
442         d->applyPendingChanges();
443         delete d->footer;
444         d->footer = 0;
445         d->footerComponent = footerComponent;
446
447         if (isComponentComplete()) {
448             d->updateFooter();
449             d->updateViewport();
450             d->fixupPosition();
451         } else {
452             emit footerItemChanged();
453         }
454         emit footerChanged();
455     }
456 }
457
458 QDeclarativeComponent *QQuickItemView::highlight() const
459 {
460     Q_D(const QQuickItemView);
461     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
462     return d->highlightComponent;
463 }
464
465 void QQuickItemView::setHighlight(QDeclarativeComponent *highlightComponent)
466 {
467     Q_D(QQuickItemView);
468     if (highlightComponent != d->highlightComponent) {
469         d->applyPendingChanges();
470         d->highlightComponent = highlightComponent;
471         d->createHighlight();
472         if (d->currentItem)
473             d->updateHighlight();
474         emit highlightChanged();
475     }
476 }
477
478 QQuickItem *QQuickItemView::highlightItem() const
479 {
480     Q_D(const QQuickItemView);
481     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
482     return d->highlight ? d->highlight->item : 0;
483 }
484
485 bool QQuickItemView::highlightFollowsCurrentItem() const
486 {
487     Q_D(const QQuickItemView);
488     return d->autoHighlight;
489 }
490
491 void QQuickItemView::setHighlightFollowsCurrentItem(bool autoHighlight)
492 {
493     Q_D(QQuickItemView);
494     if (d->autoHighlight != autoHighlight) {
495         d->autoHighlight = autoHighlight;
496         if (autoHighlight)
497             d->updateHighlight();
498         emit highlightFollowsCurrentItemChanged();
499     }
500 }
501
502 QQuickItemView::HighlightRangeMode QQuickItemView::highlightRangeMode() const
503 {
504     Q_D(const QQuickItemView);
505     return static_cast<QQuickItemView::HighlightRangeMode>(d->highlightRange);
506 }
507
508 void QQuickItemView::setHighlightRangeMode(HighlightRangeMode mode)
509 {
510     Q_D(QQuickItemView);
511     if (d->highlightRange == mode)
512         return;
513     d->highlightRange = mode;
514     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
515     emit highlightRangeModeChanged();
516 }
517
518 //###Possibly rename these properties, since they are very useful even without a highlight?
519 qreal QQuickItemView::preferredHighlightBegin() const
520 {
521     Q_D(const QQuickItemView);
522     return d->highlightRangeStart;
523 }
524
525 void QQuickItemView::setPreferredHighlightBegin(qreal start)
526 {
527     Q_D(QQuickItemView);
528     d->highlightRangeStartValid = true;
529     if (d->highlightRangeStart == start)
530         return;
531     d->highlightRangeStart = start;
532     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
533     emit preferredHighlightBeginChanged();
534 }
535
536 void QQuickItemView::resetPreferredHighlightBegin()
537 {
538     Q_D(QQuickItemView);
539     d->highlightRangeStartValid = false;
540     if (d->highlightRangeStart == 0)
541         return;
542     d->highlightRangeStart = 0;
543     emit preferredHighlightBeginChanged();
544 }
545
546 qreal QQuickItemView::preferredHighlightEnd() const
547 {
548     Q_D(const QQuickItemView);
549     return d->highlightRangeEnd;
550 }
551
552 void QQuickItemView::setPreferredHighlightEnd(qreal end)
553 {
554     Q_D(QQuickItemView);
555     d->highlightRangeEndValid = true;
556     if (d->highlightRangeEnd == end)
557         return;
558     d->highlightRangeEnd = end;
559     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
560     emit preferredHighlightEndChanged();
561 }
562
563 void QQuickItemView::resetPreferredHighlightEnd()
564 {
565     Q_D(QQuickItemView);
566     d->highlightRangeEndValid = false;
567     if (d->highlightRangeEnd == 0)
568         return;
569     d->highlightRangeEnd = 0;
570     emit preferredHighlightEndChanged();
571 }
572
573 int QQuickItemView::highlightMoveDuration() const
574 {
575     Q_D(const QQuickItemView);
576     return d->highlightMoveDuration;
577 }
578
579 void QQuickItemView::setHighlightMoveDuration(int duration)
580 {
581     Q_D(QQuickItemView);
582     if (d->highlightMoveDuration != duration) {
583         d->highlightMoveDuration = duration;
584         emit highlightMoveDurationChanged();
585     }
586 }
587
588 void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
589 {
590     Q_Q(QQuickItemView);
591     if (!isValid())
592         return;
593     if (mode < QQuickItemView::Beginning || mode > QQuickItemView::Contain)
594         return;
595
596     applyPendingChanges();
597     int idx = qMax(qMin(index, model->count()-1), 0);
598
599     qreal pos = isContentFlowReversed() ? -position() - size() : position();
600     FxViewItem *item = visibleItem(idx);
601     qreal maxExtent;
602     if (layoutOrientation() == Qt::Vertical)
603         maxExtent = -q->maxYExtent();
604     else
605         maxExtent = isContentFlowReversed() ? q->minXExtent()-size(): -q->maxXExtent();
606     if (!item) {
607         int itemPos = positionAt(idx);
608         changedVisibleIndex(idx);
609         // save the currently visible items in case any of them end up visible again
610         QList<FxViewItem *> oldVisible = visibleItems;
611         visibleItems.clear();
612         setPosition(qMin(qreal(itemPos), maxExtent));
613         // now release the reference to all the old visible items.
614         for (int i = 0; i < oldVisible.count(); ++i)
615             releaseItem(oldVisible.at(i));
616         item = visibleItem(idx);
617     }
618     if (item) {
619         const qreal itemPos = item->position();
620         switch (mode) {
621         case QQuickItemView::Beginning:
622             pos = itemPos;
623             if (index < 0 && header)
624                 pos -= headerSize();
625             break;
626         case QQuickItemView::Center:
627             pos = itemPos - (size() - item->size())/2;
628             break;
629         case QQuickItemView::End:
630             pos = itemPos - size() + item->size();
631             if (index >= model->count() && footer)
632                 pos += footerSize();
633             break;
634         case QQuickItemView::Visible:
635             if (itemPos > pos + size())
636                 pos = itemPos - size() + item->size();
637             else if (item->endPosition() <= pos)
638                 pos = itemPos;
639             break;
640         case QQuickItemView::Contain:
641             if (item->endPosition() >= pos + size())
642                 pos = itemPos - size() + item->size();
643             if (itemPos < pos)
644                 pos = itemPos;
645         }
646         pos = qMin(pos, maxExtent);
647         qreal minExtent;
648         if (layoutOrientation() == Qt::Vertical)
649             minExtent = -q->minYExtent();
650         else
651             minExtent = isContentFlowReversed() ? q->maxXExtent()-size(): -q->minXExtent();
652         pos = qMax(pos, minExtent);
653         moveReason = QQuickItemViewPrivate::Other;
654         q->cancelFlick();
655         setPosition(pos);
656
657         if (highlight) {
658             if (autoHighlight)
659                 resetHighlightPosition();
660             updateHighlight();
661         }
662     }
663     fixupPosition();
664 }
665
666 void QQuickItemView::positionViewAtIndex(int index, int mode)
667 {
668     Q_D(QQuickItemView);
669     if (!d->isValid() || index < 0 || index >= d->model->count())
670         return;
671     d->positionViewAtIndex(index, mode);
672 }
673
674
675 void QQuickItemView::positionViewAtBeginning()
676 {
677     Q_D(QQuickItemView);
678     if (!d->isValid())
679         return;
680     d->positionViewAtIndex(-1, Beginning);
681 }
682
683 void QQuickItemView::positionViewAtEnd()
684 {
685     Q_D(QQuickItemView);
686     if (!d->isValid())
687         return;
688     d->positionViewAtIndex(d->model->count(), End);
689 }
690
691 int QQuickItemView::indexAt(qreal x, qreal y) const
692 {
693     Q_D(const QQuickItemView);
694     for (int i = 0; i < d->visibleItems.count(); ++i) {
695         const FxViewItem *item = d->visibleItems.at(i);
696         if (item->contains(x, y))
697             return item->index;
698     }
699
700     return -1;
701 }
702
703 void QQuickItemViewPrivate::applyPendingChanges()
704 {
705     Q_Q(QQuickItemView);
706     if (q->isComponentComplete() && currentChanges.hasPendingChanges())
707         layout();
708 }
709
710 // for debugging only
711 void QQuickItemViewPrivate::checkVisible() const
712 {
713     int skip = 0;
714     for (int i = 0; i < visibleItems.count(); ++i) {
715         FxViewItem *item = visibleItems.at(i);
716         if (item->index == -1) {
717             ++skip;
718         } else if (item->index != visibleIndex + i - skip) {
719             qFatal("index %d %d %d", visibleIndex, i, item->index);
720         }
721     }
722 }
723
724 void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
725 {
726     Q_Q(QQuickItemView);
727     QQuickFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
728     if (!q->isComponentComplete())
729         return;
730
731     if (header && header->item == item) {
732         updateHeader();
733         markExtentsDirty();
734         if (!q->isMoving() && !q->isFlicking())
735             fixupPosition();
736     } else if (footer && footer->item == item) {
737         updateFooter();
738         markExtentsDirty();
739         if (!q->isMoving() && !q->isFlicking())
740             fixupPosition();
741     }
742
743     if (currentItem && currentItem->item == item)
744         updateHighlight();
745     if (trackedItem && trackedItem->item == item)
746         q->trackedPositionChanged();
747 }
748
749 void QQuickItemView::destroyRemoved()
750 {
751     Q_D(QQuickItemView);
752     for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
753             it != d->visibleItems.end();) {
754         FxViewItem *item = *it;
755         if (item->index == -1 && item->attached->delayRemove() == false) {
756             d->releaseItem(item);
757             it = d->visibleItems.erase(it);
758         } else {
759             ++it;
760         }
761     }
762
763     // Correct the positioning of the items
764     d->updateSections();
765     d->forceLayout = true;
766     d->layout();
767 }
768
769 void QQuickItemView::modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset)
770 {
771     Q_D(QQuickItemView);
772     if (reset) {
773         d->moveReason = QQuickItemViewPrivate::SetIndex;
774         d->regenerate();
775         if (d->highlight && d->currentItem) {
776             if (d->autoHighlight)
777                 d->resetHighlightPosition();
778             d->updateTrackedItem();
779         }
780         d->moveReason = QQuickItemViewPrivate::Other;
781
782         emit countChanged();
783     } else {
784         d->currentChanges.prepare(d->currentIndex, d->itemCount);
785         d->currentChanges.applyChanges(changeSet);
786         polish();
787     }
788 }
789
790 void QQuickItemView::animStopped()
791 {
792     Q_D(QQuickItemView);
793     d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
794     d->refill();
795     if (d->haveHighlightRange && d->highlightRange == QQuickItemView::StrictlyEnforceRange)
796         d->updateHighlight();
797 }
798
799
800 void QQuickItemView::trackedPositionChanged()
801 {
802     Q_D(QQuickItemView);
803     if (!d->trackedItem || !d->currentItem)
804         return;
805     if (d->moveReason == QQuickItemViewPrivate::SetIndex) {
806         qreal trackedPos = d->trackedItem->position();
807         qreal trackedSize = d->trackedItem->size();
808         if (d->trackedItem != d->currentItem) {
809             trackedSize += d->currentItem->sectionSize();
810         }
811         qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
812         qreal pos = viewPos;
813         if (d->haveHighlightRange) {
814             if (trackedPos > pos + d->highlightRangeEnd - trackedSize)
815                 pos = trackedPos - d->highlightRangeEnd + trackedSize;
816             if (trackedPos < pos + d->highlightRangeStart)
817                 pos = trackedPos - d->highlightRangeStart;
818             if (d->highlightRange != StrictlyEnforceRange) {
819                 if (pos > d->endPosition() - d->size())
820                     pos = d->endPosition() - d->size();
821                 if (pos < d->startPosition())
822                     pos = d->startPosition();
823             }
824         } else {
825             qreal trackedEndPos = d->trackedItem->endPosition();
826             qreal toItemPos = d->currentItem->position();
827             qreal toItemEndPos = d->currentItem->endPosition();
828
829             if (d->header && d->showHeaderForIndex(d->currentIndex)) {
830                 trackedPos -= d->headerSize();
831                 trackedEndPos -= d->headerSize();
832                 toItemPos -= d->headerSize();
833                 toItemEndPos -= d->headerSize();
834             } else if (d->footer && d->showFooterForIndex(d->currentIndex)) {
835                 trackedPos += d->footerSize();
836                 trackedEndPos += d->footerSize();
837                 toItemPos += d->footerSize();
838                 toItemEndPos += d->footerSize();
839             }
840
841             if (trackedPos < viewPos && toItemPos < viewPos) {
842                 pos = qMax(trackedPos, toItemPos);
843             } else if (trackedEndPos >= viewPos + d->size()
844                 && toItemEndPos >= viewPos + d->size()) {
845                 if (trackedEndPos <= toItemEndPos) {
846                     pos = trackedEndPos - d->size();
847                     if (trackedSize > d->size())
848                         pos = trackedPos;
849                 } else {
850                     pos = toItemEndPos - d->size();
851                     if (d->currentItem->size() > d->size())
852                         pos = d->currentItem->position();
853                 }
854             }
855         }
856         if (viewPos != pos) {
857             cancelFlick();
858             d->calcVelocity = true;
859             d->setPosition(pos);
860             d->calcVelocity = false;
861         }
862     }
863 }
864
865 void QQuickItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
866 {
867     Q_D(QQuickItemView);
868     d->markExtentsDirty();
869     QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
870 }
871
872
873 qreal QQuickItemView::minYExtent() const
874 {
875     Q_D(const QQuickItemView);
876     if (d->layoutOrientation() == Qt::Horizontal)
877         return QQuickFlickable::minYExtent();
878
879     if (d->vData.minExtentDirty) {
880         d->minExtent = d->vData.startMargin-d->startPosition();
881         if (d->header)
882             d->minExtent += d->headerSize();
883         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
884             d->minExtent += d->highlightRangeStart;
885             if (d->visibleItem(0))
886                 d->minExtent -= d->visibleItem(0)->sectionSize();
887             d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd));
888         }
889         d->vData.minExtentDirty = false;
890     }
891
892     return d->minExtent;
893 }
894
895 qreal QQuickItemView::maxYExtent() const
896 {
897     Q_D(const QQuickItemView);
898     if (d->layoutOrientation() == Qt::Horizontal)
899         return height();
900
901     if (d->vData.maxExtentDirty) {
902         if (!d->model || !d->model->count()) {
903             d->maxExtent = d->header ? -d->headerSize() : 0;
904             d->maxExtent += height();
905         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
906             d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
907             if (d->highlightRangeEnd != d->highlightRangeStart)
908                 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd));
909         } else {
910             d->maxExtent = -(d->endPosition() - height());
911         }
912
913         if (d->footer)
914             d->maxExtent -= d->footerSize();
915         d->maxExtent -= d->vData.endMargin;
916         qreal minY = minYExtent();
917         if (d->maxExtent > minY)
918             d->maxExtent = minY;
919         d->vData.maxExtentDirty = false;
920     }
921     return d->maxExtent;
922 }
923
924 qreal QQuickItemView::minXExtent() const
925 {
926     Q_D(const QQuickItemView);
927     if (d->layoutOrientation() == Qt::Vertical)
928         return QQuickFlickable::minXExtent();
929
930     if (d->hData.minExtentDirty) {
931         d->minExtent = -d->startPosition();
932         qreal highlightStart;
933         qreal highlightEnd;
934         qreal endPositionFirstItem = 0;
935         if (d->isContentFlowReversed()) {
936             d->minExtent += d->hData.endMargin;
937             if (d->model && d->model->count())
938                 endPositionFirstItem = d->positionAt(d->model->count()-1);
939             else if (d->header)
940                 d->minExtent += d->headerSize();
941             highlightStart = d->highlightRangeEndValid ? d->size() - d->highlightRangeEnd : d->size();
942             highlightEnd = d->highlightRangeStartValid ? d->size() - d->highlightRangeStart : d->size();
943             if (d->footer)
944                 d->minExtent += d->footerSize();
945             qreal maxX = maxXExtent();
946             if (d->minExtent < maxX)
947                 d->minExtent = maxX;
948         } else {
949             d->minExtent += d->hData.startMargin;
950             endPositionFirstItem = d->endPositionAt(0);
951             highlightStart = d->highlightRangeStart;
952             highlightEnd = d->highlightRangeEnd;
953             if (d->header)
954                 d->minExtent += d->headerSize();
955         }
956         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
957             d->minExtent += highlightStart;
958             d->minExtent = d->isContentFlowReversed()
959                                 ? qMin(d->minExtent, endPositionFirstItem + highlightEnd)
960                                 : qMax(d->minExtent, -(endPositionFirstItem - highlightEnd));
961         }
962         d->hData.minExtentDirty = false;
963     }
964
965     return d->minExtent;
966 }
967
968 qreal QQuickItemView::maxXExtent() const
969 {
970     Q_D(const QQuickItemView);
971     if (d->layoutOrientation() == Qt::Vertical)
972         return width();
973
974     if (d->hData.maxExtentDirty) {
975         qreal highlightStart;
976         qreal highlightEnd;
977         qreal lastItemPosition = 0;
978         d->maxExtent = 0;
979         if (d->isContentFlowReversed()) {
980             highlightStart = d->highlightRangeEndValid ? d->size() - d->highlightRangeEnd : d->size();
981             highlightEnd = d->highlightRangeStartValid ? d->size() - d->highlightRangeStart : d->size();
982             lastItemPosition = d->endPosition();
983         } else {
984             highlightStart = d->highlightRangeStart;
985             highlightEnd = d->highlightRangeEnd;
986             if (d->model && d->model->count())
987                 lastItemPosition = d->positionAt(d->model->count()-1);
988         }
989         if (!d->model || !d->model->count()) {
990             if (!d->isContentFlowReversed())
991                 d->maxExtent = d->header ? -d->headerSize() : 0;
992             d->maxExtent += width();
993         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
994             d->maxExtent = -(lastItemPosition - highlightStart);
995             if (highlightEnd != highlightStart) {
996                 d->maxExtent = d->isContentFlowReversed()
997                         ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd))
998                         : qMin(d->maxExtent, -(d->endPosition() - highlightEnd));
999             }
1000         } else {
1001             d->maxExtent = -(d->endPosition() - width());
1002         }
1003         if (d->isContentFlowReversed()) {
1004             if (d->header)
1005                 d->maxExtent -= d->headerSize();
1006             d->maxExtent -= d->hData.startMargin;
1007         } else {
1008             if (d->footer)
1009                 d->maxExtent -= d->footerSize();
1010             d->maxExtent -= d->hData.endMargin;
1011             qreal minX = minXExtent();
1012             if (d->maxExtent > minX)
1013                 d->maxExtent = minX;
1014         }
1015         d->hData.maxExtentDirty = false;
1016     }
1017
1018     return d->maxExtent;
1019 }
1020
1021 void QQuickItemView::setContentX(qreal pos)
1022 {
1023     Q_D(QQuickItemView);
1024     // Positioning the view manually should override any current movement state
1025     d->moveReason = QQuickItemViewPrivate::Other;
1026     QQuickFlickable::setContentX(pos);
1027 }
1028
1029 void QQuickItemView::setContentY(qreal pos)
1030 {
1031     Q_D(QQuickItemView);
1032     // Positioning the view manually should override any current movement state
1033     d->moveReason = QQuickItemViewPrivate::Other;
1034     QQuickFlickable::setContentY(pos);
1035 }
1036
1037 qreal QQuickItemView::xOrigin() const
1038 {
1039     Q_D(const QQuickItemView);
1040     if (d->isContentFlowReversed())
1041         return -maxXExtent() + d->size() - d->hData.startMargin;
1042     else
1043         return -minXExtent() + d->hData.startMargin;
1044 }
1045
1046 void QQuickItemView::updatePolish()
1047 {
1048     Q_D(QQuickItemView);
1049     QQuickFlickable::updatePolish();
1050     d->layout();
1051 }
1052
1053 void QQuickItemView::componentComplete()
1054 {
1055     Q_D(QQuickItemView);
1056     if (d->model && d->ownModel)
1057         static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
1058
1059     QQuickFlickable::componentComplete();
1060
1061     updateSections();
1062     d->updateHeader();
1063     d->updateFooter();
1064     d->updateViewport();
1065     d->setPosition(d->contentStartPosition());
1066     if (d->isValid()) {
1067         d->refill();
1068         d->moveReason = QQuickItemViewPrivate::SetIndex;
1069         if (d->currentIndex < 0 && !d->currentIndexCleared)
1070             d->updateCurrent(0);
1071         else
1072             d->updateCurrent(d->currentIndex);
1073         if (d->highlight && d->currentItem) {
1074             if (d->autoHighlight)
1075                 d->resetHighlightPosition();
1076             d->updateTrackedItem();
1077         }
1078         d->moveReason = QQuickItemViewPrivate::Other;
1079         d->fixupPosition();
1080     }
1081     if (d->model && d->model->count())
1082         emit countChanged();
1083 }
1084
1085
1086
1087 QQuickItemViewPrivate::QQuickItemViewPrivate()
1088     : itemCount(0)
1089     , buffer(0), bufferMode(BufferBefore | BufferAfter)
1090     , layoutDirection(Qt::LeftToRight)
1091     , moveReason(Other)
1092     , visibleIndex(0)
1093     , currentIndex(-1), currentItem(0)
1094     , trackedItem(0), requestedIndex(-1), requestedItem(0)
1095     , highlightComponent(0), highlight(0)
1096     , highlightRange(QQuickItemView::NoHighlightRange)
1097     , highlightRangeStart(0), highlightRangeEnd(0)
1098     , highlightMoveDuration(150)
1099     , headerComponent(0), header(0), footerComponent(0), footer(0)
1100     , minExtent(0), maxExtent(0)
1101     , ownModel(false), wrap(false), deferredRelease(false)
1102     , inApplyModelChanges(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false)
1103     , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
1104     , fillCacheBuffer(false), inRequest(false), requestedAsync(false)
1105 {
1106 }
1107
1108 bool QQuickItemViewPrivate::isValid() const
1109 {
1110     return model && model->count() && model->isValid();
1111 }
1112
1113 qreal QQuickItemViewPrivate::position() const
1114 {
1115     Q_Q(const QQuickItemView);
1116     return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX();
1117 }
1118
1119 qreal QQuickItemViewPrivate::size() const
1120 {
1121     Q_Q(const QQuickItemView);
1122     return layoutOrientation() == Qt::Vertical ? q->height() : q->width();
1123 }
1124
1125 qreal QQuickItemViewPrivate::startPosition() const
1126 {
1127     return isContentFlowReversed() ? -lastPosition() : originPosition();
1128 }
1129
1130 qreal QQuickItemViewPrivate::endPosition() const
1131 {
1132     return isContentFlowReversed() ? -originPosition() : lastPosition();
1133 }
1134
1135 qreal QQuickItemViewPrivate::contentStartPosition() const
1136 {
1137     qreal pos = -headerSize();
1138     if (layoutOrientation() == Qt::Vertical)
1139         pos -= vData.startMargin;
1140     else if (isContentFlowReversed())
1141         pos -= hData.endMargin;
1142     else
1143         pos -= hData.startMargin;
1144
1145     return pos;
1146 }
1147
1148 int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const
1149 {
1150     if (visibleItems.count()) {
1151         int i = visibleItems.count() - 1;
1152         while (i > 0 && visibleItems.at(i)->index == -1)
1153             --i;
1154         if (visibleItems.at(i)->index != -1)
1155             return visibleItems.at(i)->index;
1156     }
1157     return defaultValue;
1158 }
1159
1160 FxViewItem *QQuickItemViewPrivate::visibleItem(int modelIndex) const {
1161     if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
1162         for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
1163             FxViewItem *item = visibleItems.at(i);
1164             if (item->index == modelIndex)
1165                 return item;
1166         }
1167     }
1168     return 0;
1169 }
1170
1171 FxViewItem *QQuickItemViewPrivate::firstVisibleItem() const {
1172     const qreal pos = isContentFlowReversed() ? -position()-size() : position();
1173     for (int i = 0; i < visibleItems.count(); ++i) {
1174         FxViewItem *item = visibleItems.at(i);
1175         if (item->index != -1 && item->endPosition() > pos)
1176             return item;
1177     }
1178     return visibleItems.count() ? visibleItems.first() : 0;
1179 }
1180
1181 // Map a model index to visibleItems list index.
1182 // These may differ if removed items are still present in the visible list,
1183 // e.g. doing a removal animation
1184 int QQuickItemViewPrivate::mapFromModel(int modelIndex) const
1185 {
1186     if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
1187         return -1;
1188     for (int i = 0; i < visibleItems.count(); ++i) {
1189         FxViewItem *item = visibleItems.at(i);
1190         if (item->index == modelIndex)
1191             return i;
1192         if (item->index > modelIndex)
1193             return -1;
1194     }
1195     return -1; // Not in visibleList
1196 }
1197
1198 void QQuickItemViewPrivate::init()
1199 {
1200     Q_Q(QQuickItemView);
1201     QQuickItemPrivate::get(contentItem)->childrenDoNotOverlap = true;
1202     q->setFlag(QQuickItem::ItemIsFocusScope);
1203     addItemChangeListener(this, Geometry);
1204     QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
1205     q->setFlickableDirection(QQuickFlickable::VerticalFlick);
1206 }
1207
1208 void QQuickItemViewPrivate::updateCurrent(int modelIndex)
1209 {
1210     Q_Q(QQuickItemView);
1211     applyPendingChanges();
1212
1213     if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1214         if (currentItem) {
1215             currentItem->attached->setIsCurrentItem(false);
1216             releaseItem(currentItem);
1217             currentItem = 0;
1218             currentIndex = modelIndex;
1219             emit q->currentIndexChanged();
1220             emit q->currentItemChanged();
1221             updateHighlight();
1222         } else if (currentIndex != modelIndex) {
1223             currentIndex = modelIndex;
1224             emit q->currentIndexChanged();
1225         }
1226         return;
1227     }
1228
1229     if (currentItem && currentIndex == modelIndex) {
1230         updateHighlight();
1231         return;
1232     }
1233
1234     FxViewItem *oldCurrentItem = currentItem;
1235     int oldCurrentIndex = currentIndex;
1236     currentIndex = modelIndex;
1237     currentItem = createItem(modelIndex, false);
1238     if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1239         oldCurrentItem->attached->setIsCurrentItem(false);
1240     if (currentItem) {
1241         currentItem->item->setFocus(true);
1242         currentItem->attached->setIsCurrentItem(true);
1243         initializeCurrentItem();
1244     }
1245
1246     updateHighlight();
1247     if (oldCurrentIndex != currentIndex)
1248         emit q->currentIndexChanged();
1249     if (oldCurrentItem != currentItem)
1250         emit q->currentItemChanged();
1251     releaseItem(oldCurrentItem);
1252 }
1253
1254 void QQuickItemViewPrivate::clear()
1255 {
1256     currentChanges.reset();
1257     timeline.clear();
1258
1259     for (int i = 0; i < visibleItems.count(); ++i)
1260         releaseItem(visibleItems.at(i));
1261     visibleItems.clear();
1262     visibleIndex = 0;
1263
1264     releaseItem(currentItem);
1265     currentItem = 0;
1266     createHighlight();
1267     trackedItem = 0;
1268
1269     markExtentsDirty();
1270     itemCount = 0;
1271 }
1272
1273
1274 void QQuickItemViewPrivate::mirrorChange()
1275 {
1276     Q_Q(QQuickItemView);
1277     regenerate();
1278     emit q->effectiveLayoutDirectionChanged();
1279 }
1280
1281 void QQuickItemViewPrivate::refill()
1282 {
1283     if (isContentFlowReversed())
1284         refill(-position()-size(), -position());
1285     else
1286         refill(position(), position()+size());
1287 }
1288
1289 void QQuickItemViewPrivate::refill(qreal from, qreal to, bool doBuffer)
1290 {
1291     Q_Q(QQuickItemView);
1292     if (!isValid() || !q->isComponentComplete())
1293         return;
1294
1295     currentChanges.reset();
1296
1297     int prevCount = itemCount;
1298     itemCount = model->count();
1299     qreal bufferFrom = from - buffer;
1300     qreal bufferTo = to + buffer;
1301     qreal fillFrom = from;
1302     qreal fillTo = to;
1303     if (doBuffer && (bufferMode & BufferAfter))
1304         fillTo = bufferTo;
1305     if (doBuffer && (bufferMode & BufferBefore))
1306         fillFrom = bufferFrom;
1307
1308     // Item creation and release is staggered in order to avoid
1309     // creating/releasing multiple items in one frame
1310     // while flicking (as much as possible).
1311
1312     bool changed = addVisibleItems(fillFrom, fillTo, doBuffer);
1313
1314     if (!changed || deferredRelease) { // avoid destroying items in the same frame that we create
1315         if (removeNonVisibleItems(bufferFrom, bufferTo))
1316             changed = true;
1317         deferredRelease = false;
1318     } else {
1319         deferredRelease = true;
1320     }
1321
1322     if (changed) {
1323         markExtentsDirty();
1324         visibleItemsChanged();
1325     } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
1326         refill(from, to, true);
1327     }
1328
1329     if (!q->isMoving() && changed) {
1330         fillCacheBuffer = true;
1331         q->polish();
1332     }
1333
1334     if (prevCount != itemCount)
1335         emit q->countChanged();
1336 }
1337
1338 void QQuickItemViewPrivate::regenerate()
1339 {
1340     Q_Q(QQuickItemView);
1341     if (q->isComponentComplete()) {
1342         currentChanges.reset();
1343         delete header;
1344         header = 0;
1345         delete footer;
1346         footer = 0;
1347         updateHeader();
1348         updateFooter();
1349         clear();
1350         updateViewport();
1351         setPosition(contentStartPosition());
1352         refill();
1353         updateCurrent(currentIndex);
1354     }
1355 }
1356
1357 void QQuickItemViewPrivate::updateViewport()
1358 {
1359     Q_Q(QQuickItemView);
1360     if (isValid()) {
1361         if (layoutOrientation() == Qt::Vertical)
1362             q->setContentHeight(endPosition() - startPosition());
1363         else
1364             q->setContentWidth(endPosition() - startPosition());
1365     }
1366 }
1367
1368 void QQuickItemViewPrivate::layout()
1369 {
1370     Q_Q(QQuickItemView);
1371     if (inApplyModelChanges)
1372         return;
1373
1374     if (!isValid() && !visibleItems.count()) {
1375         clear();
1376         setPosition(contentStartPosition());
1377         return;
1378     }
1379
1380     if (!applyModelChanges() && !forceLayout) {
1381         if (fillCacheBuffer)
1382             refill();
1383         return;
1384     }
1385     forceLayout = false;
1386
1387     layoutVisibleItems();
1388     refill();
1389
1390     markExtentsDirty();
1391
1392     updateHighlight();
1393
1394     if (!q->isMoving() && !q->isFlicking()) {
1395         fixupPosition();
1396         refill();
1397     }
1398
1399     updateHeader();
1400     updateFooter();
1401     updateViewport();
1402     updateUnrequestedPositions();
1403 }
1404
1405 bool QQuickItemViewPrivate::applyModelChanges()
1406 {
1407     Q_Q(QQuickItemView);
1408     if (!q->isComponentComplete() || !currentChanges.hasPendingChanges() || inApplyModelChanges)
1409         return false;
1410
1411     inApplyModelChanges = true;
1412
1413     updateUnrequestedIndexes();
1414     moveReason = QQuickItemViewPrivate::Other;
1415
1416     int prevCount = itemCount;
1417     bool visibleAffected = false;
1418     bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty()
1419             || !currentChanges.pendingChanges.inserts().isEmpty();
1420
1421     FxViewItem *firstVisible = firstVisibleItem();
1422     FxViewItem *origVisibleItemsFirst = visibleItems.count() ? visibleItems.first() : 0;
1423     int firstItemIndex = firstVisible ? firstVisible->index : -1;
1424     qreal removedBeforeFirstVisibleBy = 0;
1425
1426     const QVector<QDeclarativeChangeSet::Remove> &removals = currentChanges.pendingChanges.removes();
1427     for (int i=0; i<removals.count(); i++) {
1428         itemCount -= removals[i].count;
1429
1430         // Remove the items from the visible list, skipping anything already marked for removal
1431         QList<FxViewItem*>::Iterator it = visibleItems.begin();
1432         while (it != visibleItems.end()) {
1433             FxViewItem *item = *it;
1434             if (item->index == -1 || item->index < removals[i].index) {
1435                 // already removed, or before removed items
1436                 if (!visibleAffected && item->index < removals[i].index)
1437                     visibleAffected = true;
1438                 ++it;
1439             } else if (item->index >= removals[i].index + removals[i].count) {
1440                 // after removed items
1441                 item->index -= removals[i].count;
1442                 ++it;
1443             } else {
1444                 // removed item
1445                 visibleAffected = true;
1446                 if (!removals[i].isMove())
1447                     item->attached->emitRemove();
1448
1449                 if (item->attached->delayRemove() && !removals[i].isMove()) {
1450                     item->index = -1;
1451                     QObject::connect(item->attached, SIGNAL(delayRemoveChanged()), q, SLOT(destroyRemoved()), Qt::QueuedConnection);
1452                     ++it;
1453                 } else {
1454                     if (firstVisible && item->position() < firstVisible->position() && item != visibleItems.first())
1455                         removedBeforeFirstVisibleBy += item->size();
1456                     if (removals[i].isMove()) {
1457                         currentChanges.removedItems.insert(removals[i].moveKey(item->index), item);
1458                     } else {
1459                         if (item == firstVisible)
1460                             firstVisible = 0;
1461                         currentChanges.removedItems.insertMulti(QDeclarativeChangeSet::MoveKey(), item);
1462                     }
1463                     it = visibleItems.erase(it);
1464                 }
1465             }
1466         }
1467         if (!visibleAffected && needsRefillForAddedOrRemovedIndex(removals[i].index))
1468             visibleAffected = true;
1469     }
1470     if (!removals.isEmpty())
1471         updateVisibleIndex();
1472
1473     const QVector<QDeclarativeChangeSet::Insert> &insertions = currentChanges.pendingChanges.inserts();
1474     InsertionsResult insertResult;
1475     bool allInsertionsBeforeVisible = true;
1476
1477     for (int i=0; i<insertions.count(); i++) {
1478         bool wasEmpty = visibleItems.isEmpty();
1479         if (applyInsertionChange(insertions[i], firstVisible, &insertResult))
1480             visibleAffected = true;
1481         if (!visibleAffected && needsRefillForAddedOrRemovedIndex(insertions[i].index))
1482             visibleAffected = true;
1483         if (insertions[i].index >= visibleIndex)
1484             allInsertionsBeforeVisible = false;
1485         if (wasEmpty && !visibleItems.isEmpty())
1486             resetFirstItemPosition();
1487         itemCount += insertions[i].count;
1488     }
1489     for (int i=0; i<insertResult.addedItems.count(); ++i)
1490         insertResult.addedItems.at(i)->attached->emitAdd();
1491
1492     // if the first visible item has moved, ensure another one takes its place
1493     // so that we avoid shifting all content forwards
1494     // (if an item is removed from before the first visible, the first visible should not move upwards)
1495     bool movedBackToFirstVisible = false;
1496     if (firstVisible && firstItemIndex >= 0) {
1497         for (int i=0; i<insertResult.movedBackwards.count(); i++) {
1498             if (insertResult.movedBackwards[i]->index == firstItemIndex) {
1499                 // an item has moved backwards up to the first visible's position
1500                 resetItemPosition(insertResult.movedBackwards[i], firstVisible);
1501                 insertResult.movedBackwards.removeAt(i);
1502                 movedBackToFirstVisible = true;
1503                 break;
1504             }
1505         }
1506         if (!movedBackToFirstVisible && !allInsertionsBeforeVisible) {
1507             // first visible item has moved forward, another visible item takes its place
1508             FxViewItem *item = visibleItem(firstItemIndex);
1509             if (item)
1510                 resetItemPosition(item, firstVisible);
1511         }
1512     }
1513
1514     // Ensure we don't cause an ugly list scroll
1515     if (firstVisible && visibleItems.count() && visibleItems.first() != firstVisible) {
1516         // ensure first item is placed at correct postion if moving backward
1517         // since it will be used to position all subsequent items
1518         if (insertResult.movedBackwards.count() && origVisibleItemsFirst)
1519             resetItemPosition(visibleItems.first(), origVisibleItemsFirst);
1520
1521         // correct the first item position (unless it has already been fixed)
1522         if (!movedBackToFirstVisible) {
1523             qreal moveBackwardsBy = insertResult.sizeAddedBeforeVisible;
1524             for (int i=0; i<insertResult.movedBackwards.count(); i++)
1525                 moveBackwardsBy += insertResult.movedBackwards[i]->size();
1526             moveItemBy(visibleItems.first(), removedBeforeFirstVisibleBy, moveBackwardsBy);
1527         }
1528     }
1529
1530     // Whatever removed/moved items remain are no longer visible items.
1531     for (QHash<QDeclarativeChangeSet::MoveKey, FxViewItem *>::Iterator it = currentChanges.removedItems.begin();
1532          it != currentChanges.removedItems.end(); ++it) {
1533         releaseItem(it.value());
1534     }
1535     currentChanges.removedItems.clear();
1536
1537     if (currentChanges.currentChanged) {
1538         if (currentChanges.currentRemoved && currentItem) {
1539             currentItem->attached->setIsCurrentItem(false);
1540             releaseItem(currentItem);
1541             currentItem = 0;
1542         }
1543         if (!currentIndexCleared)
1544             updateCurrent(currentChanges.newCurrentIndex);
1545     }
1546     currentChanges.reset();
1547
1548     updateSections();
1549     if (prevCount != itemCount)
1550         emit q->countChanged();
1551
1552     if (!visibleAffected)
1553         visibleAffected = !currentChanges.pendingChanges.changes().isEmpty();
1554     if (!visibleAffected && viewportChanged)
1555         updateViewport();
1556
1557     inApplyModelChanges = false;
1558     return visibleAffected;
1559 }
1560
1561 /*
1562   This may return 0 if the item is being created asynchronously.
1563   When the item becomes available, refill() will be called and the item
1564   will be returned on the next call to createItem().
1565 */
1566 FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous)
1567 {
1568     Q_Q(QQuickItemView);
1569     if (requestedIndex == modelIndex && (asynchronous || requestedAsync == asynchronous))
1570         return 0;
1571
1572     if (requestedIndex != -1 && requestedIndex != modelIndex) {
1573         delete requestedItem;
1574         requestedItem = 0;
1575     }
1576
1577     requestedIndex = modelIndex;
1578     requestedAsync = asynchronous;
1579     inRequest = true;
1580
1581     if (QQuickItem *item = model->item(modelIndex, asynchronous)) {
1582         item->setParentItem(q->contentItem());
1583         QDeclarative_setParent_noEvent(item, q->contentItem());
1584         requestedIndex = -1;
1585         fillCacheBuffer = false;
1586         FxViewItem *viewItem = requestedItem;
1587         if (!viewItem)
1588             viewItem = newViewItem(modelIndex, item); // already in cache, so viewItem not initialized in initItem()
1589         if (viewItem) {
1590             viewItem->index = modelIndex;
1591             // do other set up for the new item that should not happen
1592             // until after bindings are evaluated
1593             initializeViewItem(viewItem);
1594             unrequestedItems.remove(item);
1595         }
1596         requestedItem = 0;
1597         inRequest = false;
1598         return viewItem;
1599     }
1600
1601     inRequest = false;
1602     return 0;
1603 }
1604
1605 void QQuickItemView::createdItem(int index, QQuickItem *item)
1606 {
1607     Q_D(QQuickItemView);
1608     if (d->requestedIndex != index) {
1609         item->setParentItem(contentItem());
1610         d->unrequestedItems.insert(item, index);
1611         item->setVisible(false);
1612         d->repositionPackageItemAt(item, index);
1613     } else {
1614         d->requestedIndex = -1;
1615         if (!d->inRequest) {
1616             if (index == d->currentIndex)
1617                 d->updateCurrent(index);
1618             d->refill();
1619         } else {
1620             d->fillCacheBuffer = true;
1621             polish();
1622         }
1623     }
1624 }
1625
1626 void QQuickItemView::initItem(int index, QQuickItem *item)
1627 {
1628     Q_D(QQuickItemView);
1629     item->setZ(1);
1630     if (d->requestedIndex == index) {
1631         item->setParentItem(contentItem());
1632         QDeclarative_setParent_noEvent(item, contentItem());
1633         d->requestedItem = d->newViewItem(index, item);
1634     }
1635 }
1636
1637 void QQuickItemView::destroyingItem(QQuickItem *item)
1638 {
1639     Q_D(QQuickItemView);
1640     d->unrequestedItems.remove(item);
1641 }
1642
1643 void QQuickItemViewPrivate::releaseItem(FxViewItem *item)
1644 {
1645     Q_Q(QQuickItemView);
1646     if (!item || !model)
1647         return;
1648     if (trackedItem == item)
1649         trackedItem = 0;
1650     QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
1651     itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
1652     if (model->release(item->item) == 0) {
1653         // item was not destroyed, and we no longer reference it.
1654         item->item->setVisible(false);
1655         unrequestedItems.insert(item->item, model->indexOf(item->item, q));
1656     }
1657     delete item;
1658 }
1659
1660 QQuickItem *QQuickItemViewPrivate::createHighlightItem()
1661 {
1662     return createComponentItem(highlightComponent, true, true);
1663 }
1664
1665 QQuickItem *QQuickItemViewPrivate::createComponentItem(QDeclarativeComponent *component, bool receiveItemGeometryChanges, bool createDefault)
1666 {
1667     Q_Q(QQuickItemView);
1668
1669     QQuickItem *item = 0;
1670     if (component) {
1671         QDeclarativeContext *creationContext = component->creationContext();
1672         QDeclarativeContext *context = new QDeclarativeContext(
1673                 creationContext ? creationContext : qmlContext(q));
1674         QObject *nobj = component->create(context);
1675         if (nobj) {
1676             QDeclarative_setParent_noEvent(context, nobj);
1677             item = qobject_cast<QQuickItem *>(nobj);
1678             if (!item)
1679                 delete nobj;
1680         } else {
1681             delete context;
1682         }
1683     } else if (createDefault) {
1684         item = new QQuickItem;
1685     }
1686     if (item) {
1687         QDeclarative_setParent_noEvent(item, q->contentItem());
1688         item->setParentItem(q->contentItem());
1689         if (receiveItemGeometryChanges) {
1690             QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1691             itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry);
1692         }
1693     }
1694     return item;
1695 }
1696
1697 void QQuickItemViewPrivate::updateTrackedItem()
1698 {
1699     Q_Q(QQuickItemView);
1700     FxViewItem *item = currentItem;
1701     if (highlight)
1702         item = highlight;
1703     trackedItem = item;
1704
1705     if (trackedItem)
1706         q->trackedPositionChanged();
1707 }
1708
1709 void QQuickItemViewPrivate::updateUnrequestedIndexes()
1710 {
1711     Q_Q(QQuickItemView);
1712     for (QHash<QQuickItem*,int>::iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
1713         *it = model->indexOf(it.key(), q);
1714 }
1715
1716 void QQuickItemViewPrivate::updateUnrequestedPositions()
1717 {
1718     for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
1719         repositionPackageItemAt(it.key(), it.value());
1720 }
1721
1722 void QQuickItemViewPrivate::updateVisibleIndex()
1723 {
1724     visibleIndex = 0;
1725     for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end(); ++it) {
1726         if ((*it)->index != -1) {
1727             visibleIndex = (*it)->index;
1728             break;
1729         }
1730     }
1731 }
1732
1733 QT_END_NAMESPACE