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