402bbbf414ac712a67b28b29aa94de77e88e3a00
[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             if (trackedPos < viewPos && d->currentItem->position() < viewPos) {
760                 pos = qMax(trackedPos, d->currentItem->position());
761             } else if (d->trackedItem->endPosition() >= viewPos + d->size()
762                 && d->currentItem->endPosition() >= viewPos + d->size()) {
763                 if (d->trackedItem->endPosition() <= d->currentItem->endPosition()) {
764                     pos = d->trackedItem->endPosition() - d->size();
765                     if (trackedSize > d->size())
766                         pos = trackedPos;
767                 } else {
768                     pos = d->currentItem->endPosition() - d->size();
769                     if (d->currentItem->size() > d->size())
770                         pos = d->currentItem->position();
771                 }
772             }
773         }
774         if (viewPos != pos) {
775             cancelFlick();
776             d->calcVelocity = true;
777             d->setPosition(pos);
778             d->calcVelocity = false;
779         }
780     }
781 }
782
783
784 void QSGItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
785 {
786     Q_D(QSGItemView);
787     d->maxExtentDirty = true;
788     d->minExtentDirty = true;
789     QSGFlickable::geometryChanged(newGeometry, oldGeometry);
790 }
791
792
793 qreal QSGItemView::minYExtent() const
794 {
795     Q_D(const QSGItemView);
796     if (d->layoutOrientation() == Qt::Horizontal)
797         return QSGFlickable::minYExtent();
798
799     if (d->minExtentDirty) {
800         d->minExtent = -d->startPosition();
801         if (d->header && d->visibleItems.count())
802             d->minExtent += d->headerSize();
803         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
804             d->minExtent += d->highlightRangeStart;
805             if (d->visibleItem(0))
806                 d->minExtent -= d->visibleItem(0)->sectionSize();
807             d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd));
808         }
809         d->minExtentDirty = false;
810     }
811
812     return d->minExtent;
813 }
814
815 qreal QSGItemView::maxYExtent() const
816 {
817     Q_D(const QSGItemView);
818     if (d->layoutOrientation() == Qt::Horizontal)
819         return height();
820
821     if (d->maxExtentDirty) {
822         if (!d->model || !d->model->count()) {
823             d->maxExtent = d->header ? -d->headerSize() : 0;
824             d->maxExtent += height();
825         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
826             d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart);
827             if (d->highlightRangeEnd != d->highlightRangeStart)
828                 d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd));
829         } else {
830             d->maxExtent = -(d->endPosition() - height());
831         }
832
833         if (d->footer)
834             d->maxExtent -= d->footerSize();
835         qreal minY = minYExtent();
836         if (d->maxExtent > minY)
837             d->maxExtent = minY;
838         d->maxExtentDirty = false;
839     }
840     return d->maxExtent;
841 }
842
843 qreal QSGItemView::minXExtent() const
844 {
845     Q_D(const QSGItemView);
846     if (d->layoutOrientation() == Qt::Vertical)
847         return QSGFlickable::minXExtent();
848
849     if (d->minExtentDirty) {
850         d->minExtent = -d->startPosition();
851         qreal highlightStart;
852         qreal highlightEnd;
853         qreal endPositionFirstItem = 0;
854         if (d->isContentFlowReversed()) {
855             if (d->model && d->model->count())
856                 endPositionFirstItem = d->positionAt(d->model->count()-1);
857             else if (d->header)
858                 d->minExtent += d->headerSize();
859             highlightStart = d->highlightRangeStartValid
860                     ? d->highlightRangeStart - (d->lastPosition()-endPositionFirstItem)
861                     : d->size() - (d->lastPosition()-endPositionFirstItem);
862             highlightEnd = d->highlightRangeEndValid ? d->highlightRangeEnd : d->size();
863             if (d->footer)
864                 d->minExtent += d->footerSize();
865             qreal maxX = maxXExtent();
866             if (d->minExtent < maxX)
867                 d->minExtent = maxX;
868         } else {
869             endPositionFirstItem = d->endPositionAt(0);
870             highlightStart = d->highlightRangeStart;
871             highlightEnd = d->highlightRangeEnd;
872             if (d->header && d->visibleItems.count())
873                 d->minExtent += d->headerSize();
874         }
875         if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
876             d->minExtent += highlightStart;
877             d->minExtent = qMax(d->minExtent, -(endPositionFirstItem - highlightEnd));
878         }
879         d->minExtentDirty = false;
880     }
881
882     return d->minExtent;
883 }
884
885 qreal QSGItemView::maxXExtent() const
886 {
887     Q_D(const QSGItemView);
888     if (d->layoutOrientation() == Qt::Vertical)
889         return width();
890
891     if (d->maxExtentDirty) {
892         qreal highlightStart;
893         qreal highlightEnd;
894         qreal lastItemPosition = 0;
895         d->maxExtent = 0;
896         if (d->isContentFlowReversed()) {
897             highlightStart = d->highlightRangeStartValid ? d->highlightRangeEnd : d->size();
898             highlightEnd = d->highlightRangeEndValid ? d->highlightRangeStart : d->size();
899             lastItemPosition = d->endPosition();
900         } else {
901             highlightStart = d->highlightRangeStart;
902             highlightEnd = d->highlightRangeEnd;
903             if (d->model && d->model->count())
904                 lastItemPosition = d->positionAt(d->model->count()-1);
905         }
906         if (!d->model || !d->model->count()) {
907             if (!d->isContentFlowReversed())
908                 d->maxExtent = d->header ? -d->headerSize() : 0;
909             d->maxExtent += width();
910         } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) {
911             d->maxExtent = -(lastItemPosition - highlightStart);
912             if (highlightEnd != highlightStart) {
913                 d->maxExtent = d->isContentFlowReversed()
914                         ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd))
915                         : qMin(d->maxExtent, -(d->endPosition() - highlightEnd));
916             }
917         } else {
918             d->maxExtent = -(d->endPosition() - width());
919         }
920         if (d->isContentFlowReversed()) {
921             if (d->header && d->visibleItems.count())
922                 d->maxExtent -= d->headerSize();
923         } else {
924             if (d->footer)
925                 d->maxExtent -= d->footerSize();
926             qreal minX = minXExtent();
927             if (d->maxExtent > minX)
928                 d->maxExtent = minX;
929         }
930         d->maxExtentDirty = false;
931     }
932
933     return d->maxExtent;
934 }
935
936 void QSGItemView::setContentX(qreal pos)
937 {
938     Q_D(QSGItemView);
939     // Positioning the view manually should override any current movement state
940     d->moveReason = QSGItemViewPrivate::Other;
941     QSGFlickable::setContentX(pos);
942 }
943
944 void QSGItemView::setContentY(qreal pos)
945 {
946     Q_D(QSGItemView);
947     // Positioning the view manually should override any current movement state
948     d->moveReason = QSGItemViewPrivate::Other;
949     QSGFlickable::setContentY(pos);
950 }
951
952
953 void QSGItemView::updatePolish()
954 {
955     Q_D(QSGItemView);
956     QSGFlickable::updatePolish();
957     d->layout();
958 }
959
960 void QSGItemView::componentComplete()
961 {
962     Q_D(QSGItemView);
963     QSGFlickable::componentComplete();
964
965     updateSections();
966     d->updateHeader();
967     d->updateFooter();
968     d->updateViewport();
969     d->setPosition(d->contentStartPosition());
970     if (d->isValid()) {
971         d->refill();
972         d->moveReason = QSGItemViewPrivate::SetIndex;
973         if (d->currentIndex < 0 && !d->currentIndexCleared)
974             d->updateCurrent(0);
975         else
976             d->updateCurrent(d->currentIndex);
977         if (d->highlight && d->currentItem) {
978             if (d->autoHighlight)
979                 d->resetHighlightPosition();
980             d->updateTrackedItem();
981         }
982         d->moveReason = QSGItemViewPrivate::Other;
983         d->fixupPosition();
984     }
985 }
986
987
988
989 QSGItemViewPrivate::QSGItemViewPrivate()
990     : itemCount(0)
991     , buffer(0), bufferMode(BufferBefore | BufferAfter)
992     , layoutDirection(Qt::LeftToRight)
993     , moveReason(Other)
994     , visibleIndex(0)
995     , currentIndex(-1), currentItem(0)
996     , trackedItem(0), requestedIndex(-1)
997     , highlightComponent(0), highlight(0)
998     , highlightRange(QSGItemView::NoHighlightRange)
999     , highlightRangeStart(0), highlightRangeEnd(0)
1000     , highlightMoveDuration(150)
1001     , headerComponent(0), header(0), footerComponent(0), footer(0)
1002     , minExtent(0), maxExtent(0)
1003     , ownModel(false), wrap(false), lazyRelease(false), deferredRelease(false)
1004     , layoutScheduled(false), inViewportMoved(false), currentIndexCleared(false)
1005     , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false)
1006     , minExtentDirty(true), maxExtentDirty(true)
1007 {
1008 }
1009
1010 bool QSGItemViewPrivate::isValid() const
1011 {
1012     return model && model->count() && model->isValid();
1013 }
1014
1015 qreal QSGItemViewPrivate::position() const
1016 {
1017     Q_Q(const QSGItemView);
1018     return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX();
1019 }
1020
1021 qreal QSGItemViewPrivate::size() const
1022 {
1023     Q_Q(const QSGItemView);
1024     return layoutOrientation() == Qt::Vertical ? q->height() : q->width();
1025 }
1026
1027 qreal QSGItemViewPrivate::startPosition() const
1028 {
1029     return isContentFlowReversed() ? -lastPosition() : originPosition();
1030 }
1031
1032 qreal QSGItemViewPrivate::endPosition() const
1033 {
1034     return isContentFlowReversed() ? -originPosition() : lastPosition();
1035 }
1036
1037 qreal QSGItemViewPrivate::contentStartPosition() const
1038 {
1039     return -headerSize();
1040 }
1041
1042 int QSGItemViewPrivate::findLastVisibleIndex(int defaultValue) const
1043 {
1044     if (visibleItems.count()) {
1045         int i = visibleItems.count() - 1;
1046         while (i > 0 && visibleItems.at(i)->index == -1)
1047             --i;
1048         if (visibleItems.at(i)->index != -1)
1049             return visibleItems.at(i)->index;
1050     }
1051     return defaultValue;
1052 }
1053
1054 FxViewItem *QSGItemViewPrivate::visibleItem(int modelIndex) const {
1055     if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) {
1056         for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) {
1057             FxViewItem *item = visibleItems.at(i);
1058             if (item->index == modelIndex)
1059                 return item;
1060         }
1061     }
1062     return 0;
1063 }
1064
1065 FxViewItem *QSGItemViewPrivate::firstVisibleItem() const {
1066     const qreal pos = isContentFlowReversed() ? -position()-size() : position();
1067     for (int i = 0; i < visibleItems.count(); ++i) {
1068         FxViewItem *item = visibleItems.at(i);
1069         if (item->index != -1 && item->endPosition() >= pos)
1070             return item;
1071     }
1072     return visibleItems.count() ? visibleItems.first() : 0;
1073 }
1074
1075 // Map a model index to visibleItems list index.
1076 // These may differ if removed items are still present in the visible list,
1077 // e.g. doing a removal animation
1078 int QSGItemViewPrivate::mapFromModel(int modelIndex) const
1079 {
1080     if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count())
1081         return -1;
1082     for (int i = 0; i < visibleItems.count(); ++i) {
1083         FxViewItem *item = visibleItems.at(i);
1084         if (item->index == modelIndex)
1085             return i + visibleIndex;
1086         if (item->index > modelIndex)
1087             return -1;
1088     }
1089     return -1; // Not in visibleList
1090 }
1091
1092 void QSGItemViewPrivate::adjustMoveParameters(int *from, int *to, int *count) const
1093 {
1094     if (*from > *to) {
1095         // Only move forwards - flip if backwards moving
1096         int tfrom = *from;
1097         int tto = *to;
1098         *from = tto;
1099         *to = tto + *count;
1100         *count = tfrom - tto;
1101     }
1102 }
1103
1104 void QSGItemViewPrivate::init()
1105 {
1106     Q_Q(QSGItemView);
1107     QSGItemPrivate::get(contentItem)->childrenDoNotOverlap = true;
1108     q->setFlag(QSGItem::ItemIsFocusScope);
1109     addItemChangeListener(this, Geometry);
1110     QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped()));
1111     q->setFlickableDirection(QSGFlickable::VerticalFlick);
1112 }
1113
1114 void QSGItemViewPrivate::updateCurrent(int modelIndex)
1115 {
1116     Q_Q(QSGItemView);
1117     if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) {
1118         if (currentItem) {
1119             currentItem->attached->setIsCurrentItem(false);
1120             releaseItem(currentItem);
1121             currentItem = 0;
1122             currentIndex = modelIndex;
1123             emit q->currentIndexChanged();
1124             updateHighlight();
1125         } else if (currentIndex != modelIndex) {
1126             currentIndex = modelIndex;
1127             emit q->currentIndexChanged();
1128         }
1129         return;
1130     }
1131
1132     if (currentItem && currentIndex == modelIndex) {
1133         updateHighlight();
1134         return;
1135     }
1136
1137     FxViewItem *oldCurrentItem = currentItem;
1138     currentIndex = modelIndex;
1139     currentItem = createItem(modelIndex);
1140     if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item))
1141         oldCurrentItem->attached->setIsCurrentItem(false);
1142     if (currentItem) {
1143         currentItem->item->setFocus(true);
1144         currentItem->attached->setIsCurrentItem(true);
1145         initializeCurrentItem();
1146     }
1147
1148     updateHighlight();
1149     emit q->currentIndexChanged();
1150     releaseItem(oldCurrentItem);
1151 }
1152
1153 void QSGItemViewPrivate::clear()
1154 {
1155     timeline.clear();
1156
1157     for (int i = 0; i < visibleItems.count(); ++i)
1158         releaseItem(visibleItems.at(i));
1159     visibleItems.clear();
1160     visibleIndex = 0;
1161
1162     releaseItem(currentItem);
1163     currentItem = 0;
1164     createHighlight();
1165     trackedItem = 0;
1166
1167     minExtentDirty = true;
1168     maxExtentDirty = true;
1169     itemCount = 0;
1170 }
1171
1172
1173 void QSGItemViewPrivate::mirrorChange()
1174 {
1175     Q_Q(QSGItemView);
1176     regenerate();
1177     emit q->effectiveLayoutDirectionChanged();
1178 }
1179
1180 void QSGItemViewPrivate::refill()
1181 {
1182     if (isContentFlowReversed())
1183         refill(-position()-size(), -position());
1184     else
1185         refill(position(), position()+size());
1186 }
1187
1188 void QSGItemViewPrivate::refill(qreal from, qreal to, bool doBuffer)
1189 {
1190     Q_Q(QSGItemView);
1191     if (!isValid() || !q->isComponentComplete())
1192         return;
1193
1194     itemCount = model->count();
1195     qreal bufferFrom = from - buffer;
1196     qreal bufferTo = to + buffer;
1197     qreal fillFrom = from;
1198     qreal fillTo = to;
1199     if (doBuffer && (bufferMode & BufferAfter))
1200         fillTo = bufferTo;
1201     if (doBuffer && (bufferMode & BufferBefore))
1202         fillFrom = bufferFrom;
1203
1204     // Item creation and release is staggered in order to avoid
1205     // creating/releasing multiple items in one frame
1206     // while flicking (as much as possible).
1207
1208     bool changed = addVisibleItems(fillFrom, fillTo, doBuffer);
1209
1210     if (!lazyRelease || !changed || deferredRelease) { // avoid destroying items in the same frame that we create
1211         if (removeNonVisibleItems(bufferFrom, bufferTo))
1212             changed = true;
1213         deferredRelease = false;
1214     } else {
1215         deferredRelease = true;
1216     }
1217
1218     if (changed) {
1219         minExtentDirty = true;
1220         maxExtentDirty = true;
1221         visibleItemsChanged();
1222     } else if (!doBuffer && buffer && bufferMode != NoBuffer) {
1223         refill(from, to, true);
1224     }
1225
1226     lazyRelease = false;
1227 }
1228
1229 void QSGItemViewPrivate::regenerate()
1230 {
1231     Q_Q(QSGItemView);
1232     if (q->isComponentComplete()) {
1233         delete header;
1234         header = 0;
1235         delete footer;
1236         footer = 0;
1237         updateHeader();
1238         updateFooter();
1239         clear();
1240         updateViewport();
1241         setPosition(contentStartPosition());
1242         refill();
1243         updateCurrent(currentIndex);
1244     }
1245 }
1246
1247 void QSGItemViewPrivate::scheduleLayout()
1248 {
1249     Q_Q(QSGItemView);
1250     if (!layoutScheduled) {
1251         layoutScheduled = true;
1252         q->polish();
1253     }
1254 }
1255
1256 void QSGItemViewPrivate::updateViewport()
1257 {
1258     Q_Q(QSGItemView);
1259     if (isValid()) {
1260         if (layoutOrientation() == Qt::Vertical)
1261             q->setContentHeight(endPosition() - startPosition());
1262         else
1263             q->setContentWidth(endPosition() - startPosition());
1264     }
1265 }
1266
1267 void QSGItemViewPrivate::layout()
1268 {
1269     Q_Q(QSGItemView);
1270     layoutScheduled = false;
1271     if (!isValid() && !visibleItems.count()) {
1272         clear();
1273         setPosition(contentStartPosition());
1274         return;
1275     }
1276
1277     layoutVisibleItems();
1278     refill();
1279
1280     minExtentDirty = true;
1281     maxExtentDirty = true;
1282
1283     updateHighlight();
1284     if (!q->isMoving() && !q->isFlicking()) {
1285         fixupPosition();
1286         refill();
1287     }
1288
1289     updateHeader();
1290     updateFooter();
1291     updateViewport();
1292     updateUnrequestedPositions();
1293 }
1294
1295 FxViewItem *QSGItemViewPrivate::createItem(int modelIndex)
1296 {
1297     Q_Q(QSGItemView);
1298
1299     requestedIndex = modelIndex;
1300     FxViewItem *viewItem = 0;
1301
1302     if (QSGItem *item = model->item(modelIndex, false)) {
1303         viewItem = newViewItem(modelIndex, item);
1304         if (viewItem) {
1305             viewItem->index = modelIndex;
1306             if (model->completePending()) {
1307                 // complete
1308                 viewItem->item->setZ(1);
1309                 viewItem->item->setParentItem(q->contentItem());
1310                 model->completeItem();
1311             } else {
1312                 viewItem->item->setParentItem(q->contentItem());
1313             }
1314             // do other set up for the new item that should not happen
1315             // until after bindings are evaluated
1316             initializeViewItem(viewItem);
1317
1318             unrequestedItems.remove(viewItem->item);
1319         }
1320     }
1321     requestedIndex = -1;
1322     return viewItem;
1323 }
1324
1325
1326 void QSGItemViewPrivate::releaseItem(FxViewItem *item)
1327 {
1328     Q_Q(QSGItemView);
1329     if (!item || !model)
1330         return;
1331     if (trackedItem == item)
1332         trackedItem = 0;
1333     QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item->item);
1334     itemPrivate->removeItemChangeListener(this, QSGItemPrivate::Geometry);
1335     if (model->release(item->item) == 0) {
1336         // item was not destroyed, and we no longer reference it.
1337         unrequestedItems.insert(item->item, model->indexOf(item->item, q));
1338     }
1339     delete item;
1340 }
1341
1342 QSGItem *QSGItemViewPrivate::createHighlightItem()
1343 {
1344     return createComponentItem(highlightComponent, true, true);
1345 }
1346
1347 QSGItem *QSGItemViewPrivate::createComponentItem(QDeclarativeComponent *component, bool receiveItemGeometryChanges, bool createDefault)
1348 {
1349     Q_Q(QSGItemView);
1350
1351     QSGItem *item = 0;
1352     if (component) {
1353         QDeclarativeContext *context = new QDeclarativeContext(qmlContext(q));
1354         QObject *nobj = component->create(context);
1355         if (nobj) {
1356             QDeclarative_setParent_noEvent(context, nobj);
1357             item = qobject_cast<QSGItem *>(nobj);
1358             if (!item)
1359                 delete nobj;
1360         } else {
1361             delete context;
1362         }
1363     } else if (createDefault) {
1364         item = new QSGItem;
1365     }
1366     if (item) {
1367         QDeclarative_setParent_noEvent(item, q->contentItem());
1368         item->setParentItem(q->contentItem());
1369         if (receiveItemGeometryChanges) {
1370             QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1371             itemPrivate->addItemChangeListener(this, QSGItemPrivate::Geometry);
1372         }
1373     }
1374     return item;
1375 }
1376
1377 void QSGItemViewPrivate::updateTrackedItem()
1378 {
1379     Q_Q(QSGItemView);
1380     FxViewItem *item = currentItem;
1381     if (highlight)
1382         item = highlight;
1383     trackedItem = item;
1384
1385     if (trackedItem)
1386         q->trackedPositionChanged();
1387 }
1388
1389 void QSGItemViewPrivate::updateUnrequestedIndexes()
1390 {
1391     Q_Q(QSGItemView);
1392     for (QHash<QSGItem*,int>::iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
1393         *it = model->indexOf(it.key(), q);
1394 }
1395
1396 void QSGItemViewPrivate::updateUnrequestedPositions()
1397 {
1398     for (QHash<QSGItem*,int>::const_iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it)
1399         repositionPackageItemAt(it.key(), it.value());
1400 }
1401
1402 QT_END_NAMESPACE