Show header/footer if current index is set to first/last item or row
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsgitemview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgitemview_p_p.h"
43
44 QT_BEGIN_NAMESPACE
45
46
47 FxViewItem::FxViewItem(QSGItem *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 QSGItemView::QSGItemView(QSGFlickablePrivate &dd, QSGItem *parent)
62     : QSGFlickable(dd, parent)
63 {
64     Q_D(QSGItemView);
65     d->init();
66 }
67
68 QSGItemView::~QSGItemView()
69 {
70     Q_D(QSGItemView);
71     d->clear();
72     if (d->ownModel)
73         delete d->model;
74     delete d->header;
75     delete d->footer;
76 }
77
78
79 QSGItem *QSGItemView::currentItem() const
80 {
81     Q_D(const QSGItemView);
82     if (!d->currentItem)
83         return 0;
84     return d->currentItem->item;
85 }
86
87 QVariant QSGItemView::model() const
88 {
89     Q_D(const QSGItemView);
90     return d->modelVariant;
91 }
92
93 void QSGItemView::setModel(const QVariant &model)
94 {
95     Q_D(QSGItemView);
96     if (d->modelVariant == model)
97         return;
98     if (d->model) {
99         disconnect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
100         disconnect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
101         disconnect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
102         disconnect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
103         disconnect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
104         disconnect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
105         disconnect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*)));
106     }
107
108     QSGVisualModel *oldModel = d->model;
109
110     d->clear();
111     d->setPosition(d->contentStartPosition());
112     d->model = 0;
113     d->modelVariant = model;
114
115     QObject *object = qvariant_cast<QObject*>(model);
116     QSGVisualModel *vim = 0;
117     if (object && (vim = qobject_cast<QSGVisualModel *>(object))) {
118         if (d->ownModel) {
119             delete oldModel;
120             d->ownModel = false;
121         }
122         d->model = vim;
123     } else {
124         if (!d->ownModel) {
125             d->model = new QSGVisualDataModel(qmlContext(this), this);
126             d->ownModel = true;
127         } else {
128             d->model = oldModel;
129         }
130         if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
131             dataModel->setModel(model);
132     }
133
134     if (d->model) {
135         d->bufferMode = QSGItemViewPrivate::BufferBefore | QSGItemViewPrivate::BufferAfter;
136         if (isComponentComplete()) {
137             updateSections();
138             d->refill();
139             if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) {
140                 setCurrentIndex(0);
141             } else {
142                 d->moveReason = QSGItemViewPrivate::SetIndex;
143                 d->updateCurrent(d->currentIndex);
144                 if (d->highlight && d->currentItem) {
145                     if (d->autoHighlight)
146                         d->resetHighlightPosition();
147                     d->updateTrackedItem();
148                 }
149                 d->moveReason = QSGItemViewPrivate::Other;
150             }
151             d->updateViewport();
152         }
153         connect(d->model, SIGNAL(itemsInserted(int,int)), this, SLOT(itemsInserted(int,int)));
154         connect(d->model, SIGNAL(itemsRemoved(int,int)), this, SLOT(itemsRemoved(int,int)));
155         connect(d->model, SIGNAL(itemsMoved(int,int,int)), this, SLOT(itemsMoved(int,int,int)));
156         connect(d->model, SIGNAL(itemsChanged(int,int)), this, SLOT(itemsChanged(int,int)));
157         connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
158         connect(d->model, SIGNAL(createdItem(int,QSGItem*)), this, SLOT(createdItem(int,QSGItem*)));
159         connect(d->model, SIGNAL(destroyingItem(QSGItem*)), this, SLOT(destroyingItem(QSGItem*)));
160         emit countChanged();
161     }
162     emit modelChanged();
163 }
164
165 QDeclarativeComponent *QSGItemView::delegate() const
166 {
167     Q_D(const QSGItemView);
168     if (d->model) {
169         if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model))
170             return dataModel->delegate();
171     }
172
173     return 0;
174 }
175
176 void QSGItemView::setDelegate(QDeclarativeComponent *delegate)
177 {
178     Q_D(QSGItemView);
179     if (delegate == this->delegate())
180         return;
181     if (!d->ownModel) {
182         d->model = new QSGVisualDataModel(qmlContext(this));
183         d->ownModel = true;
184     }
185     if (QSGVisualDataModel *dataModel = qobject_cast<QSGVisualDataModel*>(d->model)) {
186         int oldCount = dataModel->count();
187         dataModel->setDelegate(delegate);
188         if (isComponentComplete()) {
189             for (int i = 0; i < d->visibleItems.count(); ++i)
190                 d->releaseItem(d->visibleItems.at(i));
191             d->visibleItems.clear();
192             d->releaseItem(d->currentItem);
193             d->currentItem = 0;
194             updateSections();
195             d->refill();
196             d->moveReason = QSGItemViewPrivate::SetIndex;
197             d->updateCurrent(d->currentIndex);
198             if (d->highlight && d->currentItem) {
199                 if (d->autoHighlight)
200                     d->resetHighlightPosition();
201                 d->updateTrackedItem();
202             }
203             d->moveReason = QSGItemViewPrivate::Other;
204             d->updateViewport();
205         }
206         if (oldCount != dataModel->count())
207             emit countChanged();
208     }
209     emit delegateChanged();
210 }
211
212
213 int QSGItemView::count() const
214 {
215     Q_D(const QSGItemView);
216     if (d->model)
217         return d->model->count();
218     return 0;
219 }
220
221 int QSGItemView::currentIndex() const
222 {
223     Q_D(const QSGItemView);
224     return d->currentIndex;
225 }
226
227 void QSGItemView::setCurrentIndex(int index)
228 {
229     Q_D(QSGItemView);
230     if (d->requestedIndex >= 0)  // currently creating item
231         return;
232     d->currentIndexCleared = (index == -1);
233     if (index == d->currentIndex)
234         return;
235     if (isComponentComplete() && d->isValid()) {
236         d->moveReason = QSGItemViewPrivate::SetIndex;
237         d->updateCurrent(index);
238     } else if (d->currentIndex != index) {
239         d->currentIndex = index;
240         emit currentIndexChanged();
241     }
242 }
243
244
245 bool QSGItemView::isWrapEnabled() const
246 {
247     Q_D(const QSGItemView);
248     return d->wrap;
249 }
250
251 void QSGItemView::setWrapEnabled(bool wrap)
252 {
253     Q_D(QSGItemView);
254     if (d->wrap == wrap)
255         return;
256     d->wrap = wrap;
257     emit keyNavigationWrapsChanged();
258 }
259
260 int QSGItemView::cacheBuffer() const
261 {
262     Q_D(const QSGItemView);
263     return d->buffer;
264 }
265
266 void QSGItemView::setCacheBuffer(int b)
267 {
268     Q_D(QSGItemView);
269     if (d->buffer != b) {
270         d->buffer = b;
271         if (isComponentComplete()) {
272             d->bufferMode = QSGItemViewPrivate::BufferBefore | QSGItemViewPrivate::BufferAfter;
273             d->refill();
274         }
275         emit cacheBufferChanged();
276     }
277 }
278
279
280 Qt::LayoutDirection QSGItemView::layoutDirection() const
281 {
282     Q_D(const QSGItemView);
283     return d->layoutDirection;
284 }
285
286 void QSGItemView::setLayoutDirection(Qt::LayoutDirection layoutDirection)
287 {
288     Q_D(QSGItemView);
289     if (d->layoutDirection != layoutDirection) {
290         d->layoutDirection = layoutDirection;
291         d->regenerate();
292         emit layoutDirectionChanged();
293         emit effectiveLayoutDirectionChanged();
294     }
295 }
296
297 Qt::LayoutDirection QSGItemView::effectiveLayoutDirection() const
298 {
299     Q_D(const QSGItemView);
300     if (d->effectiveLayoutMirror)
301         return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft;
302     else
303         return d->layoutDirection;
304 }
305
306
307 QDeclarativeComponent *QSGItemView::header() const
308 {
309     Q_D(const QSGItemView);
310     return d->headerComponent;
311 }
312
313 QSGItem *QSGItemView::headerItem() const
314 {
315     Q_D(const QSGItemView);
316     return d->header ? d->header->item : 0;
317 }
318
319 void QSGItemView::setHeader(QDeclarativeComponent *headerComponent)
320 {
321     Q_D(QSGItemView);
322     if (d->headerComponent != headerComponent) {
323         delete d->header;
324         d->header = 0;
325         d->headerComponent = headerComponent;
326
327         d->minExtentDirty = true;
328         d->maxExtentDirty = true;
329
330         if (isComponentComplete()) {
331             d->updateHeader();
332             d->updateFooter();
333             d->updateViewport();
334             d->fixupPosition();
335         } else {
336             emit headerItemChanged();
337         }
338         emit headerChanged();
339     }
340 }
341
342 QDeclarativeComponent *QSGItemView::footer() const
343 {
344     Q_D(const QSGItemView);
345     return d->footerComponent;
346 }
347
348 QSGItem *QSGItemView::footerItem() const
349 {
350     Q_D(const QSGItemView);
351     return d->footer ? d->footer->item : 0;
352 }
353
354 void QSGItemView::setFooter(QDeclarativeComponent *footerComponent)
355 {
356     Q_D(QSGItemView);
357     if (d->footerComponent != footerComponent) {
358         delete d->footer;
359         d->footer = 0;
360         d->footerComponent = footerComponent;
361
362         if (isComponentComplete()) {
363             d->updateFooter();
364             d->updateViewport();
365             d->fixupPosition();
366         } else {
367             emit footerItemChanged();
368         }
369         emit footerChanged();
370     }
371 }
372
373 QDeclarativeComponent *QSGItemView::highlight() const
374 {
375     Q_D(const QSGItemView);
376     return d->highlightComponent;
377 }
378
379 void QSGItemView::setHighlight(QDeclarativeComponent *highlightComponent)
380 {
381     Q_D(QSGItemView);
382     if (highlightComponent != d->highlightComponent) {
383         d->highlightComponent = highlightComponent;
384         d->createHighlight();
385         if (d->currentItem)
386             d->updateHighlight();
387         emit highlightChanged();
388     }
389 }
390
391 QSGItem *QSGItemView::highlightItem() const
392 {
393     Q_D(const QSGItemView);
394     if (!d->highlight)
395         return 0;
396     return d->highlight->item;
397 }
398
399 bool QSGItemView::highlightFollowsCurrentItem() const
400 {
401     Q_D(const QSGItemView);
402     return d->autoHighlight;
403 }
404
405 void QSGItemView::setHighlightFollowsCurrentItem(bool autoHighlight)
406 {
407     Q_D(QSGItemView);
408     if (d->autoHighlight != autoHighlight) {
409         d->autoHighlight = autoHighlight;
410         if (autoHighlight)
411             d->updateHighlight();
412         emit highlightFollowsCurrentItemChanged();
413     }
414 }
415
416 QSGItemView::HighlightRangeMode QSGItemView::highlightRangeMode() const
417 {
418     Q_D(const QSGItemView);
419     return static_cast<QSGItemView::HighlightRangeMode>(d->highlightRange);
420 }
421
422 void QSGItemView::setHighlightRangeMode(HighlightRangeMode mode)
423 {
424     Q_D(QSGItemView);
425     if (d->highlightRange == mode)
426         return;
427     d->highlightRange = mode;
428     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
429     emit highlightRangeModeChanged();
430 }
431
432 //###Possibly rename these properties, since they are very useful even without a highlight?
433 qreal QSGItemView::preferredHighlightBegin() const
434 {
435     Q_D(const QSGItemView);
436     return d->highlightRangeStart;
437 }
438
439 void QSGItemView::setPreferredHighlightBegin(qreal start)
440 {
441     Q_D(QSGItemView);
442     d->highlightRangeStartValid = true;
443     if (d->highlightRangeStart == start)
444         return;
445     d->highlightRangeStart = start;
446     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
447     emit preferredHighlightBeginChanged();
448 }
449
450 void QSGItemView::resetPreferredHighlightBegin()
451 {
452     Q_D(QSGItemView);
453     d->highlightRangeStartValid = false;
454     if (d->highlightRangeStart == 0)
455         return;
456     d->highlightRangeStart = 0;
457     emit preferredHighlightBeginChanged();
458 }
459
460 qreal QSGItemView::preferredHighlightEnd() const
461 {
462     Q_D(const QSGItemView);
463     return d->highlightRangeEnd;
464 }
465
466 void QSGItemView::setPreferredHighlightEnd(qreal end)
467 {
468     Q_D(QSGItemView);
469     d->highlightRangeEndValid = true;
470     if (d->highlightRangeEnd == end)
471         return;
472     d->highlightRangeEnd = end;
473     d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
474     emit preferredHighlightEndChanged();
475 }
476
477 void QSGItemView::resetPreferredHighlightEnd()
478 {
479     Q_D(QSGItemView);
480     d->highlightRangeEndValid = false;
481     if (d->highlightRangeEnd == 0)
482         return;
483     d->highlightRangeEnd = 0;
484     emit preferredHighlightEndChanged();
485 }
486
487 int QSGItemView::highlightMoveDuration() const
488 {
489     Q_D(const QSGItemView);
490     return d->highlightMoveDuration;
491 }
492
493 void QSGItemView::setHighlightMoveDuration(int duration)
494 {
495     Q_D(QSGItemView);
496     if (d->highlightMoveDuration != duration) {
497         d->highlightMoveDuration = duration;
498         emit highlightMoveDurationChanged();
499     }
500 }
501
502 void QSGItemViewPrivate::positionViewAtIndex(int index, int mode)
503 {
504     Q_Q(QSGItemView);
505     if (!isValid())
506         return;
507     if (mode < QSGItemView::Beginning || mode > QSGItemView::Contain)
508         return;
509     int idx = qMax(qMin(index, model->count()-1), 0);
510
511     if (layoutScheduled)
512         layout();
513     qreal pos = isContentFlowReversed() ? -position() - size() : position();
514     FxViewItem *item = visibleItem(idx);
515     qreal maxExtent;
516     if (layoutOrientation() == Qt::Vertical)
517         maxExtent = -q->maxYExtent();
518     else
519         maxExtent = isContentFlowReversed() ? q->minXExtent()-size(): -q->maxXExtent();
520     if (!item) {
521         int itemPos = positionAt(idx);
522         changedVisibleIndex(idx);
523         // save the currently visible items in case any of them end up visible again
524         QList<FxViewItem *> oldVisible = visibleItems;
525         visibleItems.clear();
526         setPosition(qMin(qreal(itemPos), maxExtent));
527         // now release the reference to all the old visible items.
528         for (int i = 0; i < oldVisible.count(); ++i)
529             releaseItem(oldVisible.at(i));
530         item = visibleItem(idx);
531     }
532     if (item) {
533         const qreal itemPos = item->position();
534         switch (mode) {
535         case QSGItemView::Beginning:
536             pos = itemPos;
537             if (index < 0 && header)
538                 pos -= headerSize();
539             break;
540         case QSGItemView::Center:
541             pos = itemPos - (size() - item->size())/2;
542             break;
543         case QSGItemView::End:
544             pos = itemPos - size() + item->size();
545             if (index >= model->count() && footer)
546                 pos += footerSize();
547             break;
548         case QSGItemView::Visible:
549             if (itemPos > pos + size())
550                 pos = itemPos - size() + item->size();
551             else if (item->endPosition() <= pos)
552                 pos = itemPos;
553             break;
554         case QSGItemView::Contain:
555             if (item->endPosition() >= pos + size())
556                 pos = itemPos - size() + item->size();
557             if (itemPos < pos)
558                 pos = itemPos;
559         }
560         pos = qMin(pos, maxExtent);
561         qreal minExtent;
562         if (layoutOrientation() == Qt::Vertical)
563             minExtent = -q->minYExtent();
564         else
565             minExtent = isContentFlowReversed() ? q->maxXExtent()-size(): -q->minXExtent();
566         pos = qMax(pos, minExtent);
567         moveReason = QSGItemViewPrivate::Other;
568         q->cancelFlick();
569         setPosition(pos);
570
571         if (highlight) {
572             if (autoHighlight)
573                 resetHighlightPosition();
574             updateHighlight();
575         }
576     }
577     fixupPosition();
578 }
579
580 void QSGItemView::positionViewAtIndex(int index, int mode)
581 {
582     Q_D(QSGItemView);
583     if (!d->isValid() || index < 0 || index >= d->model->count())
584         return;
585     d->positionViewAtIndex(index, mode);
586 }
587
588
589 void QSGItemView::positionViewAtBeginning()
590 {
591     Q_D(QSGItemView);
592     if (!d->isValid())
593         return;
594     d->positionViewAtIndex(-1, Beginning);
595 }
596
597 void QSGItemView::positionViewAtEnd()
598 {
599     Q_D(QSGItemView);
600     if (!d->isValid())
601         return;
602     d->positionViewAtIndex(d->model->count(), End);
603 }
604
605 int QSGItemView::indexAt(qreal x, qreal y) const
606 {
607     Q_D(const QSGItemView);
608     for (int i = 0; i < d->visibleItems.count(); ++i) {
609         const FxViewItem *item = d->visibleItems.at(i);
610         if (item->contains(x, y))
611             return item->index;
612     }
613
614     return -1;
615 }
616
617
618 // for debugging only
619 void QSGItemViewPrivate::checkVisible() const
620 {
621     int skip = 0;
622     for (int i = 0; i < visibleItems.count(); ++i) {
623         FxViewItem *item = visibleItems.at(i);
624         if (item->index == -1) {
625             ++skip;
626         } else if (item->index != visibleIndex + i - skip) {
627             qFatal("index %d %d %d", visibleIndex, i, item->index);
628         }
629     }
630 }
631
632
633
634 void QSGItemViewPrivate::itemGeometryChanged(QSGItem *item, const QRectF &newGeometry, const QRectF &oldGeometry)
635 {
636     Q_Q(QSGItemView);
637     QSGFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry);
638     if (!q->isComponentComplete())
639         return;
640
641     if (header && header->item == item)
642         updateHeader();
643     else if (footer && footer->item == item)
644         updateFooter();
645
646     if (currentItem && currentItem->item == item)
647         updateHighlight();
648     if (trackedItem && trackedItem->item == item)
649         q->trackedPositionChanged();
650 }
651
652 void QSGItemView::destroyRemoved()
653 {
654     Q_D(QSGItemView);
655     for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin();
656             it != d->visibleItems.end();) {
657         FxViewItem *item = *it;
658         if (item->index == -1 && item->attached->delayRemove() == false) {
659             d->releaseItem(item);
660             it = d->visibleItems.erase(it);
661         } else {
662             ++it;
663         }
664     }
665
666     // Correct the positioning of the items
667     d->updateSections();
668     d->layout();
669 }
670
671 void QSGItemView::itemsChanged(int, int)
672 {
673     Q_D(QSGItemView);
674     d->updateSections();
675     d->layout();
676 }
677
678 void QSGItemView::modelReset()
679 {
680     Q_D(QSGItemView);
681     d->moveReason = QSGItemViewPrivate::SetIndex;
682     d->regenerate();
683     if (d->highlight && d->currentItem) {
684         if (d->autoHighlight)
685             d->resetHighlightPosition();
686         d->updateTrackedItem();
687     }
688     d->moveReason = QSGItemViewPrivate::Other;
689
690     emit countChanged();
691 }
692
693 void QSGItemView::createdItem(int index, QSGItem *item)
694 {
695     Q_D(QSGItemView);
696     if (d->requestedIndex != index) {
697         item->setParentItem(contentItem());
698         d->unrequestedItems.insert(item, index);
699         d->repositionPackageItemAt(item, index);
700     }
701 }
702
703 void QSGItemView::destroyingItem(QSGItem *item)
704 {
705     Q_D(QSGItemView);
706     d->unrequestedItems.remove(item);
707 }
708
709 void QSGItemView::animStopped()
710 {
711     Q_D(QSGItemView);
712     d->bufferMode = QSGItemViewPrivate::NoBuffer;
713     if (d->haveHighlightRange && d->highlightRange == QSGItemView::StrictlyEnforceRange)
714         d->updateHighlight();
715 }
716
717
718 void QSGItemView::trackedPositionChanged()
719 {
720     Q_D(QSGItemView);
721     if (!d->trackedItem || !d->currentItem)
722         return;
723     if (d->moveReason == QSGItemViewPrivate::SetIndex) {
724         qreal trackedPos = d->trackedItem->position();
725         qreal trackedSize = d->trackedItem->size();
726         if (d->trackedItem != d->currentItem) {
727             trackedSize += d->currentItem->sectionSize();
728         }
729         qreal viewPos;
730         qreal highlightStart;
731         qreal highlightEnd;
732         if (d->isContentFlowReversed()) {
733             viewPos = -d->position()-d->size();
734             highlightStart = d->highlightRangeStartValid ? d->size()-d->highlightRangeEnd : d->highlightRangeStart;
735             highlightEnd = d->highlightRangeEndValid ? d->size()-d->highlightRangeStart : d->highlightRangeEnd;
736         } else {
737             viewPos = d->position();
738             highlightStart = d->highlightRangeStart;
739             highlightEnd = d->highlightRangeEnd;
740         }
741         qreal pos = viewPos;
742         if (d->haveHighlightRange) {
743             if (d->highlightRange == StrictlyEnforceRange) {
744                 if (trackedPos > pos + highlightEnd - d->trackedItem->size())
745                     pos = trackedPos - highlightEnd + d->trackedItem->size();
746                 if (trackedPos < pos + highlightStart)
747                     pos = trackedPos - highlightStart;
748             } else {
749                 if (trackedPos > pos + highlightEnd - trackedSize)
750                     pos = trackedPos - highlightEnd + trackedSize;
751                 if (trackedPos < pos + highlightStart)
752                     pos = trackedPos - highlightStart;
753                 if (pos > d->endPosition() - d->size())
754                     pos = d->endPosition() - d->size();
755                 if (pos < d->startPosition())
756                     pos = d->startPosition();
757             }
758         } else {
759             qreal trackedEndPos = d->trackedItem->endPosition();
760             qreal toItemPos = d->currentItem->position();
761             qreal toItemEndPos = d->currentItem->endPosition();
762
763             if (d->header && d->showHeaderForIndex(d->currentIndex)) {
764                 trackedPos -= d->headerSize();
765                 trackedEndPos -= d->headerSize();
766                 toItemPos -= d->headerSize();
767                 toItemEndPos -= d->headerSize();
768             } else if (d->footer && d->showFooterForIndex(d->currentIndex)) {
769                 trackedPos += d->footerSize();
770                 trackedEndPos += d->footerSize();
771                 toItemPos += d->footerSize();
772                 toItemEndPos += d->footerSize();
773             }
774
775             if (trackedPos < viewPos && toItemPos < viewPos) {
776                 pos = qMax(trackedPos, toItemPos);
777             } else if (trackedEndPos >= viewPos + d->size()
778                 && toItemEndPos >= viewPos + d->size()) {
779                 if (trackedEndPos <= toItemEndPos) {
780                     pos = trackedEndPos - d->size();
781                     if (trackedSize > d->size())
782                         pos = trackedPos;
783                 } else {
784                     pos = toItemEndPos - d->size();
785                     if (d->currentItem->size() > d->size())
786                         pos = d->currentItem->position();
787                 }
788             }
789         }
790         if (viewPos != pos) {
791             cancelFlick();
792             d->calcVelocity = true;
793             d->setPosition(pos);
794             d->calcVelocity = false;
795         }
796     }
797 }
798
799
800 void QSGItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
801 {
802     Q_D(QSGItemView);
803     d->maxExtentDirty = true;
804     d->minExtentDirty = true;
805     QSGFlickable::geometryChanged(newGeometry, oldGeometry);
806 }
807
808
809 qreal QSGItemView::minYExtent() const
810 {
811     Q_D(const QSGItemView);
812     if (d->layoutOrientation() == Qt::Horizontal)
813         return QSGFlickable::minYExtent();
814
815     if (d->minExtentDirty) {
816         d->minExtent = -d->startPosition();
817         if (d->header && d->visibleItems.count())
818             d->minExtent += d->headerSize();
819         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
820             d->minExtent += d->highlightRangeStart;
821             if (d->visibleItem(0))
822                 d->minExtent -= d->visibleItem(0)->sectionSize();
823             d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd));
824         }
825         d->minExtentDirty = false;
826     }
827
828     return d->minExtent;
829 }
830
831 qreal QSGItemView::maxYExtent() const
832 {
833     Q_D(const QSGItemView);
834     if (d->layoutOrientation() == Qt::Horizontal)
835         return height();
836
837     if (d->maxExtentDirty) {
838         if (!d->model || !d->model->count()) {
839             d->maxExtent = d->header ? -d->headerSize() : 0;
840             d->maxExtent += height();
841         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
842             d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
843             if (d->highlightRangeEnd != d->highlightRangeStart)
844                 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd));
845         } else {
846             d->maxExtent = -(d->endPosition() - height());
847         }
848
849         if (d->footer)
850             d->maxExtent -= d->footerSize();
851         qreal minY = minYExtent();
852         if (d->maxExtent > minY)
853             d->maxExtent = minY;
854         d->maxExtentDirty = false;
855     }
856     return d->maxExtent;
857 }
858
859 qreal QSGItemView::minXExtent() const
860 {
861     Q_D(const QSGItemView);
862     if (d->layoutOrientation() == Qt::Vertical)
863         return QSGFlickable::minXExtent();
864
865     if (d->minExtentDirty) {
866         d->minExtent = -d->startPosition();
867         qreal highlightStart;
868         qreal highlightEnd;
869         qreal endPositionFirstItem = 0;
870         if (d->isContentFlowReversed()) {
871             if (d->model && d->model->count())
872                 endPositionFirstItem = d->positionAt(d->model->count()-1);
873             else if (d->header)
874                 d->minExtent += d->headerSize();
875             highlightStart = d->highlightRangeStartValid
876                     ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
877                     : d->size() - (d->lastPosition()-endPositionFirstItem);
878             highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
879             if (d->footer)
880                 d->minExtent += d->footerSize();
881             qreal maxX = maxXExtent();
882             if (d->minExtent < maxX)
883                 d->minExtent = maxX;
884         } else {
885             endPositionFirstItem = d->endPositionAt(0);
886             highlightStart = d->highlightRangeStart;
887             highlightEnd = d->highlightRangeEnd;
888             if (d->header && d->visibleItems.count())
889                 d->minExtent += d->headerSize();
890         }
891         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
892             d->minExtent += highlightStart;
893             d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd));
894         }
895         d->minExtentDirty = false;
896     }
897
898     return d->minExtent;
899 }
900
901 qreal QSGItemView::maxXExtent() const
902 {
903     Q_D(const QSGItemView);
904     if (d->layoutOrientation() == Qt::Vertical)
905         return width();
906
907     if (d->maxExtentDirty) {
908         qreal highlightStart;
909         qreal highlightEnd;
910         qreal lastItemPosition = 0;
911         d->maxExtent = 0;
912         if (d->isContentFlowReversed()) {
913             highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
914             highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
915             lastItemPosition = d->endPosition();
916         } else {
917             highlightStart = d->highlightRangeStart;
918             highlightEnd = d->highlightRangeEnd;
919             if (d->model && d->model->count())
920                 lastItemPosition = d->positionAt(d->model->count()-1);
921         }
922         if (!d->model || !d->model->count()) {
923             if (!d->isContentFlowReversed())
924                 d->maxExtent = d->header ? -d->headerSize() : 0;
925             d->maxExtent += width();
926         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
927             d->maxExtent = -(lastItemPosition - highlightStart);
928             if (highlightEnd != highlightStart) {
929                 d->maxExtent = d->isContentFlowReversed()
930                         ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd))
931                         : qMin(d->maxExtent, -(d->endPosition() - highlightEnd));
932             }
933         } else {
934             d->maxExtent = -(d->endPosition() - width());
935         }
936         if (d->isContentFlowReversed()) {
937             if (d->header && d->visibleItems.count())
938                 d->maxExtent -= d->headerSize();
939         } else {
940             if (d->footer)
941                 d->maxExtent -= d->footerSize();
942             qreal minX = minXExtent();
943             if (d->maxExtent > minX)
944                 d->maxExtent = minX;
945         }
946         d->maxExtentDirty = false;
947     }
948
949     return d->maxExtent;
950 }
951
952 void QSGItemView::setContentX(qreal pos)
953 {
954     Q_D(QSGItemView);
955     // Positioning the view manually should override any current movement state
956     d->moveReason = QSGItemViewPrivate::Other;
957     QSGFlickable::setContentX(pos);
958 }
959
960 void QSGItemView::setContentY(qreal pos)
961 {
962     Q_D(QSGItemView);
963     // Positioning the view manually should override any current movement state
964     d->moveReason = QSGItemViewPrivate::Other;
965     QSGFlickable::setContentY(pos);
966 }
967
968
969 void QSGItemView::updatePolish()
970 {
971     Q_D(QSGItemView);
972     QSGFlickable::updatePolish();
973     d->layout();
974 }
975
976 void QSGItemView::componentComplete()
977 {
978     Q_D(QSGItemView);
979     QSGFlickable::componentComplete();
980
981     updateSections();
982     d->updateHeader();
983     d->updateFooter();
984     d->updateViewport();
985     d->setPosition(d->contentStartPosition());
986     if (d->isValid()) {
987         d->refill();
988         d->moveReason = QSGItemViewPrivate::SetIndex;
989         if (d->currentIndex < 0 && !d->currentIndexCleared)
990             d->updateCurrent(0);
991         else
992             d->updateCurrent(d->currentIndex);
993         if (d->highlight && d->currentItem) {
994             if (d->autoHighlight)
995                 d->resetHighlightPosition();
996             d->updateTrackedItem();
997         }
998         d->moveReason = QSGItemViewPrivate::Other;
999         d->fixupPosition();
1000     }
1001 }
1002
1003
1004
1005 QSGItemViewPrivate::QSGItemViewPrivate()
1006     : itemCount(0)
1007     , buffer(0), bufferMode(BufferBefore | BufferAfter)
1008     , layoutDirection(Qt::LeftToRight)
1009     , moveReason(Other)
1010     , visibleIndex(0)
1011     , currentIndex(-1), currentItem(0)
1012     , trackedItem(0), requestedIndex(-1)
1013     , highlightComponent(0), highlight(0)
1014     , highlightRange(QSGItemView::NoHighlightRange)
1015     , highlightRangeStart(0), highlightRangeEnd(0)
1016     , highlightMoveDuration(150)
1017     , headerComponent(0), header(0), footerComponent(0), footer(0)
1018     , minExtent(0), maxExtent(0)
1019     , ownModel(false), wrap(false), lazyRelease(false), deferredRelease(false)
1020     , layoutScheduled(false), inViewportMoved(false), currentIndexCleared(false)
1021     , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
1022     , minExtentDirty(true), maxExtentDirty(true)
1023 {
1024 }
1025
1026 bool QSGItemViewPrivate::isValid() const
1027 {
1028     return model && model->count() && model->isValid();
1029 }
1030
1031 qreal QSGItemViewPrivate::position() const
1032 {
1033     Q_Q(const QSGItemView);
1034     return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX();
1035 }
1036
1037 qreal QSGItemViewPrivate::size() const
1038 {
1039     Q_Q(const QSGItemView);
1040     return layoutOrientation() == Qt::Vertical ? q->height() : q->width();
1041 }
1042
1043 qreal QSGItemViewPrivate::startPosition() const
1044 {
1045     return isContentFlowReversed() ? -lastPosition() : originPosition();
1046 }
1047
1048 qreal QSGItemViewPrivate::endPosition() const
1049 {
1050     return isContentFlowReversed() ? -originPosition() : lastPosition();
1051 }
1052
1053 qreal QSGItemViewPrivate::contentStartPosition() const
1054 {
1055     return -headerSize();
1056 }
1057
1058 int QSGItemViewPrivate::findLastVisibleIndex(int defaultValue) const
1059 {
1060     if (visibleItems.count()) {
1061         int i = visibleItems.count() - 1;
1062         while (i > 0 && visibleItems.at(i)->index == -1)
1063             --i;
1064         if (visibleItems.at(i)->index != -1)
1065             return visibleItems.at(i)->index;
1066     }
1067     return defaultValue;
1068 }
1069
1070 FxViewItem *QSGItemViewPrivate::visibleItem(int modelIndex) const {
1071     if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
1072         for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
1073             FxViewItem *item = visibleItems.at(i);
1074             if (item->index == modelIndex)
1075                 return item;
1076         }
1077     }
1078     return 0;
1079 }
1080
1081 FxViewItem *QSGItemViewPrivate::firstVisibleItem() const {
1082     const qreal pos = isContentFlowReversed() ? -position()-size() : position();
1083     for (int i = 0; i < visibleItems.count(); ++i) {
1084         FxViewItem *item = visibleItems.at(i);
1085         if (item->index != -1 && item->endPosition() >= pos)
1086             return item;
1087     }
1088     return visibleItems.count() ? visibleItems.first() : 0;
1089 }
1090
1091 // Map a model index to visibleItems list index.
1092 // These may differ if removed items are still present in the visible list,
1093 // e.g. doing a removal animation
1094 int QSGItemViewPrivate::mapFromModel(int modelIndex) const
1095 {
1096     if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
1097         return -1;
1098     for (int i = 0; i < visibleItems.count(); ++i) {
1099         FxViewItem *item = visibleItems.at(i);
1100         if (item->index == modelIndex)
1101             return i + visibleIndex;
1102         if (item->index > modelIndex)
1103             return -1;
1104     }
1105     return -1; // Not in visibleList
1106 }
1107
1108 void QSGItemViewPrivate::adjustMoveParameters(int *from, int *to, int *count) const
1109 {
1110     if (*from > *to) {
1111         // Only move forwards - flip if backwards moving
1112         int tfrom = *from;
1113         int tto = *to;
1114         *from = tto;
1115         *to = tto + *count;
1116         *count = tfrom - tto;
1117     }
1118 }
1119
1120 void QSGItemViewPrivate::init()
1121 {
1122     Q_Q(QSGItemView);
1123     QSGItemPrivate::get(contentItem)->childrenDoNotOverlap = true;
1124     q->setFlag(QSGItem::ItemIsFocusScope);
1125     addItemChangeListener(this, Geometry);
1126     QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
1127     q->setFlickableDirection(QSGFlickable::VerticalFlick);
1128 }
1129
1130 void QSGItemViewPrivate::updateCurrent(int modelIndex)
1131 {
1132     Q_Q(QSGItemView);
1133     if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1134         if (currentItem) {
1135             currentItem->attached->setIsCurrentItem(false);
1136             releaseItem(currentItem);
1137             currentItem = 0;
1138             currentIndex = modelIndex;
1139             emit q->currentIndexChanged();
1140             updateHighlight();
1141         } else if (currentIndex != modelIndex) {
1142             currentIndex = modelIndex;
1143             emit q->currentIndexChanged();
1144         }
1145         return;
1146     }
1147
1148     if (currentItem && currentIndex == modelIndex) {
1149         updateHighlight();
1150         return;
1151     }
1152
1153     FxViewItem *oldCurrentItem = currentItem;
1154     currentIndex = modelIndex;
1155     currentItem = createItem(modelIndex);
1156     if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1157         oldCurrentItem->attached->setIsCurrentItem(false);
1158     if (currentItem) {
1159         currentItem->item->setFocus(true);
1160         currentItem->attached->setIsCurrentItem(true);
1161         initializeCurrentItem();
1162     }
1163
1164     updateHighlight();
1165     emit q->currentIndexChanged();
1166     releaseItem(oldCurrentItem);
1167 }
1168
1169 void QSGItemViewPrivate::clear()
1170 {
1171     timeline.clear();
1172
1173     for (int i = 0; i < visibleItems.count(); ++i)
1174         releaseItem(visibleItems.at(i));
1175     visibleItems.clear();
1176     visibleIndex = 0;
1177
1178     releaseItem(currentItem);
1179     currentItem = 0;
1180     createHighlight();
1181     trackedItem = 0;
1182
1183     minExtentDirty = true;
1184     maxExtentDirty = true;
1185     itemCount = 0;
1186 }
1187
1188
1189 void QSGItemViewPrivate::mirrorChange()
1190 {
1191     Q_Q(QSGItemView);
1192     regenerate();
1193     emit q->effectiveLayoutDirectionChanged();
1194 }
1195
1196 void QSGItemViewPrivate::refill()
1197 {
1198     if (isContentFlowReversed())
1199         refill(-position()-size(), -position());
1200     else
1201         refill(position(), position()+size());
1202 }
1203
1204 void QSGItemViewPrivate::refill(qreal from, qreal to, bool doBuffer)
1205 {
1206     Q_Q(QSGItemView);
1207     if (!isValid() || !q->isComponentComplete())
1208         return;
1209
1210     itemCount = model->count();
1211     qreal bufferFrom = from - buffer;
1212     qreal bufferTo = to + buffer;
1213     qreal fillFrom = from;
1214     qreal fillTo = to;
1215     if (doBuffer && (bufferMode & BufferAfter))
1216         fillTo = bufferTo;
1217     if (doBuffer && (bufferMode & BufferBefore))
1218         fillFrom = bufferFrom;
1219
1220     // Item creation and release is staggered in order to avoid
1221     // creating/releasing multiple items in one frame
1222     // while flicking (as much as possible).
1223
1224     bool changed = addVisibleItems(fillFrom, fillTo, doBuffer);
1225
1226     if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create
1227         if (removeNonVisibleItems(bufferFrom, bufferTo))
1228             changed = true;
1229         deferredRelease = false;
1230     } else {
1231         deferredRelease = true;
1232     }
1233
1234     if (changed) {
1235         minExtentDirty = true;
1236         maxExtentDirty = true;
1237         visibleItemsChanged();
1238     } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
1239         refill(from, to, true);
1240     }
1241
1242     lazyRelease = false;
1243 }
1244
1245 void QSGItemViewPrivate::regenerate()
1246 {
1247     Q_Q(QSGItemView);
1248     if (q->isComponentComplete()) {
1249         delete header;
1250         header = 0;
1251         delete footer;
1252         footer = 0;
1253         updateHeader();
1254         updateFooter();
1255         clear();
1256         updateViewport();
1257         setPosition(contentStartPosition());
1258         refill();
1259         updateCurrent(currentIndex);
1260     }
1261 }
1262
1263 void QSGItemViewPrivate::scheduleLayout()
1264 {
1265     Q_Q(QSGItemView);
1266     if (!layoutScheduled) {
1267         layoutScheduled = true;
1268         q->polish();
1269     }
1270 }
1271
1272 void QSGItemViewPrivate::updateViewport()
1273 {
1274     Q_Q(QSGItemView);
1275     if (isValid()) {
1276         if (layoutOrientation() == Qt::Vertical)
1277             q->setContentHeight(endPosition() - startPosition());
1278         else
1279             q->setContentWidth(endPosition() - startPosition());
1280     }
1281 }
1282
1283 void QSGItemViewPrivate::layout()
1284 {
1285     Q_Q(QSGItemView);
1286     layoutScheduled = false;
1287     if (!isValid() && !visibleItems.count()) {
1288         clear();
1289         setPosition(contentStartPosition());
1290         return;
1291     }
1292
1293     layoutVisibleItems();
1294     refill();
1295
1296     minExtentDirty = true;
1297     maxExtentDirty = true;
1298
1299     updateHighlight();
1300     if (!q->isMoving() && !q->isFlicking()) {
1301         fixupPosition();
1302         refill();
1303     }
1304
1305     updateHeader();
1306     updateFooter();
1307     updateViewport();
1308     updateUnrequestedPositions();
1309 }
1310
1311 FxViewItem *QSGItemViewPrivate::createItem(int modelIndex)
1312 {
1313     Q_Q(QSGItemView);
1314
1315     requestedIndex = modelIndex;
1316     FxViewItem *viewItem = 0;
1317
1318     if (QSGItem *item = model->item(modelIndex, false)) {
1319         viewItem = newViewItem(modelIndex, item);
1320         if (viewItem) {
1321             viewItem->index = modelIndex;
1322             if (model->completePending()) {
1323                 // complete
1324                 viewItem->item->setZ(1);
1325                 viewItem->item->setParentItem(q->contentItem());
1326                 model->completeItem();
1327             } else {
1328                 viewItem->item->setParentItem(q->contentItem());
1329             }
1330             // do other set up for the new item that should not happen
1331             // until after bindings are evaluated
1332             initializeViewItem(viewItem);
1333
1334             unrequestedItems.remove(viewItem->item);
1335         }
1336     }
1337     requestedIndex = -1;
1338     return viewItem;
1339 }
1340
1341
1342 void QSGItemViewPrivate::releaseItem(FxViewItem *item)
1343 {
1344     Q_Q(QSGItemView);
1345     if (!item || !model)
1346         return;
1347     if (trackedItem == item)
1348         trackedItem = 0;
1349     QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item->item);
1350     itemPrivate->removeItemChangeListener(this, QSGItemPrivate::Geometry);
1351     if (model->release(item->item) == 0) {
1352         // item was not destroyed, and we no longer reference it.
1353         unrequestedItems.insert(item->item, model->indexOf(item->item, q));
1354     }
1355     delete item;
1356 }
1357
1358 QSGItem *QSGItemViewPrivate::createHighlightItem()
1359 {
1360     return createComponentItem(highlightComponent, true, true);
1361 }
1362
1363 QSGItem *QSGItemViewPrivate::createComponentItem(QDeclarativeComponent *component, bool receiveItemGeometryChanges, bool createDefault)
1364 {
1365     Q_Q(QSGItemView);
1366
1367     QSGItem *item = 0;
1368     if (component) {
1369         QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1370         QObject *nobj = component->create(context);
1371         if (nobj) {
1372             QDeclarative_setParent_noEvent(context, nobj);
1373             item = qobject_cast<QSGItem *>(nobj);
1374             if (!item)
1375                 delete nobj;
1376         } else {
1377             delete context;
1378         }
1379     } else if (createDefault) {
1380         item = new QSGItem;
1381     }
1382     if (item) {
1383         QDeclarative_setParent_noEvent(item, q->contentItem());
1384         item->setParentItem(q->contentItem());
1385         if (receiveItemGeometryChanges) {
1386             QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1387             itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
1388         }
1389     }
1390     return item;
1391 }
1392
1393 void QSGItemViewPrivate::updateTrackedItem()
1394 {
1395     Q_Q(QSGItemView);
1396     FxViewItem *item = currentItem;
1397     if (highlight)
1398         item = highlight;
1399     trackedItem = item;
1400
1401     if (trackedItem)
1402         q->trackedPositionChanged();
1403 }
1404
1405 void QSGItemViewPrivate::updateUnrequestedIndexes()
1406 {
1407     Q_Q(QSGItemView);
1408     for (QHash<QSGItem*,int>::iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
1409         *it = model->indexOf(it.key(), q);
1410 }
1411
1412 void QSGItemViewPrivate::updateUnrequestedPositions()
1413 {
1414     for (QHash<QSGItem*,int>::const_iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
1415         repositionPackageItemAt(it.key(), it.value());
1416 }
1417
1418 QT_END_NAMESPACE