Update to 5.0.0-beta1
[profile/ivi/qtdeclarative.git] / src / quick / items / qquickitemview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qquickitemview_p_p.h"
43 #include <QtQuick/private/qquicktransition_p.h>
44 #include "qplatformdefs.h"
45
46 QT_BEGIN_NAMESPACE
47
48 // Default cacheBuffer for all views.
49 #ifndef QML_VIEW_DEFAULTCACHEBUFFER
50 #define QML_VIEW_DEFAULTCACHEBUFFER 320
51 #endif
52
53 FxViewItem::FxViewItem(QQuickItem *i, bool own, bool trackGeometry)
54     : item(i)
55     , transitionableItem(0)
56     , attached(0)
57     , ownItem(own)
58     , releaseAfterTransition(false)
59     , trackGeom(trackGeometry)
60 {
61 }
62
63 FxViewItem::~FxViewItem()
64 {
65     delete transitionableItem;
66     if (ownItem && item) {
67         item->setParentItem(0);
68         item->deleteLater();
69         item = 0;
70     }
71 }
72
73 qreal FxViewItem::itemX() const
74 {
75     return transitionableItem ? transitionableItem->itemX() : item->x();
76 }
77
78 qreal FxViewItem::itemY() const
79 {
80     return transitionableItem ? transitionableItem->itemY() : item->y();
81 }
82
83 void FxViewItem::moveTo(const QPointF &pos, bool immediate)
84 {
85     if (transitionableItem)
86         transitionableItem->moveTo(pos, immediate);
87     else
88         item->setPos(pos);
89 }
90
91 void FxViewItem::setVisible(bool visible)
92 {
93     if (!visible && transitionableItem && transitionableItem->transitionScheduledOrRunning())
94         return;
95     QQuickItemPrivate::get(item)->setCulled(!visible);
96 }
97
98 QQuickItemViewTransitioner::TransitionType FxViewItem::scheduledTransitionType() const
99 {
100     return transitionableItem ? transitionableItem->nextTransitionType : QQuickItemViewTransitioner::NoTransition;
101 }
102
103 bool FxViewItem::transitionScheduledOrRunning() const
104 {
105     return transitionableItem ? transitionableItem->transitionScheduledOrRunning() : false;
106 }
107
108 bool FxViewItem::transitionRunning() const
109 {
110     return transitionableItem ? transitionableItem->transitionRunning() : false;
111 }
112
113 bool FxViewItem::isPendingRemoval() const
114 {
115     return transitionableItem ? transitionableItem->isPendingRemoval() : false;
116 }
117
118 void FxViewItem::transitionNextReposition(QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, bool asTarget)
119 {
120     if (!transitioner)
121         return;
122     if (!transitionableItem)
123         transitionableItem = new QQuickItemViewTransitionableItem(item);
124     transitioner->transitionNextReposition(transitionableItem, type, asTarget);
125 }
126
127 bool FxViewItem::prepareTransition(QQuickItemViewTransitioner *transitioner, const QRectF &viewBounds)
128 {
129     return transitionableItem ? transitionableItem->prepareTransition(transitioner, index, viewBounds) : false;
130 }
131
132 void FxViewItem::startTransition(QQuickItemViewTransitioner *transitioner)
133 {
134     if (transitionableItem)
135         transitionableItem->startTransition(transitioner, index);
136 }
137
138
139 QQuickItemViewChangeSet::QQuickItemViewChangeSet()
140     : active(false)
141 {
142     reset();
143 }
144
145 bool QQuickItemViewChangeSet::hasPendingChanges() const
146 {
147     return !pendingChanges.isEmpty();
148 }
149
150 void QQuickItemViewChangeSet::applyChanges(const QQuickChangeSet &changeSet)
151 {
152     pendingChanges.apply(changeSet);
153
154     int moveId = -1;
155     int moveOffset = 0;
156
157     foreach (const QQuickChangeSet::Remove &r, changeSet.removes()) {
158         itemCount -= r.count;
159         if (moveId == -1 && newCurrentIndex >= r.index + r.count) {
160             newCurrentIndex -= r.count;
161             currentChanged = true;
162         } else if (moveId == -1 && newCurrentIndex >= r.index && newCurrentIndex < r.index + r.count) {
163             // current item has been removed.
164             if (r.isMove()) {
165                 moveId = r.moveId;
166                 moveOffset = newCurrentIndex - r.index;
167             } else {
168                 currentRemoved = true;
169                 newCurrentIndex = -1;
170                 if (itemCount)
171                     newCurrentIndex = qMin(r.index, itemCount - 1);
172             }
173             currentChanged = true;
174         }
175     }
176     foreach (const QQuickChangeSet::Insert &i, changeSet.inserts()) {
177         if (moveId == -1) {
178             if (itemCount && newCurrentIndex >= i.index) {
179                 newCurrentIndex += i.count;
180                 currentChanged = true;
181             } else if (newCurrentIndex < 0) {
182                 newCurrentIndex = 0;
183                 currentChanged = true;
184             } else if (newCurrentIndex == 0 && !itemCount) {
185                 // this is the first item, set the initial current index
186                 currentChanged = true;
187             }
188         } else if (moveId == i.moveId) {
189             newCurrentIndex = i.index + moveOffset;
190         }
191         itemCount += i.count;
192     }
193 }
194
195 void QQuickItemViewChangeSet::applyBufferedChanges(const QQuickItemViewChangeSet &other)
196 {
197     if (!other.hasPendingChanges())
198         return;
199
200     pendingChanges.apply(other.pendingChanges);
201     itemCount = other.itemCount;
202     newCurrentIndex = other.newCurrentIndex;
203     currentChanged = other.currentChanged;
204     currentRemoved = other.currentRemoved;
205 }
206
207 void QQuickItemViewChangeSet::prepare(int currentIndex, int count)
208 {
209     if (active)
210         return;
211     reset();
212     active = true;
213     itemCount = count;
214     newCurrentIndex = currentIndex;
215 }
216
217 void QQuickItemViewChangeSet::reset()
218 {
219     itemCount = 0;
220     newCurrentIndex = -1;
221     pendingChanges.clear();
222     removedItems.clear();
223     active = false;
224     currentChanged = false;
225     currentRemoved = false;
226 }
227
228 //-----------------------------------
229
230 QQuickItemView::QQuickItemView(QQuickFlickablePrivate &dd, QQuickItem *parent)
231     : QQuickFlickable(dd, parent)
232 {
233     Q_D(QQuickItemView);
234     d->init();
235 }
236
237 QQuickItemView::~QQuickItemView()
238 {
239     Q_D(QQuickItemView);
240     d->clear();
241     if (d->ownModel)
242         delete d->model;
243     delete d->header;
244     delete d->footer;
245 }
246
247
248 QQuickItem *QQuickItemView::currentItem() const
249 {
250     Q_D(const QQuickItemView);
251     if (!d->currentItem)
252         return 0;
253     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
254     return d->currentItem->item;
255 }
256
257 QVariant QQuickItemView::model() const
258 {
259     Q_D(const QQuickItemView);
260     return d->modelVariant;
261 }
262
263 void QQuickItemView::setModel(const QVariant &model)
264 {
265     Q_D(QQuickItemView);
266     if (d->modelVariant == model)
267         return;
268     if (d->model) {
269         disconnect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
270                 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
271         disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
272         disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
273         disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
274     }
275
276     QQuickVisualModel *oldModel = d->model;
277
278     d->clear();
279     d->model = 0;
280     d->setPosition(d->contentStartOffset());
281     d->modelVariant = model;
282
283     QObject *object = qvariant_cast<QObject*>(model);
284     QQuickVisualModel *vim = 0;
285     if (object && (vim = qobject_cast<QQuickVisualModel *>(object))) {
286         if (d->ownModel) {
287             delete oldModel;
288             d->ownModel = false;
289         }
290         d->model = vim;
291     } else {
292         if (!d->ownModel) {
293             d->model = new QQuickVisualDataModel(qmlContext(this), this);
294             d->ownModel = true;
295             if (isComponentComplete())
296                 static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
297         } else {
298             d->model = oldModel;
299         }
300         if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
301             dataModel->setModel(model);
302     }
303
304     if (d->model) {
305         d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
306         connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*)));
307         connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*)));
308         connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*)));
309         if (isComponentComplete()) {
310             d->updateSectionCriteria();
311             d->refill();
312             d->currentIndex = -1;
313             setCurrentIndex(0);
314             d->updateViewport();
315
316             if (d->transitioner && d->transitioner->populateTransition) {
317                 d->transitioner->setPopulateTransitionEnabled(true);
318                 d->forceLayoutPolish();
319             }
320         }
321
322         connect(d->model, SIGNAL(modelUpdated(QQuickChangeSet,bool)),
323                 this, SLOT(modelUpdated(QQuickChangeSet,bool)));
324         emit countChanged();
325     }
326     emit modelChanged();
327 }
328
329 QQmlComponent *QQuickItemView::delegate() const
330 {
331     Q_D(const QQuickItemView);
332     if (d->model) {
333         if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model))
334             return dataModel->delegate();
335     }
336
337     return 0;
338 }
339
340 void QQuickItemView::setDelegate(QQmlComponent *delegate)
341 {
342     Q_D(QQuickItemView);
343     if (delegate == this->delegate())
344         return;
345     if (!d->ownModel) {
346         d->model = new QQuickVisualDataModel(qmlContext(this));
347         d->ownModel = true;
348     }
349     if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) {
350         int oldCount = dataModel->count();
351         dataModel->setDelegate(delegate);
352         if (isComponentComplete()) {
353             for (int i = 0; i < d->visibleItems.count(); ++i)
354                 d->releaseItem(d->visibleItems.at(i));
355             d->visibleItems.clear();
356             d->releaseItem(d->currentItem);
357             d->currentItem = 0;
358             d->updateSectionCriteria();
359             d->refill();
360             d->moveReason = QQuickItemViewPrivate::SetIndex;
361             d->updateCurrent(d->currentIndex);
362             if (d->highlight && d->currentItem) {
363                 if (d->autoHighlight)
364                     d->resetHighlightPosition();
365                 d->updateTrackedItem();
366             }
367             d->moveReason = QQuickItemViewPrivate::Other;
368             d->updateViewport();
369         }
370         if (oldCount != dataModel->count())
371             emit countChanged();
372     }
373     emit delegateChanged();
374 }
375
376
377 int QQuickItemView::count() const
378 {
379     Q_D(const QQuickItemView);
380     if (!d->model)
381         return 0;
382     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
383     return d->model->count();
384 }
385
386 int QQuickItemView::currentIndex() const
387 {
388     Q_D(const QQuickItemView);
389     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
390     return d->currentIndex;
391 }
392
393 void QQuickItemView::setCurrentIndex(int index)
394 {
395     Q_D(QQuickItemView);
396     if (d->inRequest)  // currently creating item
397         return;
398     d->currentIndexCleared = (index == -1);
399
400     d->applyPendingChanges();
401     if (index == d->currentIndex)
402         return;
403     if (isComponentComplete() && d->isValid()) {
404         d->moveReason = QQuickItemViewPrivate::SetIndex;
405         d->updateCurrent(index);
406     } else if (d->currentIndex != index) {
407         d->currentIndex = index;
408         emit currentIndexChanged();
409     }
410 }
411
412
413 bool QQuickItemView::isWrapEnabled() const
414 {
415     Q_D(const QQuickItemView);
416     return d->wrap;
417 }
418
419 void QQuickItemView::setWrapEnabled(bool wrap)
420 {
421     Q_D(QQuickItemView);
422     if (d->wrap == wrap)
423         return;
424     d->wrap = wrap;
425     emit keyNavigationWrapsChanged();
426 }
427
428 int QQuickItemView::cacheBuffer() const
429 {
430     Q_D(const QQuickItemView);
431     return d->buffer;
432 }
433
434 void QQuickItemView::setCacheBuffer(int b)
435 {
436     Q_D(QQuickItemView);
437     if (d->buffer != b) {
438         d->buffer = b;
439         if (isComponentComplete()) {
440             d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
441             d->refillOrLayout();
442         }
443         emit cacheBufferChanged();
444     }
445 }
446
447
448 Qt::LayoutDirection QQuickItemView::layoutDirection() const
449 {
450     Q_D(const QQuickItemView);
451     return d->layoutDirection;
452 }
453
454 void QQuickItemView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
455 {
456     Q_D(QQuickItemView);
457     if (d->layoutDirection != layoutDirection) {
458         d->layoutDirection = layoutDirection;
459         d->regenerate();
460         emit layoutDirectionChanged();
461         emit effectiveLayoutDirectionChanged();
462     }
463 }
464
465 Qt::LayoutDirection QQuickItemView::effectiveLayoutDirection() const
466 {
467     Q_D(const QQuickItemView);
468     if (d->effectiveLayoutMirror)
469         return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
470     else
471         return d->layoutDirection;
472 }
473
474 QQuickItemView::VerticalLayoutDirection QQuickItemView::verticalLayoutDirection() const
475 {
476     Q_D(const QQuickItemView);
477     return d->verticalLayoutDirection;
478 }
479
480 void QQuickItemView::setVerticalLayoutDirection(VerticalLayoutDirection layoutDirection)
481 {
482     Q_D(QQuickItemView);
483     if (d->verticalLayoutDirection != layoutDirection) {
484         d->verticalLayoutDirection = layoutDirection;
485         d->regenerate();
486         emit verticalLayoutDirectionChanged();
487     }
488 }
489
490 QQmlComponent *QQuickItemView::header() const
491 {
492     Q_D(const QQuickItemView);
493     return d->headerComponent;
494 }
495
496 QQuickItem *QQuickItemView::headerItem() const
497 {
498     Q_D(const QQuickItemView);
499     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
500     return d->header ? d->header->item : 0;
501 }
502
503 void QQuickItemView::setHeader(QQmlComponent *headerComponent)
504 {
505     Q_D(QQuickItemView);
506     if (d->headerComponent != headerComponent) {
507         d->applyPendingChanges();
508         delete d->header;
509         d->header = 0;
510         d->headerComponent = headerComponent;
511
512         d->markExtentsDirty();
513
514         if (isComponentComplete()) {
515             d->updateHeader();
516             d->updateFooter();
517             d->updateViewport();
518             d->fixupPosition();
519         } else {
520             emit headerItemChanged();
521         }
522         emit headerChanged();
523     }
524 }
525
526 QQmlComponent *QQuickItemView::footer() const
527 {
528     Q_D(const QQuickItemView);
529     return d->footerComponent;
530 }
531
532 QQuickItem *QQuickItemView::footerItem() const
533 {
534     Q_D(const QQuickItemView);
535     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
536     return d->footer ? d->footer->item : 0;
537 }
538
539 void QQuickItemView::setFooter(QQmlComponent *footerComponent)
540 {
541     Q_D(QQuickItemView);
542     if (d->footerComponent != footerComponent) {
543         d->applyPendingChanges();
544         delete d->footer;
545         d->footer = 0;
546         d->footerComponent = footerComponent;
547
548         if (isComponentComplete()) {
549             d->updateFooter();
550             d->updateViewport();
551             d->fixupPosition();
552         } else {
553             emit footerItemChanged();
554         }
555         emit footerChanged();
556     }
557 }
558
559 QQmlComponent *QQuickItemView::highlight() const
560 {
561     Q_D(const QQuickItemView);
562     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
563     return d->highlightComponent;
564 }
565
566 void QQuickItemView::setHighlight(QQmlComponent *highlightComponent)
567 {
568     Q_D(QQuickItemView);
569     if (highlightComponent != d->highlightComponent) {
570         d->applyPendingChanges();
571         d->highlightComponent = highlightComponent;
572         d->createHighlight();
573         if (d->currentItem)
574             d->updateHighlight();
575         emit highlightChanged();
576     }
577 }
578
579 QQuickItem *QQuickItemView::highlightItem() const
580 {
581     Q_D(const QQuickItemView);
582     const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges();
583     return d->highlight ? d->highlight->item : 0;
584 }
585
586 bool QQuickItemView::highlightFollowsCurrentItem() const
587 {
588     Q_D(const QQuickItemView);
589     return d->autoHighlight;
590 }
591
592 void QQuickItemView::setHighlightFollowsCurrentItem(bool autoHighlight)
593 {
594     Q_D(QQuickItemView);
595     if (d->autoHighlight != autoHighlight) {
596         d->autoHighlight = autoHighlight;
597         if (autoHighlight)
598             d->updateHighlight();
599         emit highlightFollowsCurrentItemChanged();
600     }
601 }
602
603 QQuickItemView::HighlightRangeMode QQuickItemView::highlightRangeMode() const
604 {
605     Q_D(const QQuickItemView);
606     return static_cast<QQuickItemView::HighlightRangeMode>(d->highlightRange);
607 }
608
609 void QQuickItemView::setHighlightRangeMode(HighlightRangeMode mode)
610 {
611     Q_D(QQuickItemView);
612     if (d->highlightRange == mode)
613         return;
614     d->highlightRange = mode;
615     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
616     if (isComponentComplete()) {
617         d->updateViewport();
618         d->fixupPosition();
619     }
620     emit highlightRangeModeChanged();
621 }
622
623 //###Possibly rename these properties, since they are very useful even without a highlight?
624 qreal QQuickItemView::preferredHighlightBegin() const
625 {
626     Q_D(const QQuickItemView);
627     return d->highlightRangeStart;
628 }
629
630 void QQuickItemView::setPreferredHighlightBegin(qreal start)
631 {
632     Q_D(QQuickItemView);
633     d->highlightRangeStartValid = true;
634     if (d->highlightRangeStart == start)
635         return;
636     d->highlightRangeStart = start;
637     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
638     if (isComponentComplete()) {
639         d->updateViewport();
640         d->fixupPosition();
641     }
642     emit preferredHighlightBeginChanged();
643 }
644
645 void QQuickItemView::resetPreferredHighlightBegin()
646 {
647     Q_D(QQuickItemView);
648     d->highlightRangeStartValid = false;
649     if (d->highlightRangeStart == 0)
650         return;
651     d->highlightRangeStart = 0;
652     if (isComponentComplete()) {
653         d->updateViewport();
654         d->fixupPosition();
655     }
656     emit preferredHighlightBeginChanged();
657 }
658
659 qreal QQuickItemView::preferredHighlightEnd() const
660 {
661     Q_D(const QQuickItemView);
662     return d->highlightRangeEnd;
663 }
664
665 void QQuickItemView::setPreferredHighlightEnd(qreal end)
666 {
667     Q_D(QQuickItemView);
668     d->highlightRangeEndValid = true;
669     if (d->highlightRangeEnd == end)
670         return;
671     d->highlightRangeEnd = end;
672     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
673     if (isComponentComplete()) {
674         d->updateViewport();
675         d->fixupPosition();
676     }
677     emit preferredHighlightEndChanged();
678 }
679
680 void QQuickItemView::resetPreferredHighlightEnd()
681 {
682     Q_D(QQuickItemView);
683     d->highlightRangeEndValid = false;
684     if (d->highlightRangeEnd == 0)
685         return;
686     d->highlightRangeEnd = 0;
687     if (isComponentComplete()) {
688         d->updateViewport();
689         d->fixupPosition();
690     }
691     emit preferredHighlightEndChanged();
692 }
693
694 int QQuickItemView::highlightMoveDuration() const
695 {
696     Q_D(const QQuickItemView);
697     return d->highlightMoveDuration;
698 }
699
700 void QQuickItemView::setHighlightMoveDuration(int duration)
701 {
702     Q_D(QQuickItemView);
703     if (d->highlightMoveDuration != duration) {
704         d->highlightMoveDuration = duration;
705         emit highlightMoveDurationChanged();
706     }
707 }
708
709 QQuickTransition *QQuickItemView::populateTransition() const
710 {
711     Q_D(const QQuickItemView);
712     return d->transitioner ? d->transitioner->populateTransition : 0;
713 }
714
715 void QQuickItemView::setPopulateTransition(QQuickTransition *transition)
716 {
717     Q_D(QQuickItemView);
718     d->createTransitioner();
719     if (d->transitioner->populateTransition != transition) {
720         d->transitioner->populateTransition = transition;
721         emit populateTransitionChanged();
722     }
723 }
724
725 QQuickTransition *QQuickItemView::addTransition() const
726 {
727     Q_D(const QQuickItemView);
728     return d->transitioner ? d->transitioner->addTransition : 0;
729 }
730
731 void QQuickItemView::setAddTransition(QQuickTransition *transition)
732 {
733     Q_D(QQuickItemView);
734     d->createTransitioner();
735     if (d->transitioner->addTransition != transition) {
736         d->transitioner->addTransition = transition;
737         emit addTransitionChanged();
738     }
739 }
740
741 QQuickTransition *QQuickItemView::addDisplacedTransition() const
742 {
743     Q_D(const QQuickItemView);
744     return d->transitioner ? d->transitioner->addDisplacedTransition : 0;
745 }
746
747 void QQuickItemView::setAddDisplacedTransition(QQuickTransition *transition)
748 {
749     Q_D(QQuickItemView);
750     d->createTransitioner();
751     if (d->transitioner->addDisplacedTransition != transition) {
752         d->transitioner->addDisplacedTransition = transition;
753         emit addDisplacedTransitionChanged();
754     }
755 }
756
757 QQuickTransition *QQuickItemView::moveTransition() const
758 {
759     Q_D(const QQuickItemView);
760     return d->transitioner ? d->transitioner->moveTransition : 0;
761 }
762
763 void QQuickItemView::setMoveTransition(QQuickTransition *transition)
764 {
765     Q_D(QQuickItemView);
766     d->createTransitioner();
767     if (d->transitioner->moveTransition != transition) {
768         d->transitioner->moveTransition = transition;
769         emit moveTransitionChanged();
770     }
771 }
772
773 QQuickTransition *QQuickItemView::moveDisplacedTransition() const
774 {
775     Q_D(const QQuickItemView);
776     return d->transitioner ? d->transitioner->moveDisplacedTransition : 0;
777 }
778
779 void QQuickItemView::setMoveDisplacedTransition(QQuickTransition *transition)
780 {
781     Q_D(QQuickItemView);
782     d->createTransitioner();
783     if (d->transitioner->moveDisplacedTransition != transition) {
784         d->transitioner->moveDisplacedTransition = transition;
785         emit moveDisplacedTransitionChanged();
786     }
787 }
788
789 QQuickTransition *QQuickItemView::removeTransition() const
790 {
791     Q_D(const QQuickItemView);
792     return d->transitioner ? d->transitioner->removeTransition : 0;
793 }
794
795 void QQuickItemView::setRemoveTransition(QQuickTransition *transition)
796 {
797     Q_D(QQuickItemView);
798     d->createTransitioner();
799     if (d->transitioner->removeTransition != transition) {
800         d->transitioner->removeTransition = transition;
801         emit removeTransitionChanged();
802     }
803 }
804
805 QQuickTransition *QQuickItemView::removeDisplacedTransition() const
806 {
807     Q_D(const QQuickItemView);
808     return d->transitioner ? d->transitioner->removeDisplacedTransition : 0;
809 }
810
811 void QQuickItemView::setRemoveDisplacedTransition(QQuickTransition *transition)
812 {
813     Q_D(QQuickItemView);
814     d->createTransitioner();
815     if (d->transitioner->removeDisplacedTransition != transition) {
816         d->transitioner->removeDisplacedTransition = transition;
817         emit removeDisplacedTransitionChanged();
818     }
819 }
820
821 QQuickTransition *QQuickItemView::displacedTransition() const
822 {
823     Q_D(const QQuickItemView);
824     return d->transitioner ? d->transitioner->displacedTransition : 0;
825 }
826
827 void QQuickItemView::setDisplacedTransition(QQuickTransition *transition)
828 {
829     Q_D(QQuickItemView);
830     d->createTransitioner();
831     if (d->transitioner->displacedTransition != transition) {
832         d->transitioner->displacedTransition = transition;
833         emit displacedTransitionChanged();
834     }
835 }
836
837 void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
838 {
839     Q_Q(QQuickItemView);
840     if (!isValid())
841         return;
842     if (mode < QQuickItemView::Beginning || mode > QQuickItemView::SnapPosition)
843         return;
844
845     applyPendingChanges();
846     int idx = qMax(qMin(index, model->count()-1), 0);
847
848     qreal pos = isContentFlowReversed() ? -position() - size() : position();
849     FxViewItem *item = visibleItem(idx);
850     qreal maxExtent;
851     if (layoutOrientation() == Qt::Vertical)
852         maxExtent = isContentFlowReversed() ? q->minYExtent()-size(): -q->maxYExtent();
853     else
854         maxExtent = isContentFlowReversed() ? q->minXExtent()-size(): -q->maxXExtent();
855     if (!item) {
856         int itemPos = positionAt(idx);
857         changedVisibleIndex(idx);
858         // save the currently visible items in case any of them end up visible again
859         QList<FxViewItem *> oldVisible = visibleItems;
860         visibleItems.clear();
861         setPosition(qMin(qreal(itemPos), maxExtent));
862         // now release the reference to all the old visible items.
863         for (int i = 0; i < oldVisible.count(); ++i)
864             releaseItem(oldVisible.at(i));
865         item = visibleItem(idx);
866     }
867     if (item) {
868         const qreal itemPos = item->position();
869         switch (mode) {
870         case QQuickItemView::Beginning:
871             pos = itemPos;
872             if (index < 0 && header)
873                 pos -= headerSize();
874             break;
875         case QQuickItemView::Center:
876             pos = itemPos - (size() - item->size())/2;
877             break;
878         case QQuickItemView::End:
879             pos = itemPos - size() + item->size();
880             if (index >= model->count() && footer)
881                 pos += footerSize();
882             break;
883         case QQuickItemView::Visible:
884             if (itemPos > pos + size())
885                 pos = itemPos - size() + item->size();
886             else if (item->endPosition() <= pos)
887                 pos = itemPos;
888             break;
889         case QQuickItemView::Contain:
890             if (item->endPosition() >= pos + size())
891                 pos = itemPos - size() + item->size();
892             if (itemPos < pos)
893                 pos = itemPos;
894             break;
895         case QQuickItemView::SnapPosition:
896             pos = itemPos - highlightRangeStart;
897             break;
898         }
899         pos = qMin(pos, maxExtent);
900         qreal minExtent;
901         if (layoutOrientation() == Qt::Vertical)
902             minExtent = isContentFlowReversed() ? q->maxYExtent()-size(): -q->minYExtent();
903         else
904             minExtent = isContentFlowReversed() ? q->maxXExtent()-size(): -q->minXExtent();
905         pos = qMax(pos, minExtent);
906         moveReason = QQuickItemViewPrivate::Other;
907         q->cancelFlick();
908         setPosition(pos);
909
910         if (highlight) {
911             if (autoHighlight)
912                 resetHighlightPosition();
913             updateHighlight();
914         }
915     }
916     fixupPosition();
917 }
918
919 void QQuickItemView::positionViewAtIndex(int index, int mode)
920 {
921     Q_D(QQuickItemView);
922     if (!d->isValid() || index < 0 || index >= d->model->count())
923         return;
924     d->positionViewAtIndex(index, mode);
925 }
926
927
928 void QQuickItemView::positionViewAtBeginning()
929 {
930     Q_D(QQuickItemView);
931     if (!d->isValid())
932         return;
933     d->positionViewAtIndex(-1, Beginning);
934 }
935
936 void QQuickItemView::positionViewAtEnd()
937 {
938     Q_D(QQuickItemView);
939     if (!d->isValid())
940         return;
941     d->positionViewAtIndex(d->model->count(), End);
942 }
943
944 int QQuickItemView::indexAt(qreal x, qreal y) const
945 {
946     Q_D(const QQuickItemView);
947     for (int i = 0; i < d->visibleItems.count(); ++i) {
948         const FxViewItem *item = d->visibleItems.at(i);
949         if (item->contains(x, y))
950             return item->index;
951     }
952
953     return -1;
954 }
955
956 QQuickItem *QQuickItemView::itemAt(qreal x, qreal y) const
957 {
958     Q_D(const QQuickItemView);
959     for (int i = 0; i < d->visibleItems.count(); ++i) {
960         const FxViewItem *item = d->visibleItems.at(i);
961         if (item->contains(x, y))
962             return item->item;
963     }
964
965     return 0;
966 }
967
968 void QQuickItemViewPrivate::applyPendingChanges()
969 {
970     Q_Q(QQuickItemView);
971     if (q->isComponentComplete() && currentChanges.hasPendingChanges())
972         layout();
973 }
974
975 int QQuickItemViewPrivate::findMoveKeyIndex(QQuickChangeSet::MoveKey key, const QVector<QQuickChangeSet::Remove> &changes) const
976 {
977     for (int i=0; i<changes.count(); i++) {
978         for (int j=changes[i].index; j<changes[i].index + changes[i].count; j++) {
979             if (changes[i].moveKey(j) == key)
980                 return j;
981         }
982     }
983     return -1;
984 }
985
986 qreal QQuickItemViewPrivate::minExtentForAxis(const AxisData &axisData, bool forXAxis) const
987 {
988     Q_Q(const QQuickItemView);
989
990     qreal highlightStart;
991     qreal highlightEnd;
992     qreal endPositionFirstItem = 0;
993     qreal extent = -startPosition() + axisData.startMargin;
994     if (isContentFlowReversed()) {
995         if (model && model->count())
996             endPositionFirstItem = positionAt(model->count()-1);
997         else
998             extent += headerSize();
999         highlightStart = highlightRangeEndValid ? size() - highlightRangeEnd : size();
1000         highlightEnd = highlightRangeStartValid ? size() - highlightRangeStart : size();
1001         extent += footerSize();
1002         qreal maxExtentAlongAxis = forXAxis ? q->maxXExtent() : q->maxYExtent();
1003         if (extent < maxExtentAlongAxis)
1004             extent = maxExtentAlongAxis;
1005     } else {
1006         endPositionFirstItem = endPositionAt(0);
1007         highlightStart = highlightRangeStart;
1008         highlightEnd = highlightRangeEnd;
1009         extent += headerSize();
1010     }
1011     if (haveHighlightRange && highlightRange == QQuickItemView::StrictlyEnforceRange) {
1012         extent += highlightStart;
1013         FxViewItem *firstItem = visibleItem(0);
1014         if (firstItem)
1015             extent -= firstItem->sectionSize();
1016         extent = isContentFlowReversed()
1017                             ? qMin(extent, endPositionFirstItem + highlightEnd)
1018                             : qMax(extent, -(endPositionFirstItem - highlightEnd));
1019     }
1020     return extent;
1021 }
1022
1023 qreal QQuickItemViewPrivate::maxExtentForAxis(const AxisData &axisData, bool forXAxis) const
1024 {
1025     Q_Q(const QQuickItemView);
1026
1027     qreal highlightStart;
1028     qreal highlightEnd;
1029     qreal lastItemPosition = 0;
1030     qreal extent = 0;
1031     if (isContentFlowReversed()) {
1032         highlightStart = highlightRangeEndValid ? size() - highlightRangeEnd : size();
1033         highlightEnd = highlightRangeStartValid ? size() - highlightRangeStart : size();
1034         lastItemPosition = endPosition();
1035     } else {
1036         highlightStart = highlightRangeStart;
1037         highlightEnd = highlightRangeEnd;
1038         if (model && model->count())
1039             lastItemPosition = positionAt(model->count()-1);
1040     }
1041     if (!model || !model->count()) {
1042         if (!isContentFlowReversed())
1043             maxExtent = header ? -headerSize() : 0;
1044         extent += forXAxis ? q->width() : q->height();
1045     } else if (haveHighlightRange && highlightRange == QQuickItemView::StrictlyEnforceRange) {
1046         extent = -(lastItemPosition - highlightStart);
1047         if (highlightEnd != highlightStart) {
1048             extent = isContentFlowReversed()
1049                     ? qMax(extent, -(endPosition() - highlightEnd))
1050                     : qMin(extent, -(endPosition() - highlightEnd));
1051         }
1052     } else {
1053         extent = -(endPosition() - (forXAxis ? q->width() : q->height()));
1054     }
1055     if (isContentFlowReversed()) {
1056         extent -= headerSize();
1057         extent -= axisData.endMargin;
1058     } else {
1059         extent -= footerSize();
1060         extent -= axisData.endMargin;
1061         qreal minExtentAlongAxis = forXAxis ? q->minXExtent() : q->minYExtent();
1062         if (extent > minExtentAlongAxis)
1063             extent = minExtentAlongAxis;
1064     }
1065
1066     return extent;
1067 }
1068
1069 // for debugging only
1070 void QQuickItemViewPrivate::checkVisible() const
1071 {
1072     int skip = 0;
1073     for (int i = 0; i < visibleItems.count(); ++i) {
1074         FxViewItem *item = visibleItems.at(i);
1075         if (item->index == -1) {
1076             ++skip;
1077         } else if (item->index != visibleIndex + i - skip) {
1078             qFatal("index %d %d %d", visibleIndex, i, item->index);
1079         }
1080     }
1081 }
1082
1083 // for debugging only
1084 void QQuickItemViewPrivate::showVisibleItems() const
1085 {
1086     qDebug() << "Visible items:";
1087     for (int i = 0; i < visibleItems.count(); ++i) {
1088         qDebug() << "\t" << visibleItems[i]->index
1089                  << visibleItems[i]->item->objectName()
1090                  << visibleItems[i]->position();
1091     }
1092 }
1093
1094 void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
1095 {
1096     Q_Q(QQuickItemView);
1097     QQuickFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
1098     if (!q->isComponentComplete())
1099         return;
1100
1101     if (header && header->item == item) {
1102         updateHeader();
1103         markExtentsDirty();
1104         if (!q->isMoving() && !q->isFlicking())
1105             fixupPosition();
1106     } else if (footer && footer->item == item) {
1107         updateFooter();
1108         markExtentsDirty();
1109         if (!q->isMoving() && !q->isFlicking())
1110             fixupPosition();
1111     }
1112
1113     if (currentItem && currentItem->item == item) {
1114         // don't allow item movement transitions to trigger a re-layout and
1115         // start new transitions
1116         bool prevInLayout = inLayout;
1117         if (!inLayout) {
1118             FxViewItem *actualItem = transitioner ? visibleItem(currentIndex) : 0;
1119             if (actualItem && actualItem->transitionRunning())
1120                 inLayout = true;
1121         }
1122         updateHighlight();
1123         inLayout = prevInLayout;
1124     }
1125
1126     if (trackedItem && trackedItem->item == item)
1127         q->trackedPositionChanged();
1128 }
1129
1130 void QQuickItemView::destroyRemoved()
1131 {
1132     Q_D(QQuickItemView);
1133     for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
1134             it != d->visibleItems.end();) {
1135         FxViewItem *item = *it;
1136         if (item->index == -1 && item->attached->delayRemove() == false) {
1137             if (d->transitioner && d->transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true)) {
1138                 // don't remove from visibleItems until next layout()
1139                 d->runDelayedRemoveTransition = true;
1140                 QObject::disconnect(item->attached, SIGNAL(delayRemoveChanged()), this, SLOT(destroyRemoved()));
1141                 ++it;
1142             } else {
1143                 d->releaseItem(item);
1144                 it = d->visibleItems.erase(it);
1145             }
1146         } else {
1147             ++it;
1148         }
1149     }
1150
1151     // Correct the positioning of the items
1152     d->forceLayoutPolish();
1153 }
1154
1155 void QQuickItemView::modelUpdated(const QQuickChangeSet &changeSet, bool reset)
1156 {
1157     Q_D(QQuickItemView);
1158     if (reset) {
1159         if (d->transitioner)
1160             d->transitioner->setPopulateTransitionEnabled(true);
1161         d->moveReason = QQuickItemViewPrivate::SetIndex;
1162         d->regenerate();
1163         if (d->highlight && d->currentItem) {
1164             if (d->autoHighlight)
1165                 d->resetHighlightPosition();
1166             d->updateTrackedItem();
1167         }
1168         d->moveReason = QQuickItemViewPrivate::Other;
1169         emit countChanged();
1170         if (d->transitioner && d->transitioner->populateTransition)
1171             d->forceLayoutPolish();
1172     } else {
1173         if (d->inLayout) {
1174             d->bufferedChanges.prepare(d->currentIndex, d->itemCount);
1175             d->bufferedChanges.applyChanges(changeSet);
1176         } else {
1177             if (d->bufferedChanges.hasPendingChanges()) {
1178                 d->currentChanges.applyBufferedChanges(d->bufferedChanges);
1179                 d->bufferedChanges.reset();
1180             }
1181             d->currentChanges.prepare(d->currentIndex, d->itemCount);
1182             d->currentChanges.applyChanges(changeSet);
1183         }
1184         polish();
1185     }
1186 }
1187
1188 void QQuickItemView::animStopped()
1189 {
1190     Q_D(QQuickItemView);
1191     d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter;
1192     d->refillOrLayout();
1193     if (d->haveHighlightRange && d->highlightRange == QQuickItemView::StrictlyEnforceRange)
1194         d->updateHighlight();
1195 }
1196
1197
1198 void QQuickItemView::trackedPositionChanged()
1199 {
1200     Q_D(QQuickItemView);
1201     if (!d->trackedItem || !d->currentItem)
1202         return;
1203     if (d->moveReason == QQuickItemViewPrivate::SetIndex) {
1204         qreal trackedPos = d->trackedItem->position();
1205         qreal trackedSize = d->trackedItem->size();
1206         qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position();
1207         qreal pos = viewPos;
1208         if (d->haveHighlightRange) {
1209             if (trackedPos > pos + d->highlightRangeEnd - trackedSize)
1210                 pos = trackedPos - d->highlightRangeEnd + trackedSize;
1211             if (trackedPos < pos + d->highlightRangeStart)
1212                 pos = trackedPos - d->highlightRangeStart;
1213             if (d->highlightRange != StrictlyEnforceRange) {
1214                 if (pos > d->endPosition() - d->size())
1215                     pos = d->endPosition() - d->size();
1216                 if (pos < d->startPosition())
1217                     pos = d->startPosition();
1218             }
1219         } else {
1220             if (d->trackedItem != d->currentItem) {
1221                 // also make section header visible
1222                 trackedPos -= d->currentItem->sectionSize();
1223                 trackedSize += d->currentItem->sectionSize();
1224             }
1225             qreal trackedEndPos = d->trackedItem->endPosition();
1226             qreal toItemPos = d->currentItem->position();
1227             qreal toItemEndPos = d->currentItem->endPosition();
1228             if (d->showHeaderForIndex(d->currentIndex)) {
1229                 qreal startOffset = -d->contentStartOffset();
1230                 trackedPos -= startOffset;
1231                 trackedEndPos -= startOffset;
1232                 toItemPos -= startOffset;
1233                 toItemEndPos -= startOffset;
1234             } else if (d->showFooterForIndex(d->currentIndex)) {
1235                 qreal endOffset = d->footerSize();
1236                 if (d->layoutOrientation() == Qt::Vertical) {
1237                     if (d->isContentFlowReversed())
1238                         endOffset += d->vData.startMargin;
1239                     else
1240                         endOffset += d->vData.endMargin;
1241                 } else {
1242                     if (d->isContentFlowReversed())
1243                         endOffset += d->hData.startMargin;
1244                     else
1245                         endOffset += d->hData.endMargin;
1246                 }
1247                 trackedPos += endOffset;
1248                 trackedEndPos += endOffset;
1249                 toItemPos += endOffset;
1250                 toItemEndPos += endOffset;
1251             }
1252
1253             if (trackedEndPos >= viewPos + d->size()
1254                 && toItemEndPos >= viewPos + d->size()) {
1255                 if (trackedEndPos <= toItemEndPos) {
1256                     pos = trackedEndPos - d->size();
1257                     if (trackedSize > d->size())
1258                         pos = trackedPos;
1259                 } else {
1260                     pos = toItemEndPos - d->size();
1261                     if (d->currentItem->size() > d->size())
1262                         pos = d->currentItem->position();
1263                 }
1264             }
1265             if (trackedPos < pos && toItemPos < pos)
1266                 pos = qMax(trackedPos, toItemPos);
1267         }
1268         if (viewPos != pos) {
1269             cancelFlick();
1270             d->calcVelocity = true;
1271             d->setPosition(pos);
1272             d->calcVelocity = false;
1273         }
1274     }
1275 }
1276
1277 void QQuickItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
1278 {
1279     Q_D(QQuickItemView);
1280     d->markExtentsDirty();
1281     if (isComponentComplete() && d->isValid())
1282         d->forceLayoutPolish();
1283     QQuickFlickable::geometryChanged(newGeometry, oldGeometry);
1284 }
1285
1286 qreal QQuickItemView::minYExtent() const
1287 {
1288     Q_D(const QQuickItemView);
1289     if (d->layoutOrientation() == Qt::Horizontal)
1290         return QQuickFlickable::minYExtent();
1291
1292     if (d->vData.minExtentDirty) {
1293         d->minExtent = d->minExtentForAxis(d->vData, false);
1294         d->vData.minExtentDirty = false;
1295     }
1296
1297     return d->minExtent;
1298 }
1299
1300 qreal QQuickItemView::maxYExtent() const
1301 {
1302     Q_D(const QQuickItemView);
1303     if (d->layoutOrientation() == Qt::Horizontal)
1304         return height();
1305
1306     if (d->vData.maxExtentDirty) {
1307         d->maxExtent = d->maxExtentForAxis(d->vData, false);
1308         d->vData.maxExtentDirty = false;
1309     }
1310
1311     return d->maxExtent;
1312 }
1313
1314 qreal QQuickItemView::minXExtent() const
1315 {
1316     Q_D(const QQuickItemView);
1317     if (d->layoutOrientation() == Qt::Vertical)
1318         return QQuickFlickable::minXExtent();
1319
1320     if (d->hData.minExtentDirty) {
1321         d->minExtent = d->minExtentForAxis(d->hData, true);
1322         d->hData.minExtentDirty = false;
1323     }
1324
1325     return d->minExtent;
1326 }
1327
1328 qreal QQuickItemView::maxXExtent() const
1329 {
1330     Q_D(const QQuickItemView);
1331     if (d->layoutOrientation() == Qt::Vertical)
1332         return width();
1333
1334     if (d->hData.maxExtentDirty) {
1335         d->maxExtent = d->maxExtentForAxis(d->hData, true);
1336         d->hData.maxExtentDirty = false;
1337     }
1338
1339     return d->maxExtent;
1340 }
1341
1342 void QQuickItemView::setContentX(qreal pos)
1343 {
1344     Q_D(QQuickItemView);
1345     // Positioning the view manually should override any current movement state
1346     d->moveReason = QQuickItemViewPrivate::Other;
1347     QQuickFlickable::setContentX(pos);
1348 }
1349
1350 void QQuickItemView::setContentY(qreal pos)
1351 {
1352     Q_D(QQuickItemView);
1353     // Positioning the view manually should override any current movement state
1354     d->moveReason = QQuickItemViewPrivate::Other;
1355     QQuickFlickable::setContentY(pos);
1356 }
1357
1358 qreal QQuickItemView::originX() const
1359 {
1360     Q_D(const QQuickItemView);
1361     if (d->layoutOrientation() == Qt::Horizontal
1362             && effectiveLayoutDirection() == Qt::RightToLeft
1363             && contentWidth() < width()) {
1364         return d->lastPosition() - d->footerSize();
1365     }
1366     return QQuickFlickable::originX();
1367 }
1368
1369 qreal QQuickItemView::originY() const
1370 {
1371     Q_D(const QQuickItemView);
1372     if (d->layoutOrientation() == Qt::Vertical
1373             && d->verticalLayoutDirection == QQuickItemView::BottomToTop
1374             && contentHeight() < height()) {
1375         return d->lastPosition() - d->footerSize();
1376     }
1377     return QQuickFlickable::originY();
1378 }
1379
1380 void QQuickItemView::updatePolish()
1381 {
1382     Q_D(QQuickItemView);
1383     QQuickFlickable::updatePolish();
1384     d->layout();
1385 }
1386
1387 void QQuickItemView::componentComplete()
1388 {
1389     Q_D(QQuickItemView);
1390     if (d->model && d->ownModel)
1391         static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete();
1392
1393     QQuickFlickable::componentComplete();
1394
1395     d->updateSectionCriteria();
1396     d->updateHeader();
1397     d->updateFooter();
1398     d->updateViewport();
1399     d->setPosition(d->contentStartOffset());
1400     if (d->transitioner)
1401         d->transitioner->setPopulateTransitionEnabled(true);
1402
1403     if (d->isValid()) {
1404         d->refill();
1405         d->moveReason = QQuickItemViewPrivate::SetIndex;
1406         if (d->currentIndex < 0 && !d->currentIndexCleared)
1407             d->updateCurrent(0);
1408         else
1409             d->updateCurrent(d->currentIndex);
1410         if (d->highlight && d->currentItem) {
1411             if (d->autoHighlight)
1412                 d->resetHighlightPosition();
1413             d->updateTrackedItem();
1414         }
1415         d->moveReason = QQuickItemViewPrivate::Other;
1416         d->fixupPosition();
1417     }
1418     if (d->model && d->model->count())
1419         emit countChanged();
1420 }
1421
1422
1423
1424 QQuickItemViewPrivate::QQuickItemViewPrivate()
1425     : itemCount(0)
1426     , buffer(QML_VIEW_DEFAULTCACHEBUFFER), bufferMode(BufferBefore | BufferAfter)
1427     , layoutDirection(Qt::LeftToRight), verticalLayoutDirection(QQuickItemView::TopToBottom)
1428     , moveReason(Other)
1429     , visibleIndex(0)
1430     , currentIndex(-1), currentItem(0)
1431     , trackedItem(0), requestedIndex(-1)
1432     , highlightComponent(0), highlight(0)
1433     , highlightRange(QQuickItemView::NoHighlightRange)
1434     , highlightRangeStart(0), highlightRangeEnd(0)
1435     , highlightMoveDuration(150)
1436     , headerComponent(0), header(0), footerComponent(0), footer(0)
1437     , transitioner(0)
1438     , minExtent(0), maxExtent(0)
1439     , ownModel(false), wrap(false)
1440     , inLayout(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false)
1441     , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
1442     , fillCacheBuffer(false), inRequest(false)
1443     , runDelayedRemoveTransition(false)
1444 {
1445     bufferPause.addAnimationChangeListener(this, QAbstractAnimationJob::Completion);
1446     bufferPause.setLoopCount(1);
1447     bufferPause.setDuration(16);
1448 }
1449
1450 QQuickItemViewPrivate::~QQuickItemViewPrivate()
1451 {
1452     if (transitioner)
1453         transitioner->setChangeListener(0);
1454     delete transitioner;
1455 }
1456
1457 bool QQuickItemViewPrivate::isValid() const
1458 {
1459     return model && model->count() && model->isValid();
1460 }
1461
1462 qreal QQuickItemViewPrivate::position() const
1463 {
1464     Q_Q(const QQuickItemView);
1465     return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX();
1466 }
1467
1468 qreal QQuickItemViewPrivate::size() const
1469 {
1470     Q_Q(const QQuickItemView);
1471     return layoutOrientation() == Qt::Vertical ? q->height() : q->width();
1472 }
1473
1474 qreal QQuickItemViewPrivate::startPosition() const
1475 {
1476     return isContentFlowReversed() ? -lastPosition() : originPosition();
1477 }
1478
1479 qreal QQuickItemViewPrivate::endPosition() const
1480 {
1481     return isContentFlowReversed() ? -originPosition() : lastPosition();
1482 }
1483
1484 qreal QQuickItemViewPrivate::contentStartOffset() const
1485 {
1486     qreal pos = -headerSize();
1487     if (layoutOrientation() == Qt::Vertical) {
1488         if (isContentFlowReversed())
1489             pos -= vData.endMargin;
1490         else
1491             pos -= vData.startMargin;
1492     } else {
1493         if (isContentFlowReversed())
1494             pos -= hData.endMargin;
1495         else
1496             pos -= hData.startMargin;
1497     }
1498     return pos;
1499 }
1500
1501 int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const
1502 {
1503     if (visibleItems.count()) {
1504         int i = visibleItems.count() - 1;
1505         while (i > 0 && visibleItems.at(i)->index == -1)
1506             --i;
1507         if (visibleItems.at(i)->index != -1)
1508             return visibleItems.at(i)->index;
1509     }
1510     return defaultValue;
1511 }
1512
1513 FxViewItem *QQuickItemViewPrivate::visibleItem(int modelIndex) const {
1514     if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
1515         for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
1516             FxViewItem *item = visibleItems.at(i);
1517             if (item->index == modelIndex)
1518                 return item;
1519         }
1520     }
1521     return 0;
1522 }
1523
1524 // should rename to firstItemInView() to avoid confusion with other "*visible*" methods
1525 // that don't look at the view position and size
1526 FxViewItem *QQuickItemViewPrivate::firstVisibleItem() const {
1527     const qreal pos = isContentFlowReversed() ? -position()-size() : position();
1528     for (int i = 0; i < visibleItems.count(); ++i) {
1529         FxViewItem *item = visibleItems.at(i);
1530         if (item->index != -1 && item->endPosition() > pos)
1531             return item;
1532     }
1533     return visibleItems.count() ? visibleItems.first() : 0;
1534 }
1535
1536 int QQuickItemViewPrivate::findLastIndexInView() const
1537 {
1538     const qreal viewEndPos = isContentFlowReversed() ? -position() : position() + size();
1539     for (int i=visibleItems.count() - 1; i>=0; i--) {
1540         if (visibleItems.at(i)->position() <= viewEndPos && visibleItems.at(i)->index != -1)
1541             return visibleItems.at(i)->index;
1542     }
1543     return -1;
1544 }
1545
1546 // Map a model index to visibleItems list index.
1547 // These may differ if removed items are still present in the visible list,
1548 // e.g. doing a removal animation
1549 int QQuickItemViewPrivate::mapFromModel(int modelIndex) const
1550 {
1551     if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
1552         return -1;
1553     for (int i = 0; i < visibleItems.count(); ++i) {
1554         FxViewItem *item = visibleItems.at(i);
1555         if (item->index == modelIndex)
1556             return i;
1557         if (item->index > modelIndex)
1558             return -1;
1559     }
1560     return -1; // Not in visibleList
1561 }
1562
1563 void QQuickItemViewPrivate::init()
1564 {
1565     Q_Q(QQuickItemView);
1566     q->setFlag(QQuickItem::ItemIsFocusScope);
1567     QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
1568     q->setFlickableDirection(QQuickFlickable::VerticalFlick);
1569 }
1570
1571 void QQuickItemViewPrivate::updateCurrent(int modelIndex)
1572 {
1573     Q_Q(QQuickItemView);
1574     applyPendingChanges();
1575     if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1576         if (currentItem) {
1577             currentItem->attached->setIsCurrentItem(false);
1578             releaseItem(currentItem);
1579             currentItem = 0;
1580             currentIndex = modelIndex;
1581             emit q->currentIndexChanged();
1582             emit q->currentItemChanged();
1583             updateHighlight();
1584         } else if (currentIndex != modelIndex) {
1585             currentIndex = modelIndex;
1586             emit q->currentIndexChanged();
1587         }
1588         return;
1589     }
1590
1591     if (currentItem && currentIndex == modelIndex) {
1592         updateHighlight();
1593         return;
1594     }
1595
1596     FxViewItem *oldCurrentItem = currentItem;
1597     int oldCurrentIndex = currentIndex;
1598     currentIndex = modelIndex;
1599     currentItem = createItem(modelIndex, false);
1600     if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1601         oldCurrentItem->attached->setIsCurrentItem(false);
1602     if (currentItem) {
1603         currentItem->item->setFocus(true);
1604         currentItem->attached->setIsCurrentItem(true);
1605         initializeCurrentItem();
1606     }
1607
1608     updateHighlight();
1609     if (oldCurrentIndex != currentIndex)
1610         emit q->currentIndexChanged();
1611     if (oldCurrentItem != currentItem)
1612         emit q->currentItemChanged();
1613     releaseItem(oldCurrentItem);
1614 }
1615
1616 void QQuickItemViewPrivate::clear()
1617 {
1618     currentChanges.reset();
1619     timeline.clear();
1620
1621     for (int i = 0; i < visibleItems.count(); ++i)
1622         releaseItem(visibleItems.at(i));
1623     visibleItems.clear();
1624     visibleIndex = 0;
1625
1626     for (int i = 0; i < releasePendingTransition.count(); ++i) {
1627         releasePendingTransition.at(i)->releaseAfterTransition = false;
1628         releaseItem(releasePendingTransition.at(i));
1629     }
1630     releasePendingTransition.clear();
1631
1632     releaseItem(currentItem);
1633     currentItem = 0;
1634     createHighlight();
1635     trackedItem = 0;
1636
1637     if (requestedIndex >= 0) {
1638         if (model)
1639             model->cancel(requestedIndex);
1640         requestedIndex = -1;
1641     }
1642
1643     markExtentsDirty();
1644     itemCount = 0;
1645 }
1646
1647
1648 void QQuickItemViewPrivate::mirrorChange()
1649 {
1650     Q_Q(QQuickItemView);
1651     regenerate();
1652     emit q->effectiveLayoutDirectionChanged();
1653 }
1654
1655 void QQuickItemViewPrivate::animationFinished(QAbstractAnimationJob *)
1656 {
1657     Q_Q(QQuickItemView);
1658     fillCacheBuffer = true;
1659     q->polish();
1660 }
1661
1662 void QQuickItemViewPrivate::refill()
1663 {
1664     qreal s = qMax(size(), qreal(0.));
1665     if (isContentFlowReversed())
1666         refill(-position()-s, -position());
1667     else
1668         refill(position(), position()+s);
1669 }
1670
1671 void QQuickItemViewPrivate::refill(qreal from, qreal to)
1672 {
1673     Q_Q(QQuickItemView);
1674     if (!isValid() || !q->isComponentComplete())
1675         return;
1676
1677     bufferPause.stop();
1678     currentChanges.reset();
1679
1680     int prevCount = itemCount;
1681     itemCount = model->count();
1682     qreal bufferFrom = from - buffer;
1683     qreal bufferTo = to + buffer;
1684     qreal fillFrom = from;
1685     qreal fillTo = to;
1686
1687     bool added = addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, false);
1688     bool removed = removeNonVisibleItems(bufferFrom, bufferTo);
1689
1690     if (requestedIndex == -1 && buffer && bufferMode != NoBuffer) {
1691         if (added) {
1692             // We've already created a new delegate this frame.
1693             // Just schedule a buffer refill.
1694             bufferPause.start();
1695         } else {
1696             if (bufferMode & BufferAfter)
1697                 fillTo = bufferTo;
1698             if (bufferMode & BufferBefore)
1699                 fillFrom = bufferFrom;
1700             added |= addVisibleItems(fillFrom, fillTo, bufferFrom, bufferTo, true);
1701         }
1702     }
1703
1704     if (added || removed) {
1705         markExtentsDirty();
1706         updateBeginningEnd();
1707         visibleItemsChanged();
1708         updateHeader();
1709         updateFooter();
1710         updateViewport();
1711     }
1712
1713     if (prevCount != itemCount)
1714         emit q->countChanged();
1715 }
1716
1717 void QQuickItemViewPrivate::regenerate()
1718 {
1719     Q_Q(QQuickItemView);
1720     if (q->isComponentComplete()) {
1721         currentChanges.reset();
1722         delete header;
1723         header = 0;
1724         delete footer;
1725         footer = 0;
1726         updateHeader();
1727         updateFooter();
1728         clear();
1729         updateViewport();
1730         setPosition(contentStartOffset());
1731         refill();
1732         updateCurrent(currentIndex);
1733     }
1734 }
1735
1736 void QQuickItemViewPrivate::updateViewport()
1737 {
1738     Q_Q(QQuickItemView);
1739     qreal extra = headerSize() + footerSize();
1740     qreal contentSize = isValid() ? (endPosition() - startPosition()) : 0.0;
1741     if (layoutOrientation() == Qt::Vertical)
1742         q->setContentHeight(contentSize + extra);
1743     else
1744         q->setContentWidth(contentSize + extra);
1745 }
1746
1747 void QQuickItemViewPrivate::layout()
1748 {
1749     Q_Q(QQuickItemView);
1750     if (inLayout)
1751         return;
1752
1753     inLayout = true;
1754
1755     if (!isValid() && !visibleItems.count()) {
1756         clear();
1757         setPosition(contentStartOffset());
1758         if (transitioner)
1759             transitioner->setPopulateTransitionEnabled(false);
1760         inLayout = false;
1761         return;
1762     }
1763
1764     if (runDelayedRemoveTransition && transitioner
1765             && transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
1766         // assume that any items moving now are moving due to the remove - if they schedule
1767         // a different transition, that will override this one anyway
1768         for (int i=0; i<visibleItems.count(); i++)
1769             visibleItems[i]->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
1770     }
1771
1772     ChangeResult insertionPosChanges;
1773     ChangeResult removalPosChanges;
1774     if (!applyModelChanges(&insertionPosChanges, &removalPosChanges) && !forceLayout) {
1775         if (fillCacheBuffer) {
1776             fillCacheBuffer = false;
1777             refill();
1778         }
1779         inLayout = false;
1780         return;
1781     }
1782     forceLayout = false;
1783
1784     if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::PopulateTransition, true)) {
1785         for (int i=0; i<visibleItems.count(); i++) {
1786             if (!visibleItems.at(i)->transitionScheduledOrRunning())
1787                 visibleItems.at(i)->transitionNextReposition(transitioner, QQuickItemViewTransitioner::PopulateTransition, true);
1788         }
1789     }
1790
1791     updateSections();
1792     layoutVisibleItems();
1793
1794     int lastIndexInView = findLastIndexInView();
1795     refill();
1796     markExtentsDirty();
1797     updateHighlight();
1798
1799     if (!q->isMoving() && !q->isFlicking()) {
1800         fixupPosition();
1801         refill();
1802     }
1803
1804     updateHeader();
1805     updateFooter();
1806     updateViewport();
1807     updateUnrequestedPositions();
1808
1809     if (transitioner) {
1810         // items added in the last refill() may need to be transitioned in - e.g. a remove
1811         // causes items to slide up into view
1812         if (transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, false)
1813                 || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
1814             translateAndTransitionItemsAfter(lastIndexInView, insertionPosChanges, removalPosChanges);
1815         }
1816
1817         prepareVisibleItemTransitions();
1818
1819         QRectF viewBounds(0, position(), q->width(), q->height());
1820         for (QList<FxViewItem*>::Iterator it = releasePendingTransition.begin();
1821              it != releasePendingTransition.end(); ) {
1822             FxViewItem *item = *it;
1823             if (prepareNonVisibleItemTransition(item, viewBounds)) {
1824                 ++it;
1825             } else {
1826                 releaseItem(item);
1827                 it = releasePendingTransition.erase(it);
1828             }
1829         }
1830
1831         for (int i=0; i<visibleItems.count(); i++)
1832             visibleItems[i]->startTransition(transitioner);
1833         for (int i=0; i<releasePendingTransition.count(); i++)
1834             releasePendingTransition[i]->startTransition(transitioner);
1835
1836         transitioner->setPopulateTransitionEnabled(false);
1837         transitioner->resetTargetLists();
1838     }
1839
1840     runDelayedRemoveTransition = false;
1841     inLayout = false;
1842 }
1843
1844 bool QQuickItemViewPrivate::applyModelChanges(ChangeResult *totalInsertionResult, ChangeResult *totalRemovalResult)
1845 {
1846     Q_Q(QQuickItemView);
1847     if (!q->isComponentComplete() || !hasPendingChanges())
1848         return false;
1849
1850     if (bufferedChanges.hasPendingChanges()) {
1851         currentChanges.applyBufferedChanges(bufferedChanges);
1852         bufferedChanges.reset();
1853     }
1854
1855     updateUnrequestedIndexes();
1856     moveReason = QQuickItemViewPrivate::Other;
1857
1858     FxViewItem *prevVisibleItemsFirst = visibleItems.count() ? *visibleItems.constBegin() : 0;
1859     int prevItemCount = itemCount;
1860     int prevVisibleItemsCount = visibleItems.count();
1861     bool visibleAffected = false;
1862     bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty()
1863             || !currentChanges.pendingChanges.inserts().isEmpty();
1864
1865     FxViewItem *prevFirstVisible = firstVisibleItem();
1866     QQmlNullableValue<qreal> prevViewPos;
1867     int prevFirstVisibleIndex = -1;
1868     if (prevFirstVisible) {
1869         prevViewPos = prevFirstVisible->position();
1870         prevFirstVisibleIndex = prevFirstVisible->index;
1871     }
1872     qreal prevVisibleItemsFirstPos = visibleItems.count() ? visibleItems.first()->position() : 0.0;
1873
1874     totalInsertionResult->visiblePos = prevViewPos;
1875     totalRemovalResult->visiblePos = prevViewPos;
1876
1877     const QVector<QQuickChangeSet::Remove> &removals = currentChanges.pendingChanges.removes();
1878     const QVector<QQuickChangeSet::Insert> &insertions = currentChanges.pendingChanges.inserts();
1879     ChangeResult insertionResult(prevViewPos);
1880     ChangeResult removalResult(prevViewPos);
1881
1882     int removedCount = 0;
1883     for (int i=0; i<removals.count(); i++) {
1884         itemCount -= removals[i].count;
1885         if (applyRemovalChange(removals[i], &removalResult, &removedCount))
1886             visibleAffected = true;
1887         if (!visibleAffected && needsRefillForAddedOrRemovedIndex(removals[i].index))
1888             visibleAffected = true;
1889         const int correctedFirstVisibleIndex = prevFirstVisibleIndex - removalResult.countChangeBeforeVisible;
1890         if (correctedFirstVisibleIndex >= 0 && removals[i].index < correctedFirstVisibleIndex) {
1891             if (removals[i].index + removals[i].count < correctedFirstVisibleIndex)
1892                 removalResult.countChangeBeforeVisible += removals[i].count;
1893             else
1894                 removalResult.countChangeBeforeVisible += (correctedFirstVisibleIndex  - removals[i].index);
1895         }
1896     }
1897     if (runDelayedRemoveTransition) {
1898         QQuickChangeSet::Remove removal;
1899         for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end();) {
1900             FxViewItem *item = *it;
1901             if (item->index == -1 && !item->attached->delayRemove()) {
1902                 removeItem(item, removal, &removalResult);
1903                 removedCount++;
1904                 it = visibleItems.erase(it);
1905             } else {
1906                ++it;
1907             }
1908         }
1909     }
1910     *totalRemovalResult += removalResult;
1911     if (!removals.isEmpty()) {
1912         updateVisibleIndex();
1913
1914         // set positions correctly for the next insertion
1915         if (!insertions.isEmpty()) {
1916             repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
1917             layoutVisibleItems(removals.first().index);
1918         }
1919     }
1920
1921     QList<FxViewItem *> newItems;
1922     QList<MovedItem> movingIntoView;
1923
1924     for (int i=0; i<insertions.count(); i++) {
1925         bool wasEmpty = visibleItems.isEmpty();
1926         if (applyInsertionChange(insertions[i], &insertionResult, &newItems, &movingIntoView))
1927             visibleAffected = true;
1928         if (!visibleAffected && needsRefillForAddedOrRemovedIndex(insertions[i].index))
1929             visibleAffected = true;
1930         if (wasEmpty && !visibleItems.isEmpty())
1931             resetFirstItemPosition();
1932         *totalInsertionResult += insertionResult;
1933
1934         // set positions correctly for the next insertion
1935         if (i < insertions.count() - 1) {
1936             repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
1937             layoutVisibleItems(insertions[i].index);
1938         }
1939         itemCount += insertions[i].count;
1940     }
1941     for (int i=0; i<newItems.count(); i++)
1942         newItems.at(i)->attached->emitAdd();
1943
1944     // for each item that was moved directly into the view as a result of a move(),
1945     // find the index it was moved from in order to set its initial position, so that we
1946     // can transition it from this "original" position to its new position in the view
1947     if (transitioner && transitioner->canTransition(QQuickItemViewTransitioner::MoveTransition, true)) {
1948         for (int i=0; i<movingIntoView.count(); i++) {
1949             int fromIndex = findMoveKeyIndex(movingIntoView[i].moveKey, removals);
1950             if (fromIndex >= 0) {
1951                 if (prevFirstVisibleIndex >= 0 && fromIndex < prevFirstVisibleIndex)
1952                     repositionItemAt(movingIntoView[i].item, fromIndex, -totalInsertionResult->sizeChangesAfterVisiblePos);
1953                 else
1954                     repositionItemAt(movingIntoView[i].item, fromIndex, totalInsertionResult->sizeChangesAfterVisiblePos);
1955                 movingIntoView[i].item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
1956             }
1957         }
1958     }
1959
1960     // reposition visibleItems.first() correctly so that the content y doesn't jump
1961     if (removedCount != prevVisibleItemsCount)
1962         repositionFirstItem(prevVisibleItemsFirst, prevVisibleItemsFirstPos, prevFirstVisible, &insertionResult, &removalResult);
1963
1964     // Whatever removed/moved items remain are no longer visible items.
1965     prepareRemoveTransitions(&currentChanges.removedItems);
1966     for (QHash<QQuickChangeSet::MoveKey, FxViewItem *>::Iterator it = currentChanges.removedItems.begin();
1967          it != currentChanges.removedItems.end(); ++it) {
1968         releaseItem(it.value());
1969     }
1970     currentChanges.removedItems.clear();
1971
1972     if (currentChanges.currentChanged) {
1973         if (currentChanges.currentRemoved && currentItem) {
1974             currentItem->attached->setIsCurrentItem(false);
1975             releaseItem(currentItem);
1976             currentItem = 0;
1977         }
1978         if (!currentIndexCleared)
1979             updateCurrent(currentChanges.newCurrentIndex);
1980     }
1981
1982     if (!visibleAffected)
1983         visibleAffected = !currentChanges.pendingChanges.changes().isEmpty();
1984     currentChanges.reset();
1985
1986     updateSections();
1987     if (prevItemCount != itemCount)
1988         emit q->countChanged();
1989     if (!visibleAffected && viewportChanged)
1990         updateViewport();
1991
1992     return visibleAffected;
1993 }
1994
1995 bool QQuickItemViewPrivate::applyRemovalChange(const QQuickChangeSet::Remove &removal, ChangeResult *removeResult, int *removedCount)
1996 {
1997     Q_Q(QQuickItemView);
1998     bool visibleAffected = false;
1999
2000     if (visibleItems.count() && removal.index + removal.count > visibleItems.last()->index) {
2001         if (removal.index > visibleItems.last()->index)
2002             removeResult->countChangeAfterVisibleItems += removal.count;
2003         else
2004             removeResult->countChangeAfterVisibleItems += ((removal.index + removal.count - 1) - visibleItems.last()->index);
2005     }
2006
2007     QList<FxViewItem*>::Iterator it = visibleItems.begin();
2008     while (it != visibleItems.end()) {
2009         FxViewItem *item = *it;
2010         if (item->index == -1 || item->index < removal.index) {
2011             // already removed, or before removed items
2012             if (!visibleAffected && item->index < removal.index)
2013                 visibleAffected = true;
2014             ++it;
2015         } else if (item->index >= removal.index + removal.count) {
2016             // after removed items
2017             item->index -= removal.count;
2018             if (removal.isMove())
2019                 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, false);
2020             else
2021                 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, false);
2022             ++it;
2023         } else {
2024             // removed item
2025             visibleAffected = true;
2026             if (!removal.isMove())
2027                 item->attached->emitRemove();
2028
2029             if (item->attached->delayRemove() && !removal.isMove()) {
2030                 item->index = -1;
2031                 QObject::connect(item->attached, SIGNAL(delayRemoveChanged()), q, SLOT(destroyRemoved()), Qt::QueuedConnection);
2032                 ++it;
2033             } else {
2034                 removeItem(item, removal, removeResult);
2035                 if (!removal.isMove())
2036                     (*removedCount)++;
2037                 it = visibleItems.erase(it);
2038             }
2039         }
2040     }
2041
2042     return visibleAffected;
2043 }
2044
2045 void QQuickItemViewPrivate::removeItem(FxViewItem *item, const QQuickChangeSet::Remove &removal, ChangeResult *removeResult)
2046 {
2047     if (removeResult->visiblePos.isValid()) {
2048         if (item->position() < removeResult->visiblePos)
2049             removeResult->sizeChangesBeforeVisiblePos += item->size();
2050         else
2051             removeResult->sizeChangesAfterVisiblePos += item->size();
2052     }
2053     if (removal.isMove()) {
2054         currentChanges.removedItems.insert(removal.moveKey(item->index), item);
2055         item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::MoveTransition, true);
2056     } else {
2057         // track item so it is released later
2058         currentChanges.removedItems.insertMulti(QQuickChangeSet::MoveKey(), item);
2059     }
2060     if (!removeResult->changedFirstItem && item == *visibleItems.constBegin())
2061         removeResult->changedFirstItem = true;
2062 }
2063
2064 void QQuickItemViewPrivate::repositionFirstItem(FxViewItem *prevVisibleItemsFirst,
2065                                                    qreal prevVisibleItemsFirstPos,
2066                                                    FxViewItem *prevFirstVisible,
2067                                                    ChangeResult *insertionResult,
2068                                                    ChangeResult *removalResult)
2069 {
2070     const QQmlNullableValue<qreal> prevViewPos = insertionResult->visiblePos;
2071
2072     // reposition visibleItems.first() correctly so that the content y doesn't jump
2073     if (visibleItems.count()) {
2074         if (prevVisibleItemsFirst && insertionResult->changedFirstItem)
2075             resetFirstItemPosition(prevVisibleItemsFirstPos);
2076
2077         if (prevFirstVisible && prevVisibleItemsFirst == prevFirstVisible
2078                 && prevFirstVisible != *visibleItems.constBegin()) {
2079             // the previous visibleItems.first() was also the first visible item, and it has been
2080             // moved/removed, so move the new visibleItems.first() to the pos of the previous one
2081             if (!insertionResult->changedFirstItem)
2082                 resetFirstItemPosition(prevVisibleItemsFirstPos);
2083
2084         } else if (prevViewPos.isValid()) {
2085             qreal moveForwardsBy = 0;
2086             qreal moveBackwardsBy = 0;
2087
2088             // shift visibleItems.first() relative to the number of added/removed items
2089             if (visibleItems.first()->position() > prevViewPos) {
2090                 moveForwardsBy = insertionResult->sizeChangesAfterVisiblePos;
2091                 moveBackwardsBy = removalResult->sizeChangesAfterVisiblePos;
2092             } else if (visibleItems.first()->position() < prevViewPos) {
2093                 moveForwardsBy = removalResult->sizeChangesBeforeVisiblePos;
2094                 moveBackwardsBy = insertionResult->sizeChangesBeforeVisiblePos;
2095             }
2096             adjustFirstItem(moveForwardsBy, moveBackwardsBy, insertionResult->countChangeBeforeVisible - removalResult->countChangeBeforeVisible);
2097         }
2098         insertionResult->reset();
2099         removalResult->reset();
2100     }
2101 }
2102
2103 void QQuickItemViewPrivate::createTransitioner()
2104 {
2105     if (!transitioner) {
2106         transitioner = new QQuickItemViewTransitioner;
2107         transitioner->setChangeListener(this);
2108     }
2109 }
2110
2111 void QQuickItemViewPrivate::prepareVisibleItemTransitions()
2112 {
2113     Q_Q(QQuickItemView);
2114     if (!transitioner)
2115         return;
2116
2117     // must call for every visible item to init or discard transitions
2118     QRectF viewBounds(0, position(), q->width(), q->height());
2119     for (int i=0; i<visibleItems.count(); i++)
2120         visibleItems[i]->prepareTransition(transitioner, viewBounds);
2121 }
2122
2123 void QQuickItemViewPrivate::prepareRemoveTransitions(QHash<QQuickChangeSet::MoveKey, FxViewItem *> *removedItems)
2124 {
2125     if (!transitioner)
2126         return;
2127
2128     if (transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, true)
2129             || transitioner->canTransition(QQuickItemViewTransitioner::RemoveTransition, false)) {
2130         for (QHash<QQuickChangeSet::MoveKey, FxViewItem *>::Iterator it = removedItems->begin();
2131              it != removedItems->end(); ) {
2132             bool isRemove = it.key().moveId < 0;
2133             if (isRemove) {
2134                 FxViewItem *item = *it;
2135                 item->releaseAfterTransition = true;
2136                 releasePendingTransition.append(item);
2137                 item->transitionNextReposition(transitioner, QQuickItemViewTransitioner::RemoveTransition, true);
2138                 it = removedItems->erase(it);
2139             } else {
2140                 ++it;
2141             }
2142         }
2143     }
2144 }
2145
2146 bool QQuickItemViewPrivate::prepareNonVisibleItemTransition(FxViewItem *item, const QRectF &viewBounds)
2147 {
2148     // Called for items that have been removed from visibleItems and may now be
2149     // transitioned out of the view. This applies to items that are being directly
2150     // removed, or moved to outside of the view, as well as those that are
2151     // displaced to a position outside of the view due to an insert or move.
2152
2153     if (!transitioner)
2154         return false;
2155
2156     if (item->scheduledTransitionType() == QQuickItemViewTransitioner::MoveTransition)
2157         repositionItemAt(item, item->index, 0);
2158
2159     if (item->prepareTransition(transitioner, viewBounds)) {
2160         item->releaseAfterTransition = true;
2161         return true;
2162     }
2163     return false;
2164 }
2165
2166 void QQuickItemViewPrivate::viewItemTransitionFinished(QQuickItemViewTransitionableItem *item)
2167 {
2168     for (int i=0; i<releasePendingTransition.count(); i++) {
2169         if (releasePendingTransition[i]->transitionableItem == item) {
2170             releaseItem(releasePendingTransition.takeAt(i));
2171             return;
2172         }
2173     }
2174 }
2175
2176 /*
2177   This may return 0 if the item is being created asynchronously.
2178   When the item becomes available, refill() will be called and the item
2179   will be returned on the next call to createItem().
2180 */
2181 FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous)
2182 {
2183     Q_Q(QQuickItemView);
2184
2185     if (requestedIndex == modelIndex && asynchronous)
2186         return 0;
2187
2188     for (int i=0; i<releasePendingTransition.count(); i++) {
2189         if (releasePendingTransition[i]->index == modelIndex
2190                 && !releasePendingTransition[i]->isPendingRemoval()) {
2191             releasePendingTransition[i]->releaseAfterTransition = false;
2192             return releasePendingTransition.takeAt(i);
2193         }
2194     }
2195
2196     if (asynchronous)
2197         requestedIndex = modelIndex;
2198     inRequest = true;
2199
2200     if (QQuickItem *item = model->item(modelIndex, asynchronous)) {
2201         item->setParentItem(q->contentItem());
2202         if (requestedIndex == modelIndex)
2203             requestedIndex = -1;
2204         FxViewItem *viewItem = newViewItem(modelIndex, item);
2205         if (viewItem) {
2206             viewItem->index = modelIndex;
2207             // do other set up for the new item that should not happen
2208             // until after bindings are evaluated
2209             initializeViewItem(viewItem);
2210             unrequestedItems.remove(item);
2211         }
2212         inRequest = false;
2213         return viewItem;
2214     }
2215
2216     inRequest = false;
2217     return 0;
2218 }
2219
2220 void QQuickItemView::createdItem(int index, QQuickItem *item)
2221 {
2222     Q_D(QQuickItemView);
2223
2224     if (!d->inRequest) {
2225         d->unrequestedItems.insert(item, index);
2226         d->requestedIndex = -1;
2227         if (d->hasPendingChanges())
2228             d->layout();
2229         else
2230             d->refill();
2231         if (d->unrequestedItems.contains(item))
2232             d->repositionPackageItemAt(item, index);
2233         else if (index == d->currentIndex)
2234             d->updateCurrent(index);
2235     }
2236 }
2237
2238 void QQuickItemView::initItem(int, QQuickItem *item)
2239 {
2240     item->setZ(1);
2241     item->setParentItem(contentItem());
2242     QQuickItemPrivate::get(item)->setCulled(true);
2243 }
2244
2245 void QQuickItemView::destroyingItem(QQuickItem *item)
2246 {
2247     Q_D(QQuickItemView);
2248     d->unrequestedItems.remove(item);
2249 }
2250
2251 bool QQuickItemViewPrivate::releaseItem(FxViewItem *item)
2252 {
2253     Q_Q(QQuickItemView);
2254     if (!item || !model)
2255         return true;
2256     if (trackedItem == item)
2257         trackedItem = 0;
2258     QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item);
2259     itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
2260     QQuickVisualModel::ReleaseFlags flags = model->release(item->item);
2261     if (flags == 0) {
2262         // item was not destroyed, and we no longer reference it.
2263         QQuickItemPrivate::get(item->item)->setCulled(true);
2264         unrequestedItems.insert(item->item, model->indexOf(item->item, q));
2265     }
2266     delete item;
2267     return flags != QQuickVisualModel::Referenced;
2268 }
2269
2270 QQuickItem *QQuickItemViewPrivate::createHighlightItem()
2271 {
2272     return createComponentItem(highlightComponent, 0.0, true);
2273 }
2274
2275 QQuickItem *QQuickItemViewPrivate::createComponentItem(QQmlComponent *component, qreal zValue, bool createDefault)
2276 {
2277     Q_Q(QQuickItemView);
2278
2279     QQuickItem *item = 0;
2280     if (component) {
2281         QQmlContext *creationContext = component->creationContext();
2282         QQmlContext *context = new QQmlContext(
2283                 creationContext ? creationContext : qmlContext(q));
2284         QObject *nobj = component->beginCreate(context);
2285         if (nobj) {
2286             QQml_setParent_noEvent(context, nobj);
2287             item = qobject_cast<QQuickItem *>(nobj);
2288             if (!item)
2289                 delete nobj;
2290         } else {
2291             delete context;
2292         }
2293     } else if (createDefault) {
2294         item = new QQuickItem;
2295     }
2296     if (item) {
2297         item->setZ(zValue);
2298         QQml_setParent_noEvent(item, q->contentItem());
2299         item->setParentItem(q->contentItem());
2300     }
2301     if (component)
2302         component->completeCreate();
2303     return item;
2304 }
2305
2306 void QQuickItemViewPrivate::updateTrackedItem()
2307 {
2308     Q_Q(QQuickItemView);
2309     FxViewItem *item = currentItem;
2310     if (highlight)
2311         item = highlight;
2312     trackedItem = item;
2313
2314     if (trackedItem)
2315         q->trackedPositionChanged();
2316 }
2317
2318 void QQuickItemViewPrivate::updateUnrequestedIndexes()
2319 {
2320     Q_Q(QQuickItemView);
2321     for (QHash<QQuickItem*,int>::iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
2322         *it = model->indexOf(it.key(), q);
2323 }
2324
2325 void QQuickItemViewPrivate::updateUnrequestedPositions()
2326 {
2327     for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
2328         repositionPackageItemAt(it.key(), it.value());
2329 }
2330
2331 void QQuickItemViewPrivate::updateVisibleIndex()
2332 {
2333     visibleIndex = 0;
2334     for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end(); ++it) {
2335         if ((*it)->index != -1) {
2336             visibleIndex = (*it)->index;
2337             break;
2338         }
2339     }
2340 }
2341
2342 QT_END_NAMESPACE