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