diff options
Diffstat (limited to 'src/quick/items/qquickitemview.cpp')
-rw-r--r-- | src/quick/items/qquickitemview.cpp | 1732 |
1 files changed, 1732 insertions, 0 deletions
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp new file mode 100644 index 0000000000..edbe2a5d06 --- /dev/null +++ b/src/quick/items/qquickitemview.cpp @@ -0,0 +1,1732 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickitemview_p_p.h" + +QT_BEGIN_NAMESPACE + + +FxViewItem::FxViewItem(QQuickItem *i, bool own) + : item(i), ownItem(own), index(-1) +{ +} + +FxViewItem::~FxViewItem() +{ + if (ownItem && item) { + item->setParentItem(0); + item->deleteLater(); + item = 0; + } +} + + +QQuickItemViewChangeSet::QQuickItemViewChangeSet() + : active(false) +{ + reset(); +} + +bool QQuickItemViewChangeSet::hasPendingChanges() const +{ + return !pendingChanges.isEmpty(); +} + +void QQuickItemViewChangeSet::applyChanges(const QDeclarativeChangeSet &changeSet) +{ + pendingChanges.apply(changeSet); + + int moveId = -1; + int moveOffset; + + foreach (const QDeclarativeChangeSet::Remove &r, changeSet.removes()) { + itemCount -= r.count; + if (moveId == -1 && newCurrentIndex >= r.index + r.count) { + newCurrentIndex -= r.count; + currentChanged = true; + } else if (moveId == -1 && newCurrentIndex >= r.index && newCurrentIndex < r.index + r.count) { + // current item has been removed. + if (r.isMove()) { + moveId = r.moveId; + moveOffset = newCurrentIndex - r.index; + } else { + currentRemoved = true; + newCurrentIndex = -1; + if (itemCount) + newCurrentIndex = qMin(r.index, itemCount - 1); + } + currentChanged = true; + } + } + foreach (const QDeclarativeChangeSet::Insert &i, changeSet.inserts()) { + if (moveId == -1) { + if (itemCount && newCurrentIndex >= i.index) { + newCurrentIndex += i.count; + currentChanged = true; + } else if (newCurrentIndex < 0) { + newCurrentIndex = 0; + currentChanged = true; + } else if (newCurrentIndex == 0 && !itemCount) { + // this is the first item, set the initial current index + currentChanged = true; + } + } else if (moveId == i.moveId) { + newCurrentIndex = i.index + moveOffset; + } + itemCount += i.count; + } +} + +void QQuickItemViewChangeSet::prepare(int currentIndex, int count) +{ + if (active) + return; + reset(); + active = true; + itemCount = count; + newCurrentIndex = currentIndex; +} + +void QQuickItemViewChangeSet::reset() +{ + itemCount = 0; + newCurrentIndex = -1; + pendingChanges.clear(); + removedItems.clear(); + active = false; + currentChanged = false; + currentRemoved = false; +} + + +QQuickItemView::QQuickItemView(QQuickFlickablePrivate &dd, QQuickItem *parent) + : QQuickFlickable(dd, parent) +{ + Q_D(QQuickItemView); + d->init(); +} + +QQuickItemView::~QQuickItemView() +{ + Q_D(QQuickItemView); + d->clear(); + if (d->ownModel) + delete d->model; + delete d->header; + delete d->footer; +} + + +QQuickItem *QQuickItemView::currentItem() const +{ + Q_D(const QQuickItemView); + if (!d->currentItem) + return 0; + const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges(); + return d->currentItem->item; +} + +QVariant QQuickItemView::model() const +{ + Q_D(const QQuickItemView); + return d->modelVariant; +} + +void QQuickItemView::setModel(const QVariant &model) +{ + Q_D(QQuickItemView); + if (d->modelVariant == model) + return; + if (d->model) { + disconnect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), + this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); + disconnect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*))); + disconnect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); + disconnect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); + } + + QQuickVisualModel *oldModel = d->model; + + d->clear(); + d->setPosition(d->contentStartPosition()); + d->model = 0; + d->modelVariant = model; + + QObject *object = qvariant_cast<QObject*>(model); + QQuickVisualModel *vim = 0; + if (object && (vim = qobject_cast<QQuickVisualModel *>(object))) { + if (d->ownModel) { + delete oldModel; + d->ownModel = false; + } + d->model = vim; + } else { + if (!d->ownModel) { + d->model = new QQuickVisualDataModel(qmlContext(this), this); + d->ownModel = true; + if (isComponentComplete()) + static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete(); + } else { + d->model = oldModel; + } + if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) + dataModel->setModel(model); + } + + if (d->model) { + d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter; + connect(d->model, SIGNAL(createdItem(int,QQuickItem*)), this, SLOT(createdItem(int,QQuickItem*))); + connect(d->model, SIGNAL(initItem(int,QQuickItem*)), this, SLOT(initItem(int,QQuickItem*))); + connect(d->model, SIGNAL(destroyingItem(QQuickItem*)), this, SLOT(destroyingItem(QQuickItem*))); + if (isComponentComplete()) { + updateSections(); + d->refill(); + if ((d->currentIndex >= d->model->count() || d->currentIndex < 0) && !d->currentIndexCleared) { + setCurrentIndex(0); + } else { + d->moveReason = QQuickItemViewPrivate::SetIndex; + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->resetHighlightPosition(); + d->updateTrackedItem(); + } + d->moveReason = QQuickItemViewPrivate::Other; + } + d->updateViewport(); + } + connect(d->model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)), + this, SLOT(modelUpdated(QDeclarativeChangeSet,bool))); + emit countChanged(); + } + emit modelChanged(); +} + +QDeclarativeComponent *QQuickItemView::delegate() const +{ + Q_D(const QQuickItemView); + if (d->model) { + if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) + return dataModel->delegate(); + } + + return 0; +} + +void QQuickItemView::setDelegate(QDeclarativeComponent *delegate) +{ + Q_D(QQuickItemView); + if (delegate == this->delegate()) + return; + if (!d->ownModel) { + d->model = new QQuickVisualDataModel(qmlContext(this)); + d->ownModel = true; + } + if (QQuickVisualDataModel *dataModel = qobject_cast<QQuickVisualDataModel*>(d->model)) { + int oldCount = dataModel->count(); + dataModel->setDelegate(delegate); + if (isComponentComplete()) { + for (int i = 0; i < d->visibleItems.count(); ++i) + d->releaseItem(d->visibleItems.at(i)); + d->visibleItems.clear(); + d->releaseItem(d->currentItem); + d->currentItem = 0; + updateSections(); + d->refill(); + d->moveReason = QQuickItemViewPrivate::SetIndex; + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->resetHighlightPosition(); + d->updateTrackedItem(); + } + d->moveReason = QQuickItemViewPrivate::Other; + d->updateViewport(); + } + if (oldCount != dataModel->count()) + emit countChanged(); + } + emit delegateChanged(); +} + + +int QQuickItemView::count() const +{ + Q_D(const QQuickItemView); + if (!d->model) + return 0; + const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges(); + return d->model->count(); +} + +int QQuickItemView::currentIndex() const +{ + Q_D(const QQuickItemView); + const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges(); + return d->currentIndex; +} + +void QQuickItemView::setCurrentIndex(int index) +{ + Q_D(QQuickItemView); + if (d->requestedIndex >= 0 && !d->requestedAsync) // currently creating item + return; + d->currentIndexCleared = (index == -1); + + d->applyPendingChanges(); + if (index == d->currentIndex) + return; + if (isComponentComplete() && d->isValid()) { + d->moveReason = QQuickItemViewPrivate::SetIndex; + d->updateCurrent(index); + } else if (d->currentIndex != index) { + d->currentIndex = index; + emit currentIndexChanged(); + } +} + + +bool QQuickItemView::isWrapEnabled() const +{ + Q_D(const QQuickItemView); + return d->wrap; +} + +void QQuickItemView::setWrapEnabled(bool wrap) +{ + Q_D(QQuickItemView); + if (d->wrap == wrap) + return; + d->wrap = wrap; + emit keyNavigationWrapsChanged(); +} + +int QQuickItemView::cacheBuffer() const +{ + Q_D(const QQuickItemView); + return d->buffer; +} + +void QQuickItemView::setCacheBuffer(int b) +{ + Q_D(QQuickItemView); + if (d->buffer != b) { + d->buffer = b; + if (isComponentComplete()) { + d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter; + d->refill(); + } + emit cacheBufferChanged(); + } +} + + +Qt::LayoutDirection QQuickItemView::layoutDirection() const +{ + Q_D(const QQuickItemView); + return d->layoutDirection; +} + +void QQuickItemView::setLayoutDirection(Qt::LayoutDirection layoutDirection) +{ + Q_D(QQuickItemView); + if (d->layoutDirection != layoutDirection) { + d->layoutDirection = layoutDirection; + d->regenerate(); + emit layoutDirectionChanged(); + emit effectiveLayoutDirectionChanged(); + } +} + +Qt::LayoutDirection QQuickItemView::effectiveLayoutDirection() const +{ + Q_D(const QQuickItemView); + if (d->effectiveLayoutMirror) + return d->layoutDirection == Qt::RightToLeft ? Qt::LeftToRight : Qt::RightToLeft; + else + return d->layoutDirection; +} + + +QDeclarativeComponent *QQuickItemView::header() const +{ + Q_D(const QQuickItemView); + return d->headerComponent; +} + +QQuickItem *QQuickItemView::headerItem() const +{ + Q_D(const QQuickItemView); + const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges(); + return d->header ? d->header->item : 0; +} + +void QQuickItemView::setHeader(QDeclarativeComponent *headerComponent) +{ + Q_D(QQuickItemView); + if (d->headerComponent != headerComponent) { + d->applyPendingChanges(); + delete d->header; + d->header = 0; + d->headerComponent = headerComponent; + + d->markExtentsDirty(); + + if (isComponentComplete()) { + d->updateHeader(); + d->updateFooter(); + d->updateViewport(); + d->fixupPosition(); + } else { + emit headerItemChanged(); + } + emit headerChanged(); + } +} + +QDeclarativeComponent *QQuickItemView::footer() const +{ + Q_D(const QQuickItemView); + return d->footerComponent; +} + +QQuickItem *QQuickItemView::footerItem() const +{ + Q_D(const QQuickItemView); + const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges(); + return d->footer ? d->footer->item : 0; +} + +void QQuickItemView::setFooter(QDeclarativeComponent *footerComponent) +{ + Q_D(QQuickItemView); + if (d->footerComponent != footerComponent) { + d->applyPendingChanges(); + delete d->footer; + d->footer = 0; + d->footerComponent = footerComponent; + + if (isComponentComplete()) { + d->updateFooter(); + d->updateViewport(); + d->fixupPosition(); + } else { + emit footerItemChanged(); + } + emit footerChanged(); + } +} + +QDeclarativeComponent *QQuickItemView::highlight() const +{ + Q_D(const QQuickItemView); + const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges(); + return d->highlightComponent; +} + +void QQuickItemView::setHighlight(QDeclarativeComponent *highlightComponent) +{ + Q_D(QQuickItemView); + if (highlightComponent != d->highlightComponent) { + d->applyPendingChanges(); + d->highlightComponent = highlightComponent; + d->createHighlight(); + if (d->currentItem) + d->updateHighlight(); + emit highlightChanged(); + } +} + +QQuickItem *QQuickItemView::highlightItem() const +{ + Q_D(const QQuickItemView); + const_cast<QQuickItemViewPrivate*>(d)->applyPendingChanges(); + return d->highlight ? d->highlight->item : 0; +} + +bool QQuickItemView::highlightFollowsCurrentItem() const +{ + Q_D(const QQuickItemView); + return d->autoHighlight; +} + +void QQuickItemView::setHighlightFollowsCurrentItem(bool autoHighlight) +{ + Q_D(QQuickItemView); + if (d->autoHighlight != autoHighlight) { + d->autoHighlight = autoHighlight; + if (autoHighlight) + d->updateHighlight(); + emit highlightFollowsCurrentItemChanged(); + } +} + +QQuickItemView::HighlightRangeMode QQuickItemView::highlightRangeMode() const +{ + Q_D(const QQuickItemView); + return static_cast<QQuickItemView::HighlightRangeMode>(d->highlightRange); +} + +void QQuickItemView::setHighlightRangeMode(HighlightRangeMode mode) +{ + Q_D(QQuickItemView); + if (d->highlightRange == mode) + return; + d->highlightRange = mode; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit highlightRangeModeChanged(); +} + +//###Possibly rename these properties, since they are very useful even without a highlight? +qreal QQuickItemView::preferredHighlightBegin() const +{ + Q_D(const QQuickItemView); + return d->highlightRangeStart; +} + +void QQuickItemView::setPreferredHighlightBegin(qreal start) +{ + Q_D(QQuickItemView); + d->highlightRangeStartValid = true; + if (d->highlightRangeStart == start) + return; + d->highlightRangeStart = start; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit preferredHighlightBeginChanged(); +} + +void QQuickItemView::resetPreferredHighlightBegin() +{ + Q_D(QQuickItemView); + d->highlightRangeStartValid = false; + if (d->highlightRangeStart == 0) + return; + d->highlightRangeStart = 0; + emit preferredHighlightBeginChanged(); +} + +qreal QQuickItemView::preferredHighlightEnd() const +{ + Q_D(const QQuickItemView); + return d->highlightRangeEnd; +} + +void QQuickItemView::setPreferredHighlightEnd(qreal end) +{ + Q_D(QQuickItemView); + d->highlightRangeEndValid = true; + if (d->highlightRangeEnd == end) + return; + d->highlightRangeEnd = end; + d->haveHighlightRange = d->highlightRange != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd; + emit preferredHighlightEndChanged(); +} + +void QQuickItemView::resetPreferredHighlightEnd() +{ + Q_D(QQuickItemView); + d->highlightRangeEndValid = false; + if (d->highlightRangeEnd == 0) + return; + d->highlightRangeEnd = 0; + emit preferredHighlightEndChanged(); +} + +int QQuickItemView::highlightMoveDuration() const +{ + Q_D(const QQuickItemView); + return d->highlightMoveDuration; +} + +void QQuickItemView::setHighlightMoveDuration(int duration) +{ + Q_D(QQuickItemView); + if (d->highlightMoveDuration != duration) { + d->highlightMoveDuration = duration; + emit highlightMoveDurationChanged(); + } +} + +void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode) +{ + Q_Q(QQuickItemView); + if (!isValid()) + return; + if (mode < QQuickItemView::Beginning || mode > QQuickItemView::Contain) + return; + + applyPendingChanges(); + int idx = qMax(qMin(index, model->count()-1), 0); + + qreal pos = isContentFlowReversed() ? -position() - size() : position(); + FxViewItem *item = visibleItem(idx); + qreal maxExtent; + if (layoutOrientation() == Qt::Vertical) + maxExtent = -q->maxYExtent(); + else + maxExtent = isContentFlowReversed() ? q->minXExtent()-size(): -q->maxXExtent(); + if (!item) { + int itemPos = positionAt(idx); + changedVisibleIndex(idx); + // save the currently visible items in case any of them end up visible again + QList<FxViewItem *> oldVisible = visibleItems; + visibleItems.clear(); + setPosition(qMin(qreal(itemPos), maxExtent)); + // now release the reference to all the old visible items. + for (int i = 0; i < oldVisible.count(); ++i) + releaseItem(oldVisible.at(i)); + item = visibleItem(idx); + } + if (item) { + const qreal itemPos = item->position(); + switch (mode) { + case QQuickItemView::Beginning: + pos = itemPos; + if (index < 0 && header) + pos -= headerSize(); + break; + case QQuickItemView::Center: + pos = itemPos - (size() - item->size())/2; + break; + case QQuickItemView::End: + pos = itemPos - size() + item->size(); + if (index >= model->count() && footer) + pos += footerSize(); + break; + case QQuickItemView::Visible: + if (itemPos > pos + size()) + pos = itemPos - size() + item->size(); + else if (item->endPosition() <= pos) + pos = itemPos; + break; + case QQuickItemView::Contain: + if (item->endPosition() >= pos + size()) + pos = itemPos - size() + item->size(); + if (itemPos < pos) + pos = itemPos; + } + pos = qMin(pos, maxExtent); + qreal minExtent; + if (layoutOrientation() == Qt::Vertical) + minExtent = -q->minYExtent(); + else + minExtent = isContentFlowReversed() ? q->maxXExtent()-size(): -q->minXExtent(); + pos = qMax(pos, minExtent); + moveReason = QQuickItemViewPrivate::Other; + q->cancelFlick(); + setPosition(pos); + + if (highlight) { + if (autoHighlight) + resetHighlightPosition(); + updateHighlight(); + } + } + fixupPosition(); +} + +void QQuickItemView::positionViewAtIndex(int index, int mode) +{ + Q_D(QQuickItemView); + if (!d->isValid() || index < 0 || index >= d->model->count()) + return; + d->positionViewAtIndex(index, mode); +} + + +void QQuickItemView::positionViewAtBeginning() +{ + Q_D(QQuickItemView); + if (!d->isValid()) + return; + d->positionViewAtIndex(-1, Beginning); +} + +void QQuickItemView::positionViewAtEnd() +{ + Q_D(QQuickItemView); + if (!d->isValid()) + return; + d->positionViewAtIndex(d->model->count(), End); +} + +int QQuickItemView::indexAt(qreal x, qreal y) const +{ + Q_D(const QQuickItemView); + for (int i = 0; i < d->visibleItems.count(); ++i) { + const FxViewItem *item = d->visibleItems.at(i); + if (item->contains(x, y)) + return item->index; + } + + return -1; +} + +void QQuickItemViewPrivate::applyPendingChanges() +{ + Q_Q(QQuickItemView); + if (q->isComponentComplete() && currentChanges.hasPendingChanges()) + layout(); +} + +// for debugging only +void QQuickItemViewPrivate::checkVisible() const +{ + int skip = 0; + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index == -1) { + ++skip; + } else if (item->index != visibleIndex + i - skip) { + qFatal("index %d %d %d", visibleIndex, i, item->index); + } + } +} + +void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_Q(QQuickItemView); + QQuickFlickablePrivate::itemGeometryChanged(item, newGeometry, oldGeometry); + if (!q->isComponentComplete()) + return; + + if (header && header->item == item) { + updateHeader(); + markExtentsDirty(); + if (!q->isMoving() && !q->isFlicking()) + fixupPosition(); + } else if (footer && footer->item == item) { + updateFooter(); + markExtentsDirty(); + if (!q->isMoving() && !q->isFlicking()) + fixupPosition(); + } + + if (currentItem && currentItem->item == item) + updateHighlight(); + if (trackedItem && trackedItem->item == item) + q->trackedPositionChanged(); +} + +void QQuickItemView::destroyRemoved() +{ + Q_D(QQuickItemView); + for (QList<FxViewItem*>::Iterator it = d->visibleItems.begin(); + it != d->visibleItems.end();) { + FxViewItem *item = *it; + if (item->index == -1 && item->attached->delayRemove() == false) { + d->releaseItem(item); + it = d->visibleItems.erase(it); + } else { + ++it; + } + } + + // Correct the positioning of the items + d->updateSections(); + d->forceLayout = true; + d->layout(); +} + +void QQuickItemView::modelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) +{ + Q_D(QQuickItemView); + if (reset) { + d->moveReason = QQuickItemViewPrivate::SetIndex; + d->regenerate(); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->resetHighlightPosition(); + d->updateTrackedItem(); + } + d->moveReason = QQuickItemViewPrivate::Other; + + emit countChanged(); + } else { + d->currentChanges.prepare(d->currentIndex, d->itemCount); + d->currentChanges.applyChanges(changeSet); + polish(); + } +} + +void QQuickItemView::animStopped() +{ + Q_D(QQuickItemView); + d->bufferMode = QQuickItemViewPrivate::BufferBefore | QQuickItemViewPrivate::BufferAfter; + d->refill(); + if (d->haveHighlightRange && d->highlightRange == QQuickItemView::StrictlyEnforceRange) + d->updateHighlight(); +} + + +void QQuickItemView::trackedPositionChanged() +{ + Q_D(QQuickItemView); + if (!d->trackedItem || !d->currentItem) + return; + if (d->moveReason == QQuickItemViewPrivate::SetIndex) { + qreal trackedPos = d->trackedItem->position(); + qreal trackedSize = d->trackedItem->size(); + if (d->trackedItem != d->currentItem) { + trackedSize += d->currentItem->sectionSize(); + } + qreal viewPos = d->isContentFlowReversed() ? -d->position()-d->size() : d->position(); + qreal pos = viewPos; + if (d->haveHighlightRange) { + if (trackedPos > pos + d->highlightRangeEnd - trackedSize) + pos = trackedPos - d->highlightRangeEnd + trackedSize; + if (trackedPos < pos + d->highlightRangeStart) + pos = trackedPos - d->highlightRangeStart; + if (d->highlightRange != StrictlyEnforceRange) { + if (pos > d->endPosition() - d->size()) + pos = d->endPosition() - d->size(); + if (pos < d->startPosition()) + pos = d->startPosition(); + } + } else { + qreal trackedEndPos = d->trackedItem->endPosition(); + qreal toItemPos = d->currentItem->position(); + qreal toItemEndPos = d->currentItem->endPosition(); + + if (d->header && d->showHeaderForIndex(d->currentIndex)) { + trackedPos -= d->headerSize(); + trackedEndPos -= d->headerSize(); + toItemPos -= d->headerSize(); + toItemEndPos -= d->headerSize(); + } else if (d->footer && d->showFooterForIndex(d->currentIndex)) { + trackedPos += d->footerSize(); + trackedEndPos += d->footerSize(); + toItemPos += d->footerSize(); + toItemEndPos += d->footerSize(); + } + + if (trackedPos < viewPos && toItemPos < viewPos) { + pos = qMax(trackedPos, toItemPos); + } else if (trackedEndPos >= viewPos + d->size() + && toItemEndPos >= viewPos + d->size()) { + if (trackedEndPos <= toItemEndPos) { + pos = trackedEndPos - d->size(); + if (trackedSize > d->size()) + pos = trackedPos; + } else { + pos = toItemEndPos - d->size(); + if (d->currentItem->size() > d->size()) + pos = d->currentItem->position(); + } + } + } + if (viewPos != pos) { + cancelFlick(); + d->calcVelocity = true; + d->setPosition(pos); + d->calcVelocity = false; + } + } +} + +void QQuickItemView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + Q_D(QQuickItemView); + d->markExtentsDirty(); + QQuickFlickable::geometryChanged(newGeometry, oldGeometry); +} + + +qreal QQuickItemView::minYExtent() const +{ + Q_D(const QQuickItemView); + if (d->layoutOrientation() == Qt::Horizontal) + return QQuickFlickable::minYExtent(); + + if (d->vData.minExtentDirty) { + d->minExtent = d->vData.startMargin-d->startPosition(); + if (d->header) + d->minExtent += d->headerSize(); + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->minExtent += d->highlightRangeStart; + if (d->visibleItem(0)) + d->minExtent -= d->visibleItem(0)->sectionSize(); + d->minExtent = qMax(d->minExtent, -(d->endPositionAt(0) - d->highlightRangeEnd)); + } + d->vData.minExtentDirty = false; + } + + return d->minExtent; +} + +qreal QQuickItemView::maxYExtent() const +{ + Q_D(const QQuickItemView); + if (d->layoutOrientation() == Qt::Horizontal) + return height(); + + if (d->vData.maxExtentDirty) { + if (!d->model || !d->model->count()) { + d->maxExtent = d->header ? -d->headerSize() : 0; + d->maxExtent += height(); + } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->maxExtent = -(d->positionAt(d->model->count()-1) - d->highlightRangeStart); + if (d->highlightRangeEnd != d->highlightRangeStart) + d->maxExtent = qMin(d->maxExtent, -(d->endPosition() - d->highlightRangeEnd)); + } else { + d->maxExtent = -(d->endPosition() - height()); + } + + if (d->footer) + d->maxExtent -= d->footerSize(); + d->maxExtent -= d->vData.endMargin; + qreal minY = minYExtent(); + if (d->maxExtent > minY) + d->maxExtent = minY; + d->vData.maxExtentDirty = false; + } + return d->maxExtent; +} + +qreal QQuickItemView::minXExtent() const +{ + Q_D(const QQuickItemView); + if (d->layoutOrientation() == Qt::Vertical) + return QQuickFlickable::minXExtent(); + + if (d->hData.minExtentDirty) { + d->minExtent = -d->startPosition(); + qreal highlightStart; + qreal highlightEnd; + qreal endPositionFirstItem = 0; + if (d->isContentFlowReversed()) { + d->minExtent += d->hData.endMargin; + if (d->model && d->model->count()) + endPositionFirstItem = d->positionAt(d->model->count()-1); + else if (d->header) + d->minExtent += d->headerSize(); + highlightStart = d->highlightRangeEndValid ? d->size() - d->highlightRangeEnd : d->size(); + highlightEnd = d->highlightRangeStartValid ? d->size() - d->highlightRangeStart : d->size(); + if (d->footer) + d->minExtent += d->footerSize(); + qreal maxX = maxXExtent(); + if (d->minExtent < maxX) + d->minExtent = maxX; + } else { + d->minExtent += d->hData.startMargin; + endPositionFirstItem = d->endPositionAt(0); + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + if (d->header) + d->minExtent += d->headerSize(); + } + if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->minExtent += highlightStart; + d->minExtent = d->isContentFlowReversed() + ? qMin(d->minExtent, endPositionFirstItem + highlightEnd) + : qMax(d->minExtent, -(endPositionFirstItem - highlightEnd)); + } + d->hData.minExtentDirty = false; + } + + return d->minExtent; +} + +qreal QQuickItemView::maxXExtent() const +{ + Q_D(const QQuickItemView); + if (d->layoutOrientation() == Qt::Vertical) + return width(); + + if (d->hData.maxExtentDirty) { + qreal highlightStart; + qreal highlightEnd; + qreal lastItemPosition = 0; + d->maxExtent = 0; + if (d->isContentFlowReversed()) { + highlightStart = d->highlightRangeEndValid ? d->size() - d->highlightRangeEnd : d->size(); + highlightEnd = d->highlightRangeStartValid ? d->size() - d->highlightRangeStart : d->size(); + lastItemPosition = d->endPosition(); + } else { + highlightStart = d->highlightRangeStart; + highlightEnd = d->highlightRangeEnd; + if (d->model && d->model->count()) + lastItemPosition = d->positionAt(d->model->count()-1); + } + if (!d->model || !d->model->count()) { + if (!d->isContentFlowReversed()) + d->maxExtent = d->header ? -d->headerSize() : 0; + d->maxExtent += width(); + } else if (d->haveHighlightRange && d->highlightRange == StrictlyEnforceRange) { + d->maxExtent = -(lastItemPosition - highlightStart); + if (highlightEnd != highlightStart) { + d->maxExtent = d->isContentFlowReversed() + ? qMax(d->maxExtent, -(d->endPosition() - highlightEnd)) + : qMin(d->maxExtent, -(d->endPosition() - highlightEnd)); + } + } else { + d->maxExtent = -(d->endPosition() - width()); + } + if (d->isContentFlowReversed()) { + if (d->header) + d->maxExtent -= d->headerSize(); + d->maxExtent -= d->hData.startMargin; + } else { + if (d->footer) + d->maxExtent -= d->footerSize(); + d->maxExtent -= d->hData.endMargin; + qreal minX = minXExtent(); + if (d->maxExtent > minX) + d->maxExtent = minX; + } + d->hData.maxExtentDirty = false; + } + + return d->maxExtent; +} + +void QQuickItemView::setContentX(qreal pos) +{ + Q_D(QQuickItemView); + // Positioning the view manually should override any current movement state + d->moveReason = QQuickItemViewPrivate::Other; + QQuickFlickable::setContentX(pos); +} + +void QQuickItemView::setContentY(qreal pos) +{ + Q_D(QQuickItemView); + // Positioning the view manually should override any current movement state + d->moveReason = QQuickItemViewPrivate::Other; + QQuickFlickable::setContentY(pos); +} + +qreal QQuickItemView::xOrigin() const +{ + Q_D(const QQuickItemView); + if (d->isContentFlowReversed()) + return -maxXExtent() + d->size() - d->hData.startMargin; + else + return -minXExtent() + d->hData.startMargin; +} + +void QQuickItemView::updatePolish() +{ + Q_D(QQuickItemView); + QQuickFlickable::updatePolish(); + d->layout(); +} + +void QQuickItemView::componentComplete() +{ + Q_D(QQuickItemView); + if (d->model && d->ownModel) + static_cast<QQuickVisualDataModel *>(d->model.data())->componentComplete(); + + QQuickFlickable::componentComplete(); + + updateSections(); + d->updateHeader(); + d->updateFooter(); + d->updateViewport(); + d->setPosition(d->contentStartPosition()); + if (d->isValid()) { + d->refill(); + d->moveReason = QQuickItemViewPrivate::SetIndex; + if (d->currentIndex < 0 && !d->currentIndexCleared) + d->updateCurrent(0); + else + d->updateCurrent(d->currentIndex); + if (d->highlight && d->currentItem) { + if (d->autoHighlight) + d->resetHighlightPosition(); + d->updateTrackedItem(); + } + d->moveReason = QQuickItemViewPrivate::Other; + d->fixupPosition(); + } + if (d->model && d->model->count()) + emit countChanged(); +} + + + +QQuickItemViewPrivate::QQuickItemViewPrivate() + : itemCount(0) + , buffer(0), bufferMode(BufferBefore | BufferAfter) + , layoutDirection(Qt::LeftToRight) + , moveReason(Other) + , visibleIndex(0) + , currentIndex(-1), currentItem(0) + , trackedItem(0), requestedIndex(-1), requestedItem(0) + , highlightComponent(0), highlight(0) + , highlightRange(QQuickItemView::NoHighlightRange) + , highlightRangeStart(0), highlightRangeEnd(0) + , highlightMoveDuration(150) + , headerComponent(0), header(0), footerComponent(0), footer(0) + , minExtent(0), maxExtent(0) + , ownModel(false), wrap(false), deferredRelease(false) + , inApplyModelChanges(false), inViewportMoved(false), forceLayout(false), currentIndexCleared(false) + , haveHighlightRange(false), autoHighlight(true), highlightRangeStartValid(false), highlightRangeEndValid(false) + , fillCacheBuffer(false), inRequest(false), requestedAsync(false) +{ +} + +bool QQuickItemViewPrivate::isValid() const +{ + return model && model->count() && model->isValid(); +} + +qreal QQuickItemViewPrivate::position() const +{ + Q_Q(const QQuickItemView); + return layoutOrientation() == Qt::Vertical ? q->contentY() : q->contentX(); +} + +qreal QQuickItemViewPrivate::size() const +{ + Q_Q(const QQuickItemView); + return layoutOrientation() == Qt::Vertical ? q->height() : q->width(); +} + +qreal QQuickItemViewPrivate::startPosition() const +{ + return isContentFlowReversed() ? -lastPosition() : originPosition(); +} + +qreal QQuickItemViewPrivate::endPosition() const +{ + return isContentFlowReversed() ? -originPosition() : lastPosition(); +} + +qreal QQuickItemViewPrivate::contentStartPosition() const +{ + qreal pos = -headerSize(); + if (layoutOrientation() == Qt::Vertical) + pos -= vData.startMargin; + else if (isContentFlowReversed()) + pos -= hData.endMargin; + else + pos -= hData.startMargin; + + return pos; +} + +int QQuickItemViewPrivate::findLastVisibleIndex(int defaultValue) const +{ + if (visibleItems.count()) { + int i = visibleItems.count() - 1; + while (i > 0 && visibleItems.at(i)->index == -1) + --i; + if (visibleItems.at(i)->index != -1) + return visibleItems.at(i)->index; + } + return defaultValue; +} + +FxViewItem *QQuickItemViewPrivate::visibleItem(int modelIndex) const { + if (modelIndex >= visibleIndex && modelIndex < visibleIndex + visibleItems.count()) { + for (int i = modelIndex - visibleIndex; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index == modelIndex) + return item; + } + } + return 0; +} + +FxViewItem *QQuickItemViewPrivate::firstVisibleItem() const { + const qreal pos = isContentFlowReversed() ? -position()-size() : position(); + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index != -1 && item->endPosition() > pos) + return item; + } + return visibleItems.count() ? visibleItems.first() : 0; +} + +// Map a model index to visibleItems list index. +// These may differ if removed items are still present in the visible list, +// e.g. doing a removal animation +int QQuickItemViewPrivate::mapFromModel(int modelIndex) const +{ + if (modelIndex < visibleIndex || modelIndex >= visibleIndex + visibleItems.count()) + return -1; + for (int i = 0; i < visibleItems.count(); ++i) { + FxViewItem *item = visibleItems.at(i); + if (item->index == modelIndex) + return i; + if (item->index > modelIndex) + return -1; + } + return -1; // Not in visibleList +} + +void QQuickItemViewPrivate::init() +{ + Q_Q(QQuickItemView); + QQuickItemPrivate::get(contentItem)->childrenDoNotOverlap = true; + q->setFlag(QQuickItem::ItemIsFocusScope); + addItemChangeListener(this, Geometry); + QObject::connect(q, SIGNAL(movementEnded()), q, SLOT(animStopped())); + q->setFlickableDirection(QQuickFlickable::VerticalFlick); +} + +void QQuickItemViewPrivate::updateCurrent(int modelIndex) +{ + Q_Q(QQuickItemView); + applyPendingChanges(); + + if (!q->isComponentComplete() || !isValid() || modelIndex < 0 || modelIndex >= model->count()) { + if (currentItem) { + currentItem->attached->setIsCurrentItem(false); + releaseItem(currentItem); + currentItem = 0; + currentIndex = modelIndex; + emit q->currentIndexChanged(); + emit q->currentItemChanged(); + updateHighlight(); + } else if (currentIndex != modelIndex) { + currentIndex = modelIndex; + emit q->currentIndexChanged(); + } + return; + } + + if (currentItem && currentIndex == modelIndex) { + updateHighlight(); + return; + } + + FxViewItem *oldCurrentItem = currentItem; + int oldCurrentIndex = currentIndex; + currentIndex = modelIndex; + currentItem = createItem(modelIndex, false); + if (oldCurrentItem && (!currentItem || oldCurrentItem->item != currentItem->item)) + oldCurrentItem->attached->setIsCurrentItem(false); + if (currentItem) { + currentItem->item->setFocus(true); + currentItem->attached->setIsCurrentItem(true); + initializeCurrentItem(); + } + + updateHighlight(); + if (oldCurrentIndex != currentIndex) + emit q->currentIndexChanged(); + if (oldCurrentItem != currentItem) + emit q->currentItemChanged(); + releaseItem(oldCurrentItem); +} + +void QQuickItemViewPrivate::clear() +{ + currentChanges.reset(); + timeline.clear(); + + for (int i = 0; i < visibleItems.count(); ++i) + releaseItem(visibleItems.at(i)); + visibleItems.clear(); + visibleIndex = 0; + + releaseItem(currentItem); + currentItem = 0; + createHighlight(); + trackedItem = 0; + + markExtentsDirty(); + itemCount = 0; +} + + +void QQuickItemViewPrivate::mirrorChange() +{ + Q_Q(QQuickItemView); + regenerate(); + emit q->effectiveLayoutDirectionChanged(); +} + +void QQuickItemViewPrivate::refill() +{ + if (isContentFlowReversed()) + refill(-position()-size(), -position()); + else + refill(position(), position()+size()); +} + +void QQuickItemViewPrivate::refill(qreal from, qreal to, bool doBuffer) +{ + Q_Q(QQuickItemView); + if (!isValid() || !q->isComponentComplete()) + return; + + currentChanges.reset(); + + int prevCount = itemCount; + itemCount = model->count(); + qreal bufferFrom = from - buffer; + qreal bufferTo = to + buffer; + qreal fillFrom = from; + qreal fillTo = to; + if (doBuffer && (bufferMode & BufferAfter)) + fillTo = bufferTo; + if (doBuffer && (bufferMode & BufferBefore)) + fillFrom = bufferFrom; + + // Item creation and release is staggered in order to avoid + // creating/releasing multiple items in one frame + // while flicking (as much as possible). + + bool changed = addVisibleItems(fillFrom, fillTo, doBuffer); + + if (!changed || deferredRelease) { // avoid destroying items in the same frame that we create + if (removeNonVisibleItems(bufferFrom, bufferTo)) + changed = true; + deferredRelease = false; + } else { + deferredRelease = true; + } + + if (changed) { + markExtentsDirty(); + visibleItemsChanged(); + } else if (!doBuffer && buffer && bufferMode != NoBuffer) { + refill(from, to, true); + } + + if (!q->isMoving() && changed) { + fillCacheBuffer = true; + q->polish(); + } + + if (prevCount != itemCount) + emit q->countChanged(); +} + +void QQuickItemViewPrivate::regenerate() +{ + Q_Q(QQuickItemView); + if (q->isComponentComplete()) { + currentChanges.reset(); + delete header; + header = 0; + delete footer; + footer = 0; + updateHeader(); + updateFooter(); + clear(); + updateViewport(); + setPosition(contentStartPosition()); + refill(); + updateCurrent(currentIndex); + } +} + +void QQuickItemViewPrivate::updateViewport() +{ + Q_Q(QQuickItemView); + if (isValid()) { + if (layoutOrientation() == Qt::Vertical) + q->setContentHeight(endPosition() - startPosition()); + else + q->setContentWidth(endPosition() - startPosition()); + } +} + +void QQuickItemViewPrivate::layout() +{ + Q_Q(QQuickItemView); + if (inApplyModelChanges) + return; + + if (!isValid() && !visibleItems.count()) { + clear(); + setPosition(contentStartPosition()); + return; + } + + if (!applyModelChanges() && !forceLayout) { + if (fillCacheBuffer) + refill(); + return; + } + forceLayout = false; + + layoutVisibleItems(); + refill(); + + markExtentsDirty(); + + updateHighlight(); + + if (!q->isMoving() && !q->isFlicking()) { + fixupPosition(); + refill(); + } + + updateHeader(); + updateFooter(); + updateViewport(); + updateUnrequestedPositions(); +} + +bool QQuickItemViewPrivate::applyModelChanges() +{ + Q_Q(QQuickItemView); + if (!q->isComponentComplete() || !currentChanges.hasPendingChanges() || inApplyModelChanges) + return false; + + inApplyModelChanges = true; + + updateUnrequestedIndexes(); + moveReason = QQuickItemViewPrivate::Other; + + int prevCount = itemCount; + bool visibleAffected = false; + bool viewportChanged = !currentChanges.pendingChanges.removes().isEmpty() + || !currentChanges.pendingChanges.inserts().isEmpty(); + + FxViewItem *firstVisible = firstVisibleItem(); + FxViewItem *origVisibleItemsFirst = visibleItems.count() ? visibleItems.first() : 0; + int firstItemIndex = firstVisible ? firstVisible->index : -1; + qreal removedBeforeFirstVisibleBy = 0; + + const QVector<QDeclarativeChangeSet::Remove> &removals = currentChanges.pendingChanges.removes(); + for (int i=0; i<removals.count(); i++) { + itemCount -= removals[i].count; + + // Remove the items from the visible list, skipping anything already marked for removal + QList<FxViewItem*>::Iterator it = visibleItems.begin(); + while (it != visibleItems.end()) { + FxViewItem *item = *it; + if (item->index == -1 || item->index < removals[i].index) { + // already removed, or before removed items + if (!visibleAffected && item->index < removals[i].index) + visibleAffected = true; + ++it; + } else if (item->index >= removals[i].index + removals[i].count) { + // after removed items + item->index -= removals[i].count; + ++it; + } else { + // removed item + visibleAffected = true; + if (!removals[i].isMove()) + item->attached->emitRemove(); + + if (item->attached->delayRemove() && !removals[i].isMove()) { + item->index = -1; + QObject::connect(item->attached, SIGNAL(delayRemoveChanged()), q, SLOT(destroyRemoved()), Qt::QueuedConnection); + ++it; + } else { + if (firstVisible && item->position() < firstVisible->position() && item != visibleItems.first()) + removedBeforeFirstVisibleBy += item->size(); + if (removals[i].isMove()) { + currentChanges.removedItems.insert(removals[i].moveKey(item->index), item); + } else { + if (item == firstVisible) + firstVisible = 0; + currentChanges.removedItems.insertMulti(QDeclarativeChangeSet::MoveKey(), item); + } + it = visibleItems.erase(it); + } + } + } + if (!visibleAffected && needsRefillForAddedOrRemovedIndex(removals[i].index)) + visibleAffected = true; + } + if (!removals.isEmpty()) + updateVisibleIndex(); + + const QVector<QDeclarativeChangeSet::Insert> &insertions = currentChanges.pendingChanges.inserts(); + InsertionsResult insertResult; + bool allInsertionsBeforeVisible = true; + + for (int i=0; i<insertions.count(); i++) { + bool wasEmpty = visibleItems.isEmpty(); + if (applyInsertionChange(insertions[i], firstVisible, &insertResult)) + visibleAffected = true; + if (!visibleAffected && needsRefillForAddedOrRemovedIndex(insertions[i].index)) + visibleAffected = true; + if (insertions[i].index >= visibleIndex) + allInsertionsBeforeVisible = false; + if (wasEmpty && !visibleItems.isEmpty()) + resetFirstItemPosition(); + itemCount += insertions[i].count; + } + for (int i=0; i<insertResult.addedItems.count(); ++i) + insertResult.addedItems.at(i)->attached->emitAdd(); + + // if the first visible item has moved, ensure another one takes its place + // so that we avoid shifting all content forwards + // (if an item is removed from before the first visible, the first visible should not move upwards) + bool movedBackToFirstVisible = false; + if (firstVisible && firstItemIndex >= 0) { + for (int i=0; i<insertResult.movedBackwards.count(); i++) { + if (insertResult.movedBackwards[i]->index == firstItemIndex) { + // an item has moved backwards up to the first visible's position + resetItemPosition(insertResult.movedBackwards[i], firstVisible); + insertResult.movedBackwards.removeAt(i); + movedBackToFirstVisible = true; + break; + } + } + if (!movedBackToFirstVisible && !allInsertionsBeforeVisible) { + // first visible item has moved forward, another visible item takes its place + FxViewItem *item = visibleItem(firstItemIndex); + if (item) + resetItemPosition(item, firstVisible); + } + } + + // Ensure we don't cause an ugly list scroll + if (firstVisible && visibleItems.count() && visibleItems.first() != firstVisible) { + // ensure first item is placed at correct postion if moving backward + // since it will be used to position all subsequent items + if (insertResult.movedBackwards.count() && origVisibleItemsFirst) + resetItemPosition(visibleItems.first(), origVisibleItemsFirst); + + // correct the first item position (unless it has already been fixed) + if (!movedBackToFirstVisible) { + qreal moveBackwardsBy = insertResult.sizeAddedBeforeVisible; + for (int i=0; i<insertResult.movedBackwards.count(); i++) + moveBackwardsBy += insertResult.movedBackwards[i]->size(); + moveItemBy(visibleItems.first(), removedBeforeFirstVisibleBy, moveBackwardsBy); + } + } + + // Whatever removed/moved items remain are no longer visible items. + for (QHash<QDeclarativeChangeSet::MoveKey, FxViewItem *>::Iterator it = currentChanges.removedItems.begin(); + it != currentChanges.removedItems.end(); ++it) { + releaseItem(it.value()); + } + currentChanges.removedItems.clear(); + + if (currentChanges.currentChanged) { + if (currentChanges.currentRemoved && currentItem) { + currentItem->attached->setIsCurrentItem(false); + releaseItem(currentItem); + currentItem = 0; + } + if (!currentIndexCleared) + updateCurrent(currentChanges.newCurrentIndex); + } + currentChanges.reset(); + + updateSections(); + if (prevCount != itemCount) + emit q->countChanged(); + + if (!visibleAffected) + visibleAffected = !currentChanges.pendingChanges.changes().isEmpty(); + if (!visibleAffected && viewportChanged) + updateViewport(); + + inApplyModelChanges = false; + return visibleAffected; +} + +/* + This may return 0 if the item is being created asynchronously. + When the item becomes available, refill() will be called and the item + will be returned on the next call to createItem(). +*/ +FxViewItem *QQuickItemViewPrivate::createItem(int modelIndex, bool asynchronous) +{ + Q_Q(QQuickItemView); + if (requestedIndex == modelIndex && (asynchronous || requestedAsync == asynchronous)) + return 0; + + if (requestedIndex != -1 && requestedIndex != modelIndex) { + delete requestedItem; + requestedItem = 0; + } + + requestedIndex = modelIndex; + requestedAsync = asynchronous; + inRequest = true; + + if (QQuickItem *item = model->item(modelIndex, asynchronous)) { + item->setParentItem(q->contentItem()); + QDeclarative_setParent_noEvent(item, q->contentItem()); + requestedIndex = -1; + fillCacheBuffer = false; + FxViewItem *viewItem = requestedItem; + if (!viewItem) + viewItem = newViewItem(modelIndex, item); // already in cache, so viewItem not initialized in initItem() + if (viewItem) { + viewItem->index = modelIndex; + // do other set up for the new item that should not happen + // until after bindings are evaluated + initializeViewItem(viewItem); + unrequestedItems.remove(item); + } + requestedItem = 0; + inRequest = false; + return viewItem; + } + + inRequest = false; + return 0; +} + +void QQuickItemView::createdItem(int index, QQuickItem *item) +{ + Q_D(QQuickItemView); + if (d->requestedIndex != index) { + item->setParentItem(contentItem()); + d->unrequestedItems.insert(item, index); + item->setVisible(false); + d->repositionPackageItemAt(item, index); + } else { + d->requestedIndex = -1; + if (!d->inRequest) { + if (index == d->currentIndex) + d->updateCurrent(index); + d->refill(); + } else { + d->fillCacheBuffer = true; + polish(); + } + } +} + +void QQuickItemView::initItem(int index, QQuickItem *item) +{ + Q_D(QQuickItemView); + item->setZ(1); + if (d->requestedIndex == index) { + item->setParentItem(contentItem()); + QDeclarative_setParent_noEvent(item, contentItem()); + d->requestedItem = d->newViewItem(index, item); + } +} + +void QQuickItemView::destroyingItem(QQuickItem *item) +{ + Q_D(QQuickItemView); + d->unrequestedItems.remove(item); +} + +void QQuickItemViewPrivate::releaseItem(FxViewItem *item) +{ + Q_Q(QQuickItemView); + if (!item || !model) + return; + if (trackedItem == item) + trackedItem = 0; + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item->item); + itemPrivate->removeItemChangeListener(this, QQuickItemPrivate::Geometry); + if (model->release(item->item) == 0) { + // item was not destroyed, and we no longer reference it. + unrequestedItems.insert(item->item, model->indexOf(item->item, q)); + } + delete item; +} + +QQuickItem *QQuickItemViewPrivate::createHighlightItem() +{ + return createComponentItem(highlightComponent, true, true); +} + +QQuickItem *QQuickItemViewPrivate::createComponentItem(QDeclarativeComponent *component, bool receiveItemGeometryChanges, bool createDefault) +{ + Q_Q(QQuickItemView); + + QQuickItem *item = 0; + if (component) { + QDeclarativeContext *creationContext = component->creationContext(); + QDeclarativeContext *context = new QDeclarativeContext( + creationContext ? creationContext : qmlContext(q)); + QObject *nobj = component->create(context); + if (nobj) { + QDeclarative_setParent_noEvent(context, nobj); + item = qobject_cast<QQuickItem *>(nobj); + if (!item) + delete nobj; + } else { + delete context; + } + } else if (createDefault) { + item = new QQuickItem; + } + if (item) { + QDeclarative_setParent_noEvent(item, q->contentItem()); + item->setParentItem(q->contentItem()); + if (receiveItemGeometryChanges) { + QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); + itemPrivate->addItemChangeListener(this, QQuickItemPrivate::Geometry); + } + } + return item; +} + +void QQuickItemViewPrivate::updateTrackedItem() +{ + Q_Q(QQuickItemView); + FxViewItem *item = currentItem; + if (highlight) + item = highlight; + trackedItem = item; + + if (trackedItem) + q->trackedPositionChanged(); +} + +void QQuickItemViewPrivate::updateUnrequestedIndexes() +{ + Q_Q(QQuickItemView); + for (QHash<QQuickItem*,int>::iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) + *it = model->indexOf(it.key(), q); +} + +void QQuickItemViewPrivate::updateUnrequestedPositions() +{ + for (QHash<QQuickItem*,int>::const_iterator it = unrequestedItems.begin(); it != unrequestedItems.end(); ++it) + repositionPackageItemAt(it.key(), it.value()); +} + +void QQuickItemViewPrivate::updateVisibleIndex() +{ + visibleIndex = 0; + for (QList<FxViewItem*>::Iterator it = visibleItems.begin(); it != visibleItems.end(); ++it) { + if ((*it)->index != -1) { + visibleIndex = (*it)->index; + break; + } + } +} + +QT_END_NAMESPACE |