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