diff options
Diffstat (limited to 'src/qml/types/qqmldelegatemodel.cpp')
-rw-r--r-- | src/qml/types/qqmldelegatemodel.cpp | 3557 |
1 files changed, 0 insertions, 3557 deletions
diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp deleted file mode 100644 index af328fbe2a..0000000000 --- a/src/qml/types/qqmldelegatemodel.cpp +++ /dev/null @@ -1,3557 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmldelegatemodel_p_p.h" -#include "qqmldelegatecomponent_p.h" - -#include <QtQml/qqmlinfo.h> - -#include <private/qquickpackage_p.h> -#include <private/qmetaobjectbuilder_p.h> -#include <private/qqmladaptormodel_p.h> -#include <private/qqmlchangeset_p.h> -#include <private/qqmlengine_p.h> -#include <private/qqmlcomponent_p.h> -#include <private/qqmlincubator_p.h> - -#include <private/qv4value_p.h> -#include <private/qv4functionobject_p.h> -#include <qv4objectiterator_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlDelegateModelItem; - -namespace QV4 { - -namespace Heap { - -struct DelegateModelGroupFunction : FunctionObject { - void init(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)); - - QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg); - uint flag; -}; - -struct QQmlDelegateModelGroupChange : Object { - void init() { Object::init(); } - - QQmlChangeSet::ChangeData change; -}; - -struct QQmlDelegateModelGroupChangeArray : Object { - void init(const QVector<QQmlChangeSet::Change> &changes); - void destroy() { - delete changes; - Object::destroy(); - } - - QVector<QQmlChangeSet::Change> *changes; -}; - - -} - -struct DelegateModelGroupFunction : QV4::FunctionObject -{ - V4_OBJECT2(DelegateModelGroupFunction, FunctionObject) - - static Heap::DelegateModelGroupFunction *create(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)) - { - return scope->engine()->memoryManager->allocate<DelegateModelGroupFunction>(scope, flag, code); - } - - static ReturnedValue virtualCall(const QV4::FunctionObject *that, const Value *thisObject, const Value *argv, int argc) - { - QV4::Scope scope(that->engine()); - QV4::Scoped<DelegateModelGroupFunction> f(scope, static_cast<const DelegateModelGroupFunction *>(that)); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject); - if (!o) - return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - - QV4::ScopedValue v(scope, argc ? argv[0] : Value::undefinedValue()); - return f->d()->code(o->d()->item, f->d()->flag, v); - } -}; - -void Heap::DelegateModelGroupFunction::init(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)) -{ - QV4::Heap::FunctionObject::init(scope, QStringLiteral("DelegateModelGroupFunction")); - this->flag = flag; - this->code = code; -} - -} - -DEFINE_OBJECT_VTABLE(QV4::DelegateModelGroupFunction); - - - -class QQmlDelegateModelEngineData : public QV8Engine::Deletable -{ -public: - QQmlDelegateModelEngineData(QV4::ExecutionEngine *v4); - ~QQmlDelegateModelEngineData(); - - QV4::ReturnedValue array(QV4::ExecutionEngine *engine, - const QVector<QQmlChangeSet::Change> &changes); - - QV4::PersistentValue changeProto; -}; - -V4_DEFINE_EXTENSION(QQmlDelegateModelEngineData, engineData) - - -void QQmlDelegateModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) -{ - prop.setWritable(false); -} - -QVariant QQmlDelegateModelPartsMetaObject::initialValue(int id) -{ - QQmlDelegateModelParts *parts = static_cast<QQmlDelegateModelParts *>(object()); - QQmlPartsModel *m = new QQmlPartsModel( - parts->model, QString::fromUtf8(name(id)), parts); - parts->models.append(m); - return QVariant::fromValue(static_cast<QObject *>(m)); -} - -QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent) -: QObject(parent), model(parent) -{ - new QQmlDelegateModelPartsMetaObject(this); -} - -//--------------------------------------------------------------------------- - -/*! - \qmltype DelegateModel - \instantiates QQmlDelegateModel - \inqmlmodule QtQml.Models - \brief Encapsulates a model and delegate. - - The DelegateModel type encapsulates a model and the delegate that will - be instantiated for items in the model. - - It is usually not necessary to create a DelegateModel. - However, it can be useful for manipulating and accessing the \l modelIndex - when a QAbstractItemModel subclass is used as the - model. Also, DelegateModel is used together with \l Package to - provide delegates to multiple views, and with DelegateModelGroup to sort and filter - delegate items. - - The example below illustrates using a DelegateModel with a ListView. - - \snippet delegatemodel/delegatemodel.qml 0 -*/ - -QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt) - : m_delegateChooser(nullptr) - , m_cacheMetaType(nullptr) - , m_context(ctxt) - , m_parts(nullptr) - , m_filterGroup(QStringLiteral("items")) - , m_count(0) - , m_groupCount(Compositor::MinimumGroupCount) - , m_compositorGroup(Compositor::Cache) - , m_complete(false) - , m_delegateValidated(false) - , m_reset(false) - , m_transaction(false) - , m_incubatorCleanupScheduled(false) - , m_waitingToFetchMore(false) - , m_cacheItems(nullptr) - , m_items(nullptr) - , m_persistedItems(nullptr) -{ -} - -QQmlDelegateModelPrivate::~QQmlDelegateModelPrivate() -{ - qDeleteAll(m_finishedIncubating); - - if (m_cacheMetaType) - m_cacheMetaType->release(); -} - -int QQmlDelegateModelPrivate::adaptorModelCount() const -{ - // QQmlDelegateModel currently only support list models. - // So even if a model is a table model, only the first - // column will be used. - return m_adaptorModel.rowCount(); -} - -void QQmlDelegateModelPrivate::requestMoreIfNecessary() -{ - Q_Q(QQmlDelegateModel); - if (!m_waitingToFetchMore && m_adaptorModel.canFetchMore()) { - m_waitingToFetchMore = true; - QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); - } -} - -void QQmlDelegateModelPrivate::init() -{ - Q_Q(QQmlDelegateModel); - m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag); - - m_items = new QQmlDelegateModelGroup(QStringLiteral("items"), q, Compositor::Default, q); - m_items->setDefaultInclude(true); - m_persistedItems = new QQmlDelegateModelGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q); - QQmlDelegateModelGroupPrivate::get(m_items)->emitters.insert(this); -} - -QQmlDelegateModel::QQmlDelegateModel() - : QQmlDelegateModel(nullptr, nullptr) -{ -} - -QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent) -: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(ctxt)), parent) -{ - Q_D(QQmlDelegateModel); - d->init(); -} - -QQmlDelegateModel::~QQmlDelegateModel() -{ - Q_D(QQmlDelegateModel); - d->disconnectFromAbstractItemModel(); - d->m_adaptorModel.setObject(nullptr, this); - - for (QQmlDelegateModelItem *cacheItem : qAsConst(d->m_cache)) { - if (cacheItem->object) { - delete cacheItem->object; - - cacheItem->object = nullptr; - cacheItem->contextData->invalidate(); - Q_ASSERT(cacheItem->contextData->refCount == 1); - cacheItem->contextData = nullptr; - cacheItem->scriptRef -= 1; - } - cacheItem->groups &= ~Compositor::UnresolvedFlag; - cacheItem->objectRef = 0; - if (!cacheItem->isReferenced()) - delete cacheItem; - else if (cacheItem->incubationTask) - cacheItem->incubationTask->vdm = nullptr; - } -} - - -void QQmlDelegateModel::classBegin() -{ - Q_D(QQmlDelegateModel); - if (!d->m_context) - d->m_context = qmlContext(this); -} - -void QQmlDelegateModel::componentComplete() -{ - Q_D(QQmlDelegateModel); - d->m_complete = true; - - int defaultGroups = 0; - QStringList groupNames; - groupNames.append(QStringLiteral("items")); - groupNames.append(QStringLiteral("persistedItems")); - if (QQmlDelegateModelGroupPrivate::get(d->m_items)->defaultInclude) - defaultGroups |= Compositor::DefaultFlag; - if (QQmlDelegateModelGroupPrivate::get(d->m_persistedItems)->defaultInclude) - defaultGroups |= Compositor::PersistedFlag; - for (int i = Compositor::MinimumGroupCount; i < d->m_groupCount; ++i) { - QString name = d->m_groups[i]->name(); - if (name.isEmpty()) { - d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; - --d->m_groupCount; - --i; - } else if (name.at(0).isUpper()) { - qmlWarning(d->m_groups[i]) << QQmlDelegateModelGroup::tr("Group names must start with a lower case letter"); - d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; - --d->m_groupCount; - --i; - } else { - groupNames.append(name); - - QQmlDelegateModelGroupPrivate *group = QQmlDelegateModelGroupPrivate::get(d->m_groups[i]); - group->setModel(this, Compositor::Group(i)); - if (group->defaultInclude) - defaultGroups |= (1 << i); - } - } - - d->m_cacheMetaType = new QQmlDelegateModelItemMetaType( - d->m_context->engine()->handle(), this, groupNames); - - d->m_compositor.setGroupCount(d->m_groupCount); - d->m_compositor.setDefaultGroups(defaultGroups); - d->updateFilterGroup(); - - while (!d->m_pendingParts.isEmpty()) - static_cast<QQmlPartsModel *>(d->m_pendingParts.first())->updateFilterGroup(); - - QVector<Compositor::Insert> inserts; - d->m_count = d->adaptorModelCount(); - d->m_compositor.append( - &d->m_adaptorModel, - 0, - d->m_count, - defaultGroups | Compositor::AppendFlag | Compositor::PrependFlag, - &inserts); - d->itemsInserted(inserts); - d->emitChanges(); - d->requestMoreIfNecessary(); -} - -/*! - \qmlproperty model QtQml.Models::DelegateModel::model - This property holds the model providing data for the DelegateModel. - - The model provides a set of data that is used to create the items - for a view. For large or dynamic datasets the model is usually - provided by a C++ model object. The C++ model object must be a \l - {QAbstractItemModel} subclass or a simple list. - - Models can also be created directly in QML, using a \l{ListModel} or - \l{QtQuick.XmlListModel::XmlListModel}{XmlListModel}. - - \sa {qml-data-models}{Data Models} - \keyword dm-model-property -*/ -QVariant QQmlDelegateModel::model() const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.model(); -} - -void QQmlDelegateModelPrivate::connectToAbstractItemModel() -{ - Q_Q(QQmlDelegateModel); - if (!m_adaptorModel.adaptsAim()) - return; - - auto aim = m_adaptorModel.aim(); - - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), - q, QQmlDelegateModel, SLOT(_q_rowsInserted(QModelIndex,int,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), - q, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - q, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), - q, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - q, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(modelReset()), - q, QQmlDelegateModel, SLOT(_q_modelReset())); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - q, QQmlDelegateModel, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); -} - -void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel() -{ - Q_Q(QQmlDelegateModel); - if (!m_adaptorModel.adaptsAim()) - return; - - auto aim = m_adaptorModel.aim(); - - QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)), - q, SLOT(_q_rowsInserted(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - q, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)), - q, SLOT(_q_rowsRemoved(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), - q, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>))); - QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - q, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); - QObject::disconnect(aim, SIGNAL(modelReset()), - q, SLOT(_q_modelReset())); - QObject::disconnect(aim, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - q, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); -} - -void QQmlDelegateModel::setModel(const QVariant &model) -{ - Q_D(QQmlDelegateModel); - - if (d->m_complete) - _q_itemsRemoved(0, d->m_count); - - d->disconnectFromAbstractItemModel(); - d->m_adaptorModel.setModel(model, this, d->m_context->engine()); - d->connectToAbstractItemModel(); - - d->m_adaptorModel.replaceWatchedRoles(QList<QByteArray>(), d->m_watchedRoles); - for (int i = 0; d->m_parts && i < d->m_parts->models.count(); ++i) { - d->m_adaptorModel.replaceWatchedRoles( - QList<QByteArray>(), d->m_parts->models.at(i)->watchedRoles()); - } - - if (d->m_complete) { - _q_itemsInserted(0, d->adaptorModelCount()); - d->requestMoreIfNecessary(); - } -} - -/*! - \qmlproperty Component QtQml.Models::DelegateModel::delegate - - The delegate provides a template defining each item instantiated by a view. - The index is exposed as an accessible \c index property. Properties of the - model are also available depending upon the type of \l {qml-data-models}{Data Model}. -*/ -QQmlComponent *QQmlDelegateModel::delegate() const -{ - Q_D(const QQmlDelegateModel); - return d->m_delegate; -} - -void QQmlDelegateModel::setDelegate(QQmlComponent *delegate) -{ - Q_D(QQmlDelegateModel); - if (d->m_transaction) { - qmlWarning(this) << tr("The delegate of a DelegateModel cannot be changed within onUpdated."); - return; - } - if (d->m_delegate == delegate) - return; - bool wasValid = d->m_delegate != nullptr; - d->m_delegate.setObject(delegate, this); - d->m_delegateValidated = false; - if (d->m_delegateChooser) - QObject::disconnect(d->m_delegateChooserChanged); - - d->m_delegateChooser = nullptr; - if (delegate) { - QQmlAbstractDelegateComponent *adc = - qobject_cast<QQmlAbstractDelegateComponent *>(delegate); - if (adc) { - d->m_delegateChooser = adc; - d->m_delegateChooserChanged = connect(adc, &QQmlAbstractDelegateComponent::delegateChanged, - [d](){ d->delegateChanged(); }); - } - } - d->delegateChanged(d->m_delegate, wasValid); -} - -/*! - \qmlproperty QModelIndex QtQml.Models::DelegateModel::rootIndex - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. \c rootIndex allows the children of - any node in a QAbstractItemModel to be provided by this model. - - This property only affects models of type QAbstractItemModel that - are hierarchical (e.g, a tree model). - - For example, here is a simple interactive file system browser. - When a directory name is clicked, the view's \c rootIndex is set to the - QModelIndex node of the clicked directory, thus updating the view to show - the new directory's contents. - - \c main.cpp: - \snippet delegatemodel/delegatemodel_rootindex/main.cpp 0 - - \c view.qml: - \snippet delegatemodel/delegatemodel_rootindex/view.qml 0 - - If the \l {dm-model-property}{model} is a QAbstractItemModel subclass, - the delegate can also reference a \c hasModelChildren property (optionally - qualified by a \e model. prefix) that indicates whether the delegate's - model item has any child nodes. - - \sa modelIndex(), parentModelIndex() -*/ -QVariant QQmlDelegateModel::rootIndex() const -{ - Q_D(const QQmlDelegateModel); - return QVariant::fromValue(QModelIndex(d->m_adaptorModel.rootIndex)); -} - -void QQmlDelegateModel::setRootIndex(const QVariant &root) -{ - Q_D(QQmlDelegateModel); - - QModelIndex modelIndex = qvariant_cast<QModelIndex>(root); - const bool changed = d->m_adaptorModel.rootIndex != modelIndex; - if (changed || !d->m_adaptorModel.isValid()) { - const int oldCount = d->m_count; - d->m_adaptorModel.rootIndex = modelIndex; - if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) { - // The previous root index was invalidated, so we need to reconnect the model. - d->disconnectFromAbstractItemModel(); - d->m_adaptorModel.setModel(d->m_adaptorModel.list.list(), this, d->m_context->engine()); - d->connectToAbstractItemModel(); - } - if (d->m_adaptorModel.canFetchMore()) - d->m_adaptorModel.fetchMore(); - if (d->m_complete) { - const int newCount = d->adaptorModelCount(); - if (oldCount) - _q_itemsRemoved(0, oldCount); - if (newCount) - _q_itemsInserted(0, newCount); - } - if (changed) - emit rootIndexChanged(); - } -} - -/*! - \qmlmethod QModelIndex QtQml.Models::DelegateModel::modelIndex(int index) - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. This function assists in using - tree models in QML. - - Returns a QModelIndex for the specified index. - This value can be assigned to rootIndex. - - \sa rootIndex -*/ -QVariant QQmlDelegateModel::modelIndex(int idx) const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.modelIndex(idx); -} - -/*! - \qmlmethod QModelIndex QtQml.Models::DelegateModel::parentModelIndex() - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. This function assists in using - tree models in QML. - - Returns a QModelIndex for the parent of the current rootIndex. - This value can be assigned to rootIndex. - - \sa rootIndex -*/ -QVariant QQmlDelegateModel::parentModelIndex() const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.parentModelIndex(); -} - -/*! - \qmlproperty int QtQml.Models::DelegateModel::count -*/ - -int QQmlDelegateModel::count() const -{ - Q_D(const QQmlDelegateModel); - if (!d->m_delegate) - return 0; - return d->m_compositor.count(d->m_compositorGroup); -} - -QQmlDelegateModel::ReleaseFlags QQmlDelegateModelPrivate::release(QObject *object) -{ - if (!object) - return QQmlDelegateModel::ReleaseFlags(0); - - QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object); - if (!cacheItem) - return QQmlDelegateModel::ReleaseFlags(0); - - if (!cacheItem->releaseObject()) - return QQmlDelegateModel::Referenced; - - cacheItem->destroyObject(); - emitDestroyingItem(object); - if (cacheItem->incubationTask) { - releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = nullptr; - } - cacheItem->Dispose(); - return QQmlInstanceModel::Destroyed; -} - -/* - Returns ReleaseStatus flags. -*/ - -QQmlDelegateModel::ReleaseFlags QQmlDelegateModel::release(QObject *item) -{ - Q_D(QQmlDelegateModel); - QQmlInstanceModel::ReleaseFlags stat = d->release(item); - return stat; -} - -// Cancel a requested async item -void QQmlDelegateModel::cancel(int index) -{ - Q_D(QQmlDelegateModel); - if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { - qWarning() << "DelegateModel::cancel: index out range" << index << d->m_compositor.count(d->m_compositorGroup); - return; - } - - Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index); - QQmlDelegateModelItem *cacheItem = it->inCache() ? d->m_cache.at(it.cacheIndex) : 0; - if (cacheItem) { - if (cacheItem->incubationTask && !cacheItem->isObjectReferenced()) { - d->releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = nullptr; - - if (cacheItem->object) { - QObject *object = cacheItem->object; - cacheItem->destroyObject(); - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) - d->emitDestroyingPackage(package); - else - d->emitDestroyingItem(object); - } - - cacheItem->scriptRef -= 1; - } - if (!cacheItem->isReferenced()) { - d->m_compositor.clearFlags(Compositor::Cache, it.cacheIndex, 1, Compositor::CacheFlag); - d->m_cache.removeAt(it.cacheIndex); - delete cacheItem; - Q_ASSERT(d->m_cache.count() == d->m_compositor.count(Compositor::Cache)); - } - } -} - -void QQmlDelegateModelPrivate::group_append( - QQmlListProperty<QQmlDelegateModelGroup> *property, QQmlDelegateModelGroup *group) -{ - QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data); - if (d->m_complete) - return; - if (d->m_groupCount == Compositor::MaximumGroupCount) { - qmlWarning(d->q_func()) << QQmlDelegateModel::tr("The maximum number of supported DelegateModelGroups is 8"); - return; - } - d->m_groups[d->m_groupCount] = group; - d->m_groupCount += 1; -} - -int QQmlDelegateModelPrivate::group_count( - QQmlListProperty<QQmlDelegateModelGroup> *property) -{ - QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data); - return d->m_groupCount - 1; -} - -QQmlDelegateModelGroup *QQmlDelegateModelPrivate::group_at( - QQmlListProperty<QQmlDelegateModelGroup> *property, int index) -{ - QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data); - return index >= 0 && index < d->m_groupCount - 1 - ? d->m_groups[index + 1] - : nullptr; -} - -/*! - \qmlproperty list<DelegateModelGroup> QtQml.Models::DelegateModel::groups - - This property holds a delegate model's group definitions. - - Groups define a sub-set of the items in a delegate model and can be used to filter - a model. - - For every group defined in a DelegateModel two attached properties are added to each - delegate item. The first of the form DelegateModel.in\e{GroupName} holds whether the - item belongs to the group and the second DelegateModel.\e{groupName}Index holds the - index of the item in that group. - - The following example illustrates using groups to select items in a model. - - \snippet delegatemodel/delegatemodelgroup.qml 0 - \keyword dm-groups-property -*/ - -QQmlListProperty<QQmlDelegateModelGroup> QQmlDelegateModel::groups() -{ - Q_D(QQmlDelegateModel); - return QQmlListProperty<QQmlDelegateModelGroup>( - this, - d, - QQmlDelegateModelPrivate::group_append, - QQmlDelegateModelPrivate::group_count, - QQmlDelegateModelPrivate::group_at, - nullptr); -} - -/*! - \qmlproperty DelegateModelGroup QtQml.Models::DelegateModel::items - - This property holds default group to which all new items are added. -*/ - -QQmlDelegateModelGroup *QQmlDelegateModel::items() -{ - Q_D(QQmlDelegateModel); - return d->m_items; -} - -/*! - \qmlproperty DelegateModelGroup QtQml.Models::DelegateModel::persistedItems - - This property holds delegate model's persisted items group. - - Items in this group are not destroyed when released by a view, instead they are persisted - until removed from the group. - - An item can be removed from the persistedItems group by setting the - DelegateModel.inPersistedItems property to false. If the item is not referenced by a view - at that time it will be destroyed. Adding an item to this group will not create a new - instance. - - Items returned by the \l QtQml.Models::DelegateModelGroup::create() function are automatically added - to this group. -*/ - -QQmlDelegateModelGroup *QQmlDelegateModel::persistedItems() -{ - Q_D(QQmlDelegateModel); - return d->m_persistedItems; -} - -/*! - \qmlproperty string QtQml.Models::DelegateModel::filterOnGroup - - This property holds name of the group that is used to filter the delegate model. - - Only items that belong to this group are visible to a view. - - By default this is the \l items group. -*/ - -QString QQmlDelegateModel::filterGroup() const -{ - Q_D(const QQmlDelegateModel); - return d->m_filterGroup; -} - -void QQmlDelegateModel::setFilterGroup(const QString &group) -{ - Q_D(QQmlDelegateModel); - - if (d->m_transaction) { - qmlWarning(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); - return; - } - - if (d->m_filterGroup != group) { - d->m_filterGroup = group; - d->updateFilterGroup(); - emit filterGroupChanged(); - } -} - -void QQmlDelegateModel::resetFilterGroup() -{ - setFilterGroup(QStringLiteral("items")); -} - -void QQmlDelegateModelPrivate::updateFilterGroup() -{ - Q_Q(QQmlDelegateModel); - if (!m_cacheMetaType) - return; - - QQmlListCompositor::Group previousGroup = m_compositorGroup; - m_compositorGroup = Compositor::Default; - for (int i = 1; i < m_groupCount; ++i) { - if (m_filterGroup == m_cacheMetaType->groupNames.at(i - 1)) { - m_compositorGroup = Compositor::Group(i); - break; - } - } - - QQmlDelegateModelGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this); - if (m_compositorGroup != previousGroup) { - QVector<QQmlChangeSet::Change> removes; - QVector<QQmlChangeSet::Change> inserts; - m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); - - QQmlChangeSet changeSet; - changeSet.move(removes, inserts); - emit q->modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit q->countChanged(); - - if (m_parts) { - auto partsCopy = m_parts->models; // deliberate; this may alter m_parts - for (QQmlPartsModel *model : qAsConst(partsCopy)) - model->updateFilterGroup(m_compositorGroup, changeSet); - } - } -} - -/*! - \qmlproperty object QtQml.Models::DelegateModel::parts - - The \a parts property selects a DelegateModel which creates - delegates from the part named. This is used in conjunction with - the \l Package type. - - For example, the code below selects a model which creates - delegates named \e list from a \l Package: - - \code - DelegateModel { - id: visualModel - delegate: Package { - Item { Package.name: "list" } - } - model: myModel - } - - ListView { - width: 200; height:200 - model: visualModel.parts.list - } - \endcode - - \sa Package -*/ - -QObject *QQmlDelegateModel::parts() -{ - Q_D(QQmlDelegateModel); - if (!d->m_parts) - d->m_parts = new QQmlDelegateModelParts(this); - return d->m_parts; -} - -const QAbstractItemModel *QQmlDelegateModel::abstractItemModel() const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.adaptsAim() ? d->m_adaptorModel.aim() : nullptr; -} - -void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->createdPackage(incubationTask->index[i], package); -} - -void QQmlDelegateModelPrivate::emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->initPackage(incubationTask->index[i], package); -} - -void QQmlDelegateModelPrivate::emitDestroyingPackage(QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->destroyingPackage(package); -} - -static bool isDoneIncubating(QQmlIncubator::Status status) -{ - return status == QQmlIncubator::Ready || status == QQmlIncubator::Error; -} - -void QQDMIncubationTask::statusChanged(Status status) -{ - if (vdm) { - vdm->incubatorStatusChanged(this, status); - } else if (isDoneIncubating(status)) { - Q_ASSERT(incubating); - // The model was deleted from under our feet, cleanup ourselves - delete incubating->object; - incubating->object = nullptr; - if (incubating->contextData) { - incubating->contextData->invalidate(); - Q_ASSERT(incubating->contextData->refCount == 1); - incubating->contextData = nullptr; - } - incubating->scriptRef = 0; - incubating->deleteLater(); - } -} - -void QQmlDelegateModelPrivate::releaseIncubator(QQDMIncubationTask *incubationTask) -{ - Q_Q(QQmlDelegateModel); - if (!incubationTask->isError()) - incubationTask->clear(); - m_finishedIncubating.append(incubationTask); - if (!m_incubatorCleanupScheduled) { - m_incubatorCleanupScheduled = true; - QCoreApplication::postEvent(q, new QEvent(QEvent::User)); - } -} - -void QQmlDelegateModelPrivate::addCacheItem(QQmlDelegateModelItem *item, Compositor::iterator it) -{ - m_cache.insert(it.cacheIndex, item); - m_compositor.setFlags(it, 1, Compositor::CacheFlag); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); -} - -void QQmlDelegateModelPrivate::removeCacheItem(QQmlDelegateModelItem *cacheItem) -{ - int cidx = m_cache.lastIndexOf(cacheItem); - if (cidx >= 0) { - m_compositor.clearFlags(Compositor::Cache, cidx, 1, Compositor::CacheFlag); - m_cache.removeAt(cidx); - } - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); -} - -void QQmlDelegateModelPrivate::incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status) -{ - if (!isDoneIncubating(status)) - return; - - const QList<QQmlError> incubationTaskErrors = incubationTask->errors(); - - QQmlDelegateModelItem *cacheItem = incubationTask->incubating; - cacheItem->incubationTask = nullptr; - incubationTask->incubating = nullptr; - releaseIncubator(incubationTask); - - if (status == QQmlIncubator::Ready) { - cacheItem->referenceObject(); - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object)) - emitCreatedPackage(incubationTask, package); - else - emitCreatedItem(incubationTask, cacheItem->object); - cacheItem->releaseObject(); - } else if (status == QQmlIncubator::Error) { - qmlInfo(m_delegate, incubationTaskErrors + m_delegate->errors()) << "Cannot create delegate"; - } - - if (!cacheItem->isObjectReferenced()) { - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object)) - emitDestroyingPackage(package); - else - emitDestroyingItem(cacheItem->object); - delete cacheItem->object; - cacheItem->object = nullptr; - cacheItem->scriptRef -= 1; - if (cacheItem->contextData) { - cacheItem->contextData->invalidate(); - Q_ASSERT(cacheItem->contextData->refCount == 1); - } - cacheItem->contextData = nullptr; - - if (!cacheItem->isReferenced()) { - removeCacheItem(cacheItem); - delete cacheItem; - } - } -} - -void QQDMIncubationTask::setInitialState(QObject *o) -{ - vdm->setInitialState(this, o); -} - -void QQmlDelegateModelPrivate::setInitialState(QQDMIncubationTask *incubationTask, QObject *o) -{ - QQmlDelegateModelItem *cacheItem = incubationTask->incubating; - cacheItem->object = o; - - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object)) - emitInitPackage(incubationTask, package); - else - emitInitItem(incubationTask, cacheItem->object); -} - -QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQmlIncubator::IncubationMode incubationMode) -{ - if (!m_delegate || index < 0 || index >= m_compositor.count(group)) { - qWarning() << "DelegateModel::item: index out range" << index << m_compositor.count(group); - return nullptr; - } else if (!m_context || !m_context->isValid()) { - return nullptr; - } - - Compositor::iterator it = m_compositor.find(group, index); - - QQmlDelegateModelItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0; - - if (!cacheItem) { - cacheItem = m_adaptorModel.createItem(m_cacheMetaType, it.modelIndex()); - if (!cacheItem) - return nullptr; - - cacheItem->groups = it->flags; - addCacheItem(cacheItem, it); - } - - // Bump the reference counts temporarily so neither the content data or the delegate object - // are deleted if incubatorStatusChanged() is called synchronously. - cacheItem->scriptRef += 1; - cacheItem->referenceObject(); - - if (cacheItem->incubationTask) { - bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested); - if (sync && cacheItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) { - // previously requested async - now needed immediately - cacheItem->incubationTask->forceCompletion(); - } - } else if (!cacheItem->object) { - QQmlComponent *delegate = m_delegate; - if (m_delegateChooser) { - QQmlAbstractDelegateComponent *chooser = m_delegateChooser; - do { - delegate = chooser->delegate(&m_adaptorModel, index); - chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate); - } while (chooser); - if (!delegate) - return nullptr; - } - - QQmlContext *creationContext = delegate->creationContext(); - - cacheItem->scriptRef += 1; - - cacheItem->incubationTask = new QQDMIncubationTask(this, incubationMode); - cacheItem->incubationTask->incubating = cacheItem; - cacheItem->incubationTask->clear(); - - for (int i = 1; i < m_groupCount; ++i) - cacheItem->incubationTask->index[i] = it.index[i]; - - QQmlContextData *ctxt = new QQmlContextData; - ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_context.data())); - ctxt->contextObject = cacheItem; - cacheItem->contextData = ctxt; - - if (m_adaptorModel.hasProxyObject()) { - if (QQmlAdaptorModelProxyInterface *proxy - = qobject_cast<QQmlAdaptorModelProxyInterface *>(cacheItem)) { - ctxt = new QQmlContextData; - ctxt->setParent(cacheItem->contextData, /*stronglyReferencedByParent*/true); - QObject *proxied = proxy->proxiedObject(); - ctxt->contextObject = proxied; - // We don't own the proxied object. We need to clear it if it goes away. - QObject::connect(proxied, &QObject::destroyed, - cacheItem, &QQmlDelegateModelItem::childContextObjectDestroyed); - } - } - - QQmlComponentPrivate *cp = QQmlComponentPrivate::get(delegate); - cp->incubateObject( - cacheItem->incubationTask, - delegate, - m_context->engine(), - ctxt, - QQmlContextData::get(m_context)); - } - - if (index == m_compositor.count(group) - 1) - requestMoreIfNecessary(); - - // Remove the temporary reference count. - cacheItem->scriptRef -= 1; - if (cacheItem->object && (!cacheItem->incubationTask || isDoneIncubating(cacheItem->incubationTask->status()))) - return cacheItem->object; - - cacheItem->releaseObject(); - if (!cacheItem->isReferenced()) { - removeCacheItem(cacheItem); - delete cacheItem; - } - - return nullptr; -} - -/* - If asynchronous is true or the component is being loaded asynchronously due - to an ancestor being loaded asynchronously, object() may return 0. In this - case createdItem() will be emitted when the object is available. The object - at this stage does not have any references, so object() must be called again - to ensure a reference is held. Any call to object() which returns a valid object - must be matched by a call to release() in order to destroy the object. -*/ -QObject *QQmlDelegateModel::object(int index, QQmlIncubator::IncubationMode incubationMode) -{ - Q_D(QQmlDelegateModel); - if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { - qWarning() << "DelegateModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup); - return nullptr; - } - - return d->object(d->m_compositorGroup, index, incubationMode); -} - -QQmlIncubator::Status QQmlDelegateModel::incubationStatus(int index) -{ - Q_D(QQmlDelegateModel); - Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index); - if (!it->inCache()) - return QQmlIncubator::Null; - - if (auto incubationTask = d->m_cache.at(it.cacheIndex)->incubationTask) - return incubationTask->status(); - - return QQmlIncubator::Ready; -} - -QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name) -{ - Compositor::iterator it = m_compositor.find(group, index); - if (QQmlAdaptorModel *model = it.list<QQmlAdaptorModel>()) { - QString role = name; - int dot = name.indexOf(QLatin1Char('.')); - if (dot > 0) - role = name.left(dot); - QVariant value = model->value(it.modelIndex(), role); - while (dot > 0) { - QObject *obj = qvariant_cast<QObject*>(value); - if (!obj) - return QString(); - int from = dot+1; - dot = name.indexOf(QLatin1Char('.'), from); - value = obj->property(name.midRef(from, dot - from).toUtf8()); - } - return value.toString(); - } - return QString(); -} - -QString QQmlDelegateModel::stringValue(int index, const QString &name) -{ - Q_D(QQmlDelegateModel); - return d->stringValue(d->m_compositorGroup, index, name); -} - -int QQmlDelegateModel::indexOf(QObject *item, QObject *) const -{ - Q_D(const QQmlDelegateModel); - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(item)) - return cacheItem->groupIndex(d->m_compositorGroup); - return -1; -} - -void QQmlDelegateModel::setWatchedRoles(const QList<QByteArray> &roles) -{ - Q_D(QQmlDelegateModel); - d->m_adaptorModel.replaceWatchedRoles(d->m_watchedRoles, roles); - d->m_watchedRoles = roles; -} - -void QQmlDelegateModelPrivate::addGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector<Compositor::Insert> inserts; - m_compositor.setFlags(from, count, group, groupFlags, &inserts); - itemsInserted(inserts); - emitChanges(); -} - -void QQmlDelegateModelPrivate::removeGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector<Compositor::Remove> removes; - m_compositor.clearFlags(from, count, group, groupFlags, &removes); - itemsRemoved(removes); - emitChanges(); -} - -void QQmlDelegateModelPrivate::setGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector<Compositor::Remove> removes; - QVector<Compositor::Insert> inserts; - - m_compositor.setFlags(from, count, group, groupFlags, &inserts); - itemsInserted(inserts); - const int removeFlags = ~groupFlags & Compositor::GroupMask; - - from = m_compositor.find(from.group, from.index[from.group]); - m_compositor.clearFlags(from, count, group, removeFlags, &removes); - itemsRemoved(removes); - emitChanges(); -} - -bool QQmlDelegateModel::event(QEvent *e) -{ - Q_D(QQmlDelegateModel); - if (e->type() == QEvent::UpdateRequest) { - d->m_waitingToFetchMore = false; - d->m_adaptorModel.fetchMore(); - } else if (e->type() == QEvent::User) { - d->m_incubatorCleanupScheduled = false; - qDeleteAll(d->m_finishedIncubating); - d->m_finishedIncubating.clear(); - } - return QQmlInstanceModel::event(e); -} - -void QQmlDelegateModelPrivate::itemsChanged(const QVector<Compositor::Change> &changes) -{ - if (!m_delegate) - return; - - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedChanges(m_groupCount); - - for (const Compositor::Change &change : changes) { - for (int i = 1; i < m_groupCount; ++i) { - if (change.inGroup(i)) { - translatedChanges[i].append(QQmlChangeSet::Change(change.index[i], change.count)); - } - } - } - - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.change(translatedChanges.at(i)); -} - -void QQmlDelegateModel::_q_itemsChanged(int index, int count, const QVector<int> &roles) -{ - Q_D(QQmlDelegateModel); - if (count <= 0 || !d->m_complete) - return; - - if (d->m_adaptorModel.notify(d->m_cache, index, count, roles)) { - QVector<Compositor::Change> changes; - d->m_compositor.listItemsChanged(&d->m_adaptorModel, index, count, &changes); - d->itemsChanged(changes); - d->emitChanges(); - } -} - -static void incrementIndexes(QQmlDelegateModelItem *cacheItem, int count, const int *deltas) -{ - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < count; ++i) - incubationTask->index[i] += deltas[i]; - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < qMin<int>(count, Compositor::MaximumGroupCount); ++i) - attached->m_currentIndex[i] += deltas[i]; - } -} - -void QQmlDelegateModelPrivate::itemsInserted( - const QVector<Compositor::Insert> &inserts, - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> *translatedInserts, - QHash<int, QList<QQmlDelegateModelItem *> > *movedItems) -{ - int cacheIndex = 0; - - int inserted[Compositor::MaximumGroupCount]; - for (int i = 1; i < m_groupCount; ++i) - inserted[i] = 0; - - for (const Compositor::Insert &insert : inserts) { - for (; cacheIndex < insert.cacheIndex; ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted); - - for (int i = 1; i < m_groupCount; ++i) { - if (insert.inGroup(i)) { - (*translatedInserts)[i].append( - QQmlChangeSet::Change(insert.index[i], insert.count, insert.moveId)); - inserted[i] += insert.count; - } - } - - if (!insert.inCache()) - continue; - - if (movedItems && insert.isMove()) { - QList<QQmlDelegateModelItem *> items = movedItems->take(insert.moveId); - Q_ASSERT(items.count() == insert.count); - m_cache = m_cache.mid(0, insert.cacheIndex) + items + m_cache.mid(insert.cacheIndex); - } - if (insert.inGroup()) { - for (int offset = 0; cacheIndex < insert.cacheIndex + insert.count; ++cacheIndex, ++offset) { - QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); - cacheItem->groups |= insert.flags & Compositor::GroupMask; - - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < m_groupCount; ++i) - incubationTask->index[i] = cacheItem->groups & (1 << i) - ? insert.index[i] + offset - : insert.index[i]; - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) - attached->m_currentIndex[i] = cacheItem->groups & (1 << i) - ? insert.index[i] + offset - : insert.index[i]; - } - } - } else { - cacheIndex = insert.cacheIndex + insert.count; - } - } - for (const QList<QQmlDelegateModelItem *> cache = m_cache; cacheIndex < cache.count(); ++cacheIndex) - incrementIndexes(cache.at(cacheIndex), m_groupCount, inserted); -} - -void QQmlDelegateModelPrivate::itemsInserted(const QVector<Compositor::Insert> &inserts) -{ - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); - itemsInserted(inserts, &translatedInserts); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert(translatedInserts.at(i)); -} - -void QQmlDelegateModel::_q_itemsInserted(int index, int count) -{ - - Q_D(QQmlDelegateModel); - if (count <= 0 || !d->m_complete) - return; - - d->m_count += count; - - const QList<QQmlDelegateModelItem *> cache = d->m_cache; - for (int i = 0, c = cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = cache.at(i); - // layout change triggered by changing the modelIndex might have - // already invalidated this item in d->m_cache and deleted it. - if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item)) - continue; - - if (item->modelIndex() >= index) { - const int newIndex = item->modelIndex() + count; - const int row = newIndex; - const int column = 0; - item->setModelIndex(newIndex, row, column); - } - } - - QVector<Compositor::Insert> inserts; - d->m_compositor.listItemsInserted(&d->m_adaptorModel, index, count, &inserts); - d->itemsInserted(inserts); - d->emitChanges(); -} - -//### This method should be split in two. It will remove delegates, and it will re-render the list. -// When e.g. QQmlListModel::remove is called, the removal of the delegates should be done on -// QAbstractItemModel::rowsAboutToBeRemoved, and the re-rendering on -// QAbstractItemModel::rowsRemoved. Currently both are done on the latter signal. The problem is -// that the destruction of an item will emit a changed signal that ends up at the delegate, which -// in turn will try to load the data from the model (which should have already freed it), resulting -// in a use-after-free. See QTBUG-59256. -void QQmlDelegateModelPrivate::itemsRemoved( - const QVector<Compositor::Remove> &removes, - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> *translatedRemoves, - QHash<int, QList<QQmlDelegateModelItem *> > *movedItems) -{ - int cacheIndex = 0; - int removedCache = 0; - - int removed[Compositor::MaximumGroupCount]; - for (int i = 1; i < m_groupCount; ++i) - removed[i] = 0; - - for (const Compositor::Remove &remove : removes) { - for (; cacheIndex < remove.cacheIndex; ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); - - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) { - (*translatedRemoves)[i].append( - QQmlChangeSet::Change(remove.index[i], remove.count, remove.moveId)); - removed[i] -= remove.count; - } - } - - if (!remove.inCache()) - continue; - - if (movedItems && remove.isMove()) { - movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex, remove.count)); - QList<QQmlDelegateModelItem *>::iterator begin = m_cache.begin() + remove.cacheIndex; - QList<QQmlDelegateModelItem *>::iterator end = begin + remove.count; - m_cache.erase(begin, end); - } else { - for (; cacheIndex < remove.cacheIndex + remove.count - removedCache; ++cacheIndex) { - QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); - if (remove.inGroup(Compositor::Persisted) && cacheItem->objectRef == 0 && cacheItem->object) { - QObject *object = cacheItem->object; - cacheItem->destroyObject(); - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) - emitDestroyingPackage(package); - else - emitDestroyingItem(object); - cacheItem->scriptRef -= 1; - } - if (!cacheItem->isReferenced()) { - m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); - m_cache.removeAt(cacheIndex); - delete cacheItem; - --cacheIndex; - ++removedCache; - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - } else if (remove.groups() == cacheItem->groups) { - cacheItem->groups = 0; - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < m_groupCount; ++i) - incubationTask->index[i] = -1; - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) - attached->m_currentIndex[i] = -1; - } - } else { - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - if (!cacheItem->isObjectReferenced()) { - releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = nullptr; - if (cacheItem->object) { - QObject *object = cacheItem->object; - cacheItem->destroyObject(); - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) - emitDestroyingPackage(package); - else - emitDestroyingItem(object); - } - cacheItem->scriptRef -= 1; - } else { - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) - incubationTask->index[i] = remove.index[i]; - } - } - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) - attached->m_currentIndex[i] = remove.index[i]; - } - } - cacheItem->groups &= ~remove.flags; - } - } - } - } - - for (const QList<QQmlDelegateModelItem *> cache = m_cache; cacheIndex < cache.count(); ++cacheIndex) - incrementIndexes(cache.at(cacheIndex), m_groupCount, removed); -} - -void QQmlDelegateModelPrivate::itemsRemoved(const QVector<Compositor::Remove> &removes) -{ - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); - itemsRemoved(removes, &translatedRemoves); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove(translatedRemoves.at(i)); -} - -void QQmlDelegateModel::_q_itemsRemoved(int index, int count) -{ - Q_D(QQmlDelegateModel); - if (count <= 0|| !d->m_complete) - return; - - d->m_count -= count; - const QList<QQmlDelegateModelItem *> cache = d->m_cache; - for (int i = 0, c = cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = cache.at(i); - // layout change triggered by removal of a previous item might have - // already invalidated this item in d->m_cache and deleted it - if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item)) - continue; - - if (item->modelIndex() >= index + count) { - const int newIndex = item->modelIndex() - count; - const int row = newIndex; - const int column = 0; - item->setModelIndex(newIndex, row, column); - } else if (item->modelIndex() >= index) { - item->setModelIndex(-1, -1, -1); - } - } - - QVector<Compositor::Remove> removes; - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, index, count, &removes); - d->itemsRemoved(removes); - - d->emitChanges(); -} - -void QQmlDelegateModelPrivate::itemsMoved( - const QVector<Compositor::Remove> &removes, const QVector<Compositor::Insert> &inserts) -{ - QHash<int, QList<QQmlDelegateModelItem *> > movedItems; - - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); - itemsRemoved(removes, &translatedRemoves, &movedItems); - - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); - itemsInserted(inserts, &translatedInserts, &movedItems); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - Q_ASSERT(movedItems.isEmpty()); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) { - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.move( - translatedRemoves.at(i), - translatedInserts.at(i)); - } -} - -void QQmlDelegateModel::_q_itemsMoved(int from, int to, int count) -{ - Q_D(QQmlDelegateModel); - if (count <= 0 || !d->m_complete) - return; - - const int minimum = qMin(from, to); - const int maximum = qMax(from, to) + count; - const int difference = from > to ? count : -count; - - const QList<QQmlDelegateModelItem *> cache = d->m_cache; - for (int i = 0, c = cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = cache.at(i); - // layout change triggered by changing the modelIndex might have - // already invalidated this item in d->m_cache and deleted it. - if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item)) - continue; - - if (item->modelIndex() >= from && item->modelIndex() < from + count) { - const int newIndex = item->modelIndex() - from + to; - const int row = newIndex; - const int column = 0; - item->setModelIndex(newIndex, row, column); - } else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) { - const int newIndex = item->modelIndex() + difference; - const int row = newIndex; - const int column = 0; - item->setModelIndex(newIndex, row, column); - } - } - - QVector<Compositor::Remove> removes; - QVector<Compositor::Insert> inserts; - d->m_compositor.listItemsMoved(&d->m_adaptorModel, from, to, count, &removes, &inserts); - d->itemsMoved(removes, inserts); - d->emitChanges(); -} - -void QQmlDelegateModelPrivate::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) -{ - Q_Q(QQmlDelegateModel); - emit q->modelUpdated(changeSet, reset); - if (changeSet.difference() != 0) - emit q->countChanged(); -} - -void QQmlDelegateModelPrivate::delegateChanged(bool add, bool remove) -{ - Q_Q(QQmlDelegateModel); - if (!m_complete) - return; - - if (m_transaction) { - qmlWarning(q) << QQmlDelegateModel::tr("The delegates of a DelegateModel cannot be changed within onUpdated."); - return; - } - - if (remove) { - for (int i = 1; i < m_groupCount; ++i) { - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove( - 0, m_compositor.count(Compositor::Group(i))); - } - } - if (add) { - for (int i = 1; i < m_groupCount; ++i) { - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert( - 0, m_compositor.count(Compositor::Group(i))); - } - } - emitChanges(); -} - -void QQmlDelegateModelPrivate::emitChanges() -{ - if (m_transaction || !m_complete || !m_context || !m_context->isValid()) - return; - - m_transaction = true; - QV4::ExecutionEngine *engine = m_context->engine()->handle(); - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitChanges(engine); - m_transaction = false; - - const bool reset = m_reset; - m_reset = false; - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); - - auto cacheCopy = m_cache; // deliberate; emitChanges may alter m_cache - for (QQmlDelegateModelItem *cacheItem : qAsConst(cacheCopy)) { - if (cacheItem->attached) - cacheItem->attached->emitChanges(); - } -} - -void QQmlDelegateModel::_q_modelReset() -{ - Q_D(QQmlDelegateModel); - if (!d->m_delegate) - return; - - int oldCount = d->m_count; - d->m_adaptorModel.rootIndex = QModelIndex(); - - if (d->m_complete) { - d->m_count = d->adaptorModelCount(); - - const QList<QQmlDelegateModelItem *> cache = d->m_cache; - for (int i = 0, c = cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = cache.at(i); - // layout change triggered by changing the modelIndex might have - // already invalidated this item in d->m_cache and deleted it. - if (!d->m_cache.isSharedWith(cache) && !d->m_cache.contains(item)) - continue; - - if (item->modelIndex() != -1) - item->setModelIndex(-1, -1, -1); - } - - QVector<Compositor::Remove> removes; - QVector<Compositor::Insert> inserts; - if (oldCount) - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); - if (d->m_count) - d->m_compositor.listItemsInserted(&d->m_adaptorModel, 0, d->m_count, &inserts); - d->itemsMoved(removes, inserts); - d->m_reset = true; - - if (d->m_adaptorModel.canFetchMore()) - d->m_adaptorModel.fetchMore(); - - d->emitChanges(); - } - emit rootIndexChanged(); -} - -void QQmlDelegateModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQmlDelegateModel); - if (parent == d->m_adaptorModel.rootIndex) - _q_itemsInserted(begin, end - begin + 1); -} - -void QQmlDelegateModel::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQmlDelegateModel); - if (!d->m_adaptorModel.rootIndex.isValid()) - return; - const QModelIndex index = d->m_adaptorModel.rootIndex; - if (index.parent() == parent && index.row() >= begin && index.row() <= end) { - const int oldCount = d->m_count; - d->m_count = 0; - d->disconnectFromAbstractItemModel(); - d->m_adaptorModel.invalidateModel(); - - if (d->m_complete && oldCount > 0) { - QVector<Compositor::Remove> removes; - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); - d->itemsRemoved(removes); - d->emitChanges(); - } - } -} - -void QQmlDelegateModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQmlDelegateModel); - if (parent == d->m_adaptorModel.rootIndex) - _q_itemsRemoved(begin, end - begin + 1); -} - -void QQmlDelegateModel::_q_rowsMoved( - const QModelIndex &sourceParent, int sourceStart, int sourceEnd, - const QModelIndex &destinationParent, int destinationRow) -{ - Q_D(QQmlDelegateModel); - const int count = sourceEnd - sourceStart + 1; - if (destinationParent == d->m_adaptorModel.rootIndex && sourceParent == d->m_adaptorModel.rootIndex) { - _q_itemsMoved(sourceStart, sourceStart > destinationRow ? destinationRow : destinationRow - count, count); - } else if (sourceParent == d->m_adaptorModel.rootIndex) { - _q_itemsRemoved(sourceStart, count); - } else if (destinationParent == d->m_adaptorModel.rootIndex) { - _q_itemsInserted(destinationRow, count); - } -} - -void QQmlDelegateModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles) -{ - Q_D(QQmlDelegateModel); - if (begin.parent() == d->m_adaptorModel.rootIndex) - _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, roles); -} - -bool QQmlDelegateModel::isDescendantOf(const QPersistentModelIndex& desc, const QList< QPersistentModelIndex >& parents) const -{ - for (int i = 0, c = parents.count(); i < c; ++i) { - for (QPersistentModelIndex parent = desc; parent.isValid(); parent = parent.parent()) { - if (parent == parents[i]) - return true; - } - } - - return false; -} - -void QQmlDelegateModel::_q_layoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint) -{ - Q_D(QQmlDelegateModel); - if (!d->m_complete) - return; - - if (hint == QAbstractItemModel::VerticalSortHint) { - if (!parents.isEmpty() && d->m_adaptorModel.rootIndex.isValid() && !isDescendantOf(d->m_adaptorModel.rootIndex, parents)) { - return; - } - - // mark all items as changed - _q_itemsChanged(0, d->m_count, QVector<int>()); - - } else if (hint == QAbstractItemModel::HorizontalSortHint) { - // Ignored - } else { - // We don't know what's going on, so reset the model - _q_modelReset(); - } -} - -QQmlDelegateModelAttached *QQmlDelegateModel::qmlAttachedProperties(QObject *obj) -{ - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(obj)) { - if (cacheItem->object == obj) { // Don't create attached item for child objects. - cacheItem->attached = new QQmlDelegateModelAttached(cacheItem, obj); - return cacheItem->attached; - } - } - return new QQmlDelegateModelAttached(obj); -} - -bool QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups) -{ - if (!m_context || !m_context->isValid()) - return false; - - QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, -1); - if (!cacheItem) - return false; - if (!object.isObject()) - return false; - - QV4::ExecutionEngine *v4 = object.as<QV4::Object>()->engine(); - QV4::Scope scope(v4); - QV4::ScopedObject o(scope, object); - if (!o) - return false; - - QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly); - QV4::ScopedValue propertyName(scope); - QV4::ScopedValue v(scope); - while (1) { - propertyName = it.nextPropertyNameAsString(v); - if (propertyName->isNull()) - break; - cacheItem->setValue(propertyName->toQStringNoThrow(), scope.engine->toVariant(v, QVariant::Invalid)); - } - - cacheItem->groups = groups | Compositor::UnresolvedFlag | Compositor::CacheFlag; - - // Must be before the new object is inserted into the cache or its indexes will be adjusted too. - itemsInserted(QVector<Compositor::Insert>(1, Compositor::Insert(before, 1, cacheItem->groups & ~Compositor::CacheFlag))); - - before = m_compositor.insert(before, nullptr, 0, 1, cacheItem->groups); - m_cache.insert(before.cacheIndex, cacheItem); - - return true; -} - -//============================================================================ - -QQmlDelegateModelItemMetaType::QQmlDelegateModelItemMetaType( - QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames) - : model(model) - , groupCount(groupNames.count() + 1) - , v4Engine(engine) - , metaObject(nullptr) - , groupNames(groupNames) -{ -} - -QQmlDelegateModelItemMetaType::~QQmlDelegateModelItemMetaType() -{ - if (metaObject) - metaObject->release(); -} - -void QQmlDelegateModelItemMetaType::initializeMetaObject() -{ - QMetaObjectBuilder builder; - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - builder.setClassName(QQmlDelegateModelAttached::staticMetaObject.className()); - builder.setSuperClass(&QQmlDelegateModelAttached::staticMetaObject); - - int notifierId = 0; - for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - QString propertyName = QLatin1String("in") + groupNames.at(i); - propertyName.replace(2, 1, propertyName.at(2).toUpper()); - builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); - QMetaPropertyBuilder propertyBuilder = builder.addProperty( - propertyName.toUtf8(), "bool", notifierId); - propertyBuilder.setWritable(true); - } - for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - const QString propertyName = groupNames.at(i) + QLatin1String("Index"); - builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); - QMetaPropertyBuilder propertyBuilder = builder.addProperty( - propertyName.toUtf8(), "int", notifierId); - propertyBuilder.setWritable(true); - } - - metaObject = new QQmlDelegateModelAttachedMetaObject(this, builder.toMetaObject()); -} - -void QQmlDelegateModelItemMetaType::initializePrototype() -{ - QV4::Scope scope(v4Engine); - - QV4::ScopedObject proto(scope, v4Engine->newObject()); - proto->defineAccessorProperty(QStringLiteral("model"), QQmlDelegateModelItem::get_model, nullptr); - proto->defineAccessorProperty(QStringLiteral("groups"), QQmlDelegateModelItem::get_groups, QQmlDelegateModelItem::set_groups); - QV4::ScopedString s(scope); - QV4::ScopedProperty p(scope); - - s = v4Engine->newString(QStringLiteral("isUnresolved")); - QV4::ScopedFunctionObject f(scope); - QV4::ExecutionContext *global = scope.engine->rootContext(); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, 30, QQmlDelegateModelItem::get_member))); - p->setSetter(nullptr); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - s = v4Engine->newString(QStringLiteral("inItems")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Default, QQmlDelegateModelItem::get_member))); - p->setSetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Default, QQmlDelegateModelItem::set_member))); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - s = v4Engine->newString(QStringLiteral("inPersistedItems")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_member))); - p->setSetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Persisted, QQmlDelegateModelItem::set_member))); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - s = v4Engine->newString(QStringLiteral("itemsIndex")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Default, QQmlDelegateModelItem::get_index))); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - s = v4Engine->newString(QStringLiteral("persistedItemsIndex")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_index))); - p->setSetter(nullptr); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - for (int i = 2; i < groupNames.count(); ++i) { - QString propertyName = QLatin1String("in") + groupNames.at(i); - propertyName.replace(2, 1, propertyName.at(2).toUpper()); - s = v4Engine->newString(propertyName); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_member))); - p->setSetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::set_member))); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - } - for (int i = 2; i < groupNames.count(); ++i) { - const QString propertyName = groupNames.at(i) + QLatin1String("Index"); - s = v4Engine->newString(propertyName); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_index))); - p->setSetter(nullptr); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - } - modelItemProto.set(v4Engine, proto); -} - -int QQmlDelegateModelItemMetaType::parseGroups(const QStringList &groups) const -{ - int groupFlags = 0; - for (const QString &groupName : groups) { - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } - return groupFlags; -} - -int QQmlDelegateModelItemMetaType::parseGroups(const QV4::Value &groups) const -{ - int groupFlags = 0; - QV4::Scope scope(v4Engine); - - QV4::ScopedString s(scope, groups); - if (s) { - const QString groupName = s->toQString(); - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - return groupFlags; - } - - QV4::ScopedArrayObject array(scope, groups); - if (array) { - QV4::ScopedValue v(scope); - uint arrayLength = array->getLength(); - for (uint i = 0; i < arrayLength; ++i) { - v = array->get(i); - const QString groupName = v->toQString(); - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } - } - return groupFlags; -} - -QV4::ReturnedValue QQmlDelegateModelItem::get_model(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) -{ - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - return b->engine()->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - if (!o->d()->item->metaType->model) - RETURN_UNDEFINED(); - - return o->d()->item->get(); -} - -QV4::ReturnedValue QQmlDelegateModelItem::get_groups(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) -{ - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - - QStringList groups; - for (int i = 1; i < o->d()->item->metaType->groupCount; ++i) { - if (o->d()->item->groups & (1 << i)) - groups.append(o->d()->item->metaType->groupNames.at(i - 1)); - } - - return scope.engine->fromVariant(groups); -} - -QV4::ReturnedValue QQmlDelegateModelItem::set_groups(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) -{ - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - - if (!argc) - THROW_TYPE_ERROR(); - - if (!o->d()->item->metaType->model) - RETURN_UNDEFINED(); - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(o->d()->item->metaType->model); - - const int groupFlags = model->m_cacheMetaType->parseGroups(argv[0]); - const int cacheIndex = model->m_cache.indexOf(o->d()->item); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - model->setGroups(it, 1, Compositor::Cache, groupFlags); - return QV4::Encode::undefined(); -} - -QV4::ReturnedValue QQmlDelegateModelItem::get_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &) -{ - return QV4::Encode(bool(thisItem->groups & (1 << flag))); -} - -QV4::ReturnedValue QQmlDelegateModelItem::set_member(QQmlDelegateModelItem *cacheItem, uint flag, const QV4::Value &arg) -{ - if (!cacheItem->metaType->model) - return QV4::Encode::undefined(); - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model); - - bool member = arg.toBoolean(); - uint groupFlag = (1 << flag); - if (member == ((cacheItem->groups & groupFlag) != 0)) - return QV4::Encode::undefined(); - - const int cacheIndex = model->m_cache.indexOf(cacheItem); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - if (member) - model->addGroups(it, 1, Compositor::Cache, groupFlag); - else - model->removeGroups(it, 1, Compositor::Cache, groupFlag); - return QV4::Encode::undefined(); -} - -QV4::ReturnedValue QQmlDelegateModelItem::get_index(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &) -{ - return QV4::Encode((int)thisItem->groupIndex(Compositor::Group(flag))); -} - -void QQmlDelegateModelItem::childContextObjectDestroyed(QObject *childContextObject) -{ - if (!contextData) - return; - - for (QQmlContextData *ctxt = contextData->childContexts; ctxt; ctxt = ctxt->nextChild) { - if (ctxt->contextObject == childContextObject) - ctxt->contextObject = nullptr; - } -} - - -//--------------------------------------------------------------------------- - -DEFINE_OBJECT_VTABLE(QQmlDelegateModelItemObject); - -void QV4::Heap::QQmlDelegateModelItemObject::destroy() -{ - item->Dispose(); - Object::destroy(); -} - - -QQmlDelegateModelItem::QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, - QQmlAdaptorModel::Accessors *accessor, - int modelIndex, int row, int column) - : v4(metaType->v4Engine) - , metaType(metaType) - , contextData(nullptr) - , object(nullptr) - , attached(nullptr) - , incubationTask(nullptr) - , delegate(nullptr) - , poolTime(0) - , objectRef(0) - , scriptRef(0) - , groups(0) - , index(modelIndex) - , row(row) - , column(column) -{ - metaType->addref(); - - if (accessor->propertyCache) { - // The property cache in the accessor is common for all the model - // items in the model it wraps. It describes available model roles, - // together with revisioned properties like row, column and index, all - // which should be available in the delegate. We assign this cache to the - // model item so that the QML engine can use the revision information - // when resolving the properties (rather than falling back to just - // inspecting the QObject in the model item directly). - QQmlData *qmldata = QQmlData::get(this, true); - if (qmldata->propertyCache) - qmldata->propertyCache->release(); - qmldata->propertyCache = accessor->propertyCache.data(); - qmldata->propertyCache->addref(); - } -} - -QQmlDelegateModelItem::~QQmlDelegateModelItem() -{ - Q_ASSERT(scriptRef == 0); - Q_ASSERT(objectRef == 0); - Q_ASSERT(!object); - - if (incubationTask) { - if (metaType->model) - QQmlDelegateModelPrivate::get(metaType->model)->releaseIncubator(incubationTask); - else - delete incubationTask; - } - - metaType->release(); - -} - -void QQmlDelegateModelItem::Dispose() -{ - --scriptRef; - if (isReferenced()) - return; - - if (metaType->model) { - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); - model->removeCacheItem(this); - } - delete this; -} - -void QQmlDelegateModelItem::setModelIndex(int idx, int newRow, int newColumn) -{ - const int prevIndex = index; - const int prevRow = row; - const int prevColumn = column; - - index = idx; - row = newRow; - column = newColumn; - - if (idx != prevIndex) - emit modelIndexChanged(); - if (row != prevRow) - emit rowChanged(); - if (column != prevColumn) - emit columnChanged(); -} - -void QQmlDelegateModelItem::destroyObject() -{ - Q_ASSERT(object); - Q_ASSERT(contextData); - - QQmlData *data = QQmlData::get(object); - Q_ASSERT(data); - if (data->ownContext) { - data->ownContext->clearContext(); - if (data->ownContext->contextObject == object) - data->ownContext->contextObject = nullptr; - data->ownContext = nullptr; - data->context = nullptr; - } - object->deleteLater(); - - if (attached) { - attached->m_cacheItem = nullptr; - attached = nullptr; - } - - contextData->invalidate(); - contextData = nullptr; - object = nullptr; -} - -QQmlDelegateModelItem *QQmlDelegateModelItem::dataForObject(QObject *object) -{ - QQmlData *d = QQmlData::get(object); - QQmlContextData *context = d ? d->context : nullptr; - for (context = context ? context->parent : nullptr; context; context = context->parent) { - if (QQmlDelegateModelItem *cacheItem = qobject_cast<QQmlDelegateModelItem *>( - context->contextObject)) { - return cacheItem; - } - } - return nullptr; -} - -int QQmlDelegateModelItem::groupIndex(Compositor::Group group) -{ - if (QQmlDelegateModelPrivate * const model = metaType->model - ? QQmlDelegateModelPrivate::get(metaType->model) - : nullptr) { - return model->m_compositor.find(Compositor::Cache, model->m_cache.indexOf(this)).index[group]; - } - return -1; -} - -//--------------------------------------------------------------------------- - -QQmlDelegateModelAttachedMetaObject::QQmlDelegateModelAttachedMetaObject( - QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject) - : metaType(metaType) - , metaObject(metaObject) - , memberPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount()) - , indexPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount() + metaType->groupNames.count()) -{ - // Don't reference count the meta-type here as that would create a circular reference. - // Instead we rely the fact that the meta-type's reference count can't reach 0 without first - // destroying all delegates with attached objects. - *static_cast<QMetaObject *>(this) = *metaObject; -} - -QQmlDelegateModelAttachedMetaObject::~QQmlDelegateModelAttachedMetaObject() -{ - ::free(metaObject); -} - -void QQmlDelegateModelAttachedMetaObject::objectDestroyed(QObject *) -{ - release(); -} - -int QQmlDelegateModelAttachedMetaObject::metaCall(QObject *object, QMetaObject::Call call, int _id, void **arguments) -{ - QQmlDelegateModelAttached *attached = static_cast<QQmlDelegateModelAttached *>(object); - if (call == QMetaObject::ReadProperty) { - if (_id >= indexPropertyOffset) { - Compositor::Group group = Compositor::Group(_id - indexPropertyOffset + 1); - *static_cast<int *>(arguments[0]) = attached->m_currentIndex[group]; - return -1; - } else if (_id >= memberPropertyOffset) { - Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); - *static_cast<bool *>(arguments[0]) = attached->m_cacheItem->groups & (1 << group); - return -1; - } - } else if (call == QMetaObject::WriteProperty) { - if (_id >= memberPropertyOffset) { - if (!metaType->model) - return -1; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); - Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); - const int groupFlag = 1 << group; - const bool member = attached->m_cacheItem->groups & groupFlag; - if (member && !*static_cast<bool *>(arguments[0])) { - Compositor::iterator it = model->m_compositor.find( - group, attached->m_currentIndex[group]); - model->removeGroups(it, 1, group, groupFlag); - } else if (!member && *static_cast<bool *>(arguments[0])) { - for (int i = 1; i < metaType->groupCount; ++i) { - if (attached->m_cacheItem->groups & (1 << i)) { - Compositor::iterator it = model->m_compositor.find( - Compositor::Group(i), attached->m_currentIndex[i]); - model->addGroups(it, 1, Compositor::Group(i), groupFlag); - break; - } - } - } - return -1; - } - } - return attached->qt_metacall(call, _id, arguments); -} - -QQmlDelegateModelAttached::QQmlDelegateModelAttached(QObject *parent) - : m_cacheItem(nullptr) - , m_previousGroups(0) -{ - QQml_setParent_noEvent(this, parent); -} - -QQmlDelegateModelAttached::QQmlDelegateModelAttached( - QQmlDelegateModelItem *cacheItem, QObject *parent) - : m_cacheItem(cacheItem) - , m_previousGroups(cacheItem->groups) -{ - QQml_setParent_noEvent(this, parent); - resetCurrentIndex(); - // Let m_previousIndex be equal to m_currentIndex - std::copy(std::begin(m_currentIndex), std::end(m_currentIndex), std::begin(m_previousIndex)); - - if (!cacheItem->metaType->metaObject) - cacheItem->metaType->initializeMetaObject(); - - QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject; - cacheItem->metaType->metaObject->addref(); -} - -void QQmlDelegateModelAttached::resetCurrentIndex() -{ - if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) { - for (int i = 1; i < qMin<int>(m_cacheItem->metaType->groupCount, Compositor::MaximumGroupCount); ++i) - m_currentIndex[i] = incubationTask->index[i]; - } else { - QQmlDelegateModelPrivate * const model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); - Compositor::iterator it = model->m_compositor.find( - Compositor::Cache, model->m_cache.indexOf(m_cacheItem)); - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) - m_currentIndex[i] = it.index[i]; - } -} - -/*! - \qmlattachedproperty model QtQml.Models::DelegateModel::model - - This attached property holds the data model this delegate instance belongs to. - - It is attached to each instance of the delegate. -*/ - -QQmlDelegateModel *QQmlDelegateModelAttached::model() const -{ - return m_cacheItem ? m_cacheItem->metaType->model : nullptr; -} - -/*! - \qmlattachedproperty stringlist QtQml.Models::DelegateModel::groups - - This attached property holds the name of DelegateModelGroups the item belongs to. - - It is attached to each instance of the delegate. -*/ - -QStringList QQmlDelegateModelAttached::groups() const -{ - QStringList groups; - - if (!m_cacheItem) - return groups; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { - if (m_cacheItem->groups & (1 << i)) - groups.append(m_cacheItem->metaType->groupNames.at(i - 1)); - } - return groups; -} - -void QQmlDelegateModelAttached::setGroups(const QStringList &groups) -{ - if (!m_cacheItem) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); - - const int groupFlags = model->m_cacheMetaType->parseGroups(groups); - const int cacheIndex = model->m_cache.indexOf(m_cacheItem); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - model->setGroups(it, 1, Compositor::Cache, groupFlags); -} - -/*! - \qmlattachedproperty bool QtQml.Models::DelegateModel::isUnresolved - - This attached property indicates whether the visual item is bound to a data model index. - Returns true if the item is not bound to the model, and false if it is. - - An unresolved item can be bound to the data model using the DelegateModelGroup::resolve() - function. - - It is attached to each instance of the delegate. -*/ - -bool QQmlDelegateModelAttached::isUnresolved() const -{ - if (!m_cacheItem) - return false; - - return m_cacheItem->groups & Compositor::UnresolvedFlag; -} - -/*! - \qmlattachedproperty int QtQml.Models::DelegateModel::inItems - - This attached property holds whether the item belongs to the default \l items DelegateModelGroup. - - Changing this property will add or remove the item from the items group. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQml.Models::DelegateModel::itemsIndex - - This attached property holds the index of the item in the default \l items DelegateModelGroup. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQml.Models::DelegateModel::inPersistedItems - - This attached property holds whether the item belongs to the \l persistedItems DelegateModelGroup. - - Changing this property will add or remove the item from the items group. Change with caution - as removing an item from the persistedItems group will destroy the current instance if it is - not referenced by a model. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQml.Models::DelegateModel::persistedItemsIndex - - This attached property holds the index of the item in the \l persistedItems DelegateModelGroup. - - It is attached to each instance of the delegate. -*/ - -void QQmlDelegateModelAttached::emitChanges() -{ - const int groupChanges = m_previousGroups ^ m_cacheItem->groups; - m_previousGroups = m_cacheItem->groups; - - int indexChanges = 0; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { - if (m_previousIndex[i] != m_currentIndex[i]) { - m_previousIndex[i] = m_currentIndex[i]; - indexChanges |= (1 << i); - } - } - - int notifierId = 0; - const QMetaObject *meta = metaObject(); - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { - if (groupChanges & (1 << i)) - QMetaObject::activate(this, meta, notifierId, nullptr); - } - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { - if (indexChanges & (1 << i)) - QMetaObject::activate(this, meta, notifierId, nullptr); - } - - if (groupChanges) - emit groupsChanged(); -} - -//============================================================================ - -void QQmlDelegateModelGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::Group g) -{ - Q_ASSERT(!model); - model = m; - group = g; -} - -bool QQmlDelegateModelGroupPrivate::isChangedConnected() -{ - Q_Q(QQmlDelegateModelGroup); - IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QQmlV4Handle &,const QQmlV4Handle &)); -} - -void QQmlDelegateModelGroupPrivate::emitChanges(QV4::ExecutionEngine *v4) -{ - Q_Q(QQmlDelegateModelGroup); - if (isChangedConnected() && !changeSet.isEmpty()) { - QV4::Scope scope(v4); - QV4::ScopedValue removed(scope, engineData(scope.engine)->array(v4, changeSet.removes())); - QV4::ScopedValue inserted(scope, engineData(scope.engine)->array(v4, changeSet.inserts())); - emit q->changed(QQmlV4Handle(removed), QQmlV4Handle(inserted)); - } - if (changeSet.difference() != 0) - emit q->countChanged(); -} - -void QQmlDelegateModelGroupPrivate::emitModelUpdated(bool reset) -{ - for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->emitModelUpdated(changeSet, reset); - changeSet.clear(); -} - -typedef QQmlDelegateModelGroupEmitterList::iterator GroupEmitterListIt; - -void QQmlDelegateModelGroupPrivate::createdPackage(int index, QQuickPackage *package) -{ - for (GroupEmitterListIt it = emitters.begin(), end = emitters.end(); it != end; ++it) - it->createdPackage(index, package); -} - -void QQmlDelegateModelGroupPrivate::initPackage(int index, QQuickPackage *package) -{ - for (GroupEmitterListIt it = emitters.begin(), end = emitters.end(); it != end; ++it) - it->initPackage(index, package); -} - -void QQmlDelegateModelGroupPrivate::destroyingPackage(QQuickPackage *package) -{ - for (GroupEmitterListIt it = emitters.begin(), end = emitters.end(); it != end; ++it) - it->destroyingPackage(package); -} - -/*! - \qmltype DelegateModelGroup - \instantiates QQmlDelegateModelGroup - \inqmlmodule QtQml.Models - \ingroup qtquick-models - \brief Encapsulates a filtered set of visual data items. - - The DelegateModelGroup type provides a means to address the model data of a - DelegateModel's delegate items, as well as sort and filter these delegate - items. - - The initial set of instantiable delegate items in a DelegateModel is represented - by its \l {QtQml.Models::DelegateModel::items}{items} group, which normally directly reflects - the contents of the model assigned to DelegateModel::model. This set can be changed to - the contents of any other member of DelegateModel::groups by assigning the \l name of that - DelegateModelGroup to the DelegateModel::filterOnGroup property. - - The data of an item in a DelegateModelGroup can be accessed using the get() function, which returns - information about group membership and indexes as well as model data. In combination - with the move() function this can be used to implement view sorting, with remove() to filter - items out of a view, or with setGroups() and \l Package delegates to categorize items into - different views. Different groups can only be sorted independently if they are disjunct. Moving - an item in one group will also move it in all other groups it is a part of. - - Data from models can be supplemented by inserting data directly into a DelegateModelGroup - with the insert() function. This can be used to introduce mock items into a view, or - placeholder items that are later \l {resolve()}{resolved} to real model data when it becomes - available. - - Delegate items can also be instantiated directly from a DelegateModelGroup using the - create() function, making it possible to use DelegateModel without an accompanying view - type or to cherry-pick specific items that should be instantiated irregardless of whether - they're currently within a view's visible area. - - \sa {QML Dynamic View Ordering Tutorial} -*/ -QQmlDelegateModelGroup::QQmlDelegateModelGroup(QObject *parent) - : QObject(*new QQmlDelegateModelGroupPrivate, parent) -{ -} - -QQmlDelegateModelGroup::QQmlDelegateModelGroup( - const QString &name, QQmlDelegateModel *model, int index, QObject *parent) - : QQmlDelegateModelGroup(parent) -{ - Q_D(QQmlDelegateModelGroup); - d->name = name; - d->setModel(model, Compositor::Group(index)); -} - -QQmlDelegateModelGroup::~QQmlDelegateModelGroup() -{ -} - -/*! - \qmlproperty string QtQml.Models::DelegateModelGroup::name - - This property holds the name of the group. - - Each group in a model must have a unique name starting with a lower case letter. -*/ - -QString QQmlDelegateModelGroup::name() const -{ - Q_D(const QQmlDelegateModelGroup); - return d->name; -} - -void QQmlDelegateModelGroup::setName(const QString &name) -{ - Q_D(QQmlDelegateModelGroup); - if (d->model) - return; - if (d->name != name) { - d->name = name; - emit nameChanged(); - } -} - -/*! - \qmlproperty int QtQml.Models::DelegateModelGroup::count - - This property holds the number of items in the group. -*/ - -int QQmlDelegateModelGroup::count() const -{ - Q_D(const QQmlDelegateModelGroup); - if (!d->model) - return 0; - return QQmlDelegateModelPrivate::get(d->model)->m_compositor.count(d->group); -} - -/*! - \qmlproperty bool QtQml.Models::DelegateModelGroup::includeByDefault - - This property holds whether new items are assigned to this group by default. -*/ - -bool QQmlDelegateModelGroup::defaultInclude() const -{ - Q_D(const QQmlDelegateModelGroup); - return d->defaultInclude; -} - -void QQmlDelegateModelGroup::setDefaultInclude(bool include) -{ - Q_D(QQmlDelegateModelGroup); - if (d->defaultInclude != include) { - d->defaultInclude = include; - - if (d->model) { - if (include) - QQmlDelegateModelPrivate::get(d->model)->m_compositor.setDefaultGroup(d->group); - else - QQmlDelegateModelPrivate::get(d->model)->m_compositor.clearDefaultGroup(d->group); - } - emit defaultIncludeChanged(); - } -} - -/*! - \qmlmethod object QtQml.Models::DelegateModelGroup::get(int index) - - Returns a javascript object describing the item at \a index in the group. - - The returned object contains the same information that is available to a delegate from the - DelegateModel attached as well as the model for that item. It has the properties: - - \list - \li \b model The model data of the item. This is the same as the model context property in - a delegate - \li \b groups A list the of names of groups the item is a member of. This property can be - written to change the item's membership. - \li \b inItems Whether the item belongs to the \l {QtQml.Models::DelegateModel::items}{items} group. - Writing to this property will add or remove the item from the group. - \li \b itemsIndex The index of the item within the \l {QtQml.Models::DelegateModel::items}{items} group. - \li \b {in<GroupName>} Whether the item belongs to the dynamic group \e groupName. Writing to - this property will add or remove the item from the group. - \li \b {<groupName>Index} The index of the item within the dynamic group \e groupName. - \li \b isUnresolved Whether the item is bound to an index in the model assigned to - DelegateModel::model. Returns true if the item is not bound to the model, and false if it is. - \endlist -*/ - -QQmlV4Handle QQmlDelegateModelGroup::get(int index) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return QQmlV4Handle(QV4::Encode::undefined()); - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (!model->m_context || !model->m_context->isValid()) { - return QQmlV4Handle(QV4::Encode::undefined()); - } else if (index < 0 || index >= model->m_compositor.count(d->group)) { - qmlWarning(this) << tr("get: index out of range"); - return QQmlV4Handle(QV4::Encode::undefined()); - } - - Compositor::iterator it = model->m_compositor.find(d->group, index); - QQmlDelegateModelItem *cacheItem = it->inCache() - ? model->m_cache.at(it.cacheIndex) - : 0; - - if (!cacheItem) { - cacheItem = model->m_adaptorModel.createItem( - model->m_cacheMetaType, it.modelIndex()); - if (!cacheItem) - return QQmlV4Handle(QV4::Encode::undefined()); - cacheItem->groups = it->flags; - - model->m_cache.insert(it.cacheIndex, cacheItem); - model->m_compositor.setFlags(it, 1, Compositor::CacheFlag); - } - - if (model->m_cacheMetaType->modelItemProto.isUndefined()) - model->m_cacheMetaType->initializePrototype(); - QV4::ExecutionEngine *v4 = model->m_cacheMetaType->v4Engine; - QV4::Scope scope(v4); - ++cacheItem->scriptRef; - QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(cacheItem)); - QV4::ScopedObject p(scope, model->m_cacheMetaType->modelItemProto.value()); - o->setPrototypeOf(p); - - return QQmlV4Handle(o); -} - -bool QQmlDelegateModelGroupPrivate::parseIndex(const QV4::Value &value, int *index, Compositor::Group *group) const -{ - if (value.isNumber()) { - *index = value.toInt32(); - return true; - } - - if (!value.isObject()) - return false; - - QV4::ExecutionEngine *v4 = value.as<QV4::Object>()->engine(); - QV4::Scope scope(v4); - QV4::Scoped<QQmlDelegateModelItemObject> object(scope, value); - - if (object) { - QQmlDelegateModelItem * const cacheItem = object->d()->item; - if (QQmlDelegateModelPrivate *model = cacheItem->metaType->model - ? QQmlDelegateModelPrivate::get(cacheItem->metaType->model) - : nullptr) { - *index = model->m_cache.indexOf(cacheItem); - *group = Compositor::Cache; - return true; - } - } - return false; -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::insert(int index, jsdict data, array groups = undefined) - \qmlmethod QtQml.Models::DelegateModelGroup::insert(jsdict data, var groups = undefined) - - Creates a new entry at \a index in a DelegateModel with the values from \a data that - correspond to roles in the model assigned to DelegateModel::model. - - If no index is supplied the data is appended to the model. - - The optional \a groups parameter identifies the groups the new entry should belong to, - if unspecified this is equal to the group insert was called on. - - Data inserted into a DelegateModel can later be merged with an existing entry in - DelegateModel::model using the \l resolve() function. This can be used to create placeholder - items that are later replaced by actual data. -*/ - -void QQmlDelegateModelGroup::insert(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - int index = model->m_compositor.count(d->group); - Compositor::Group group = d->group; - - if (args->length() == 0) - return; - - int i = 0; - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[i]); - if (d->parseIndex(v, &index, &group)) { - if (index < 0 || index > model->m_compositor.count(group)) { - qmlWarning(this) << tr("insert: index out of range"); - return; - } - if (++i == args->length()) - return; - v = (*args)[i]; - } - - Compositor::insert_iterator before = index < model->m_compositor.count(group) - ? model->m_compositor.findInsertPosition(group, index) - : model->m_compositor.end(); - - int groups = 1 << d->group; - if (++i < args->length()) { - QV4::ScopedValue val(scope, (*args)[i]); - groups |= model->m_cacheMetaType->parseGroups(val); - } - - if (v->as<QV4::ArrayObject>()) { - return; - } else if (v->as<QV4::Object>()) { - model->insert(before, v, groups); - model->emitChanges(); - } -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::create(int index) - \qmlmethod QtQml.Models::DelegateModelGroup::create(int index, jsdict data, array groups = undefined) - \qmlmethod QtQml.Models::DelegateModelGroup::create(jsdict data, array groups = undefined) - - Returns a reference to the instantiated item at \a index in the group. - - If a \a data object is provided it will be \l {insert}{inserted} at \a index and an item - referencing this new entry will be returned. The optional \a groups parameter identifies - the groups the new entry should belong to, if unspecified this is equal to the group create() - was called on. - - All items returned by create are added to the - \l {QtQml.Models::DelegateModel::persistedItems}{persistedItems} group. Items in this - group remain instantiated when not referenced by any view. -*/ - -void QQmlDelegateModelGroup::create(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return; - - if (args->length() == 0) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - int index = model->m_compositor.count(d->group); - Compositor::Group group = d->group; - - int i = 0; - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[i]); - if (d->parseIndex(v, &index, &group)) - ++i; - - if (i < args->length() && index >= 0 && index <= model->m_compositor.count(group)) { - v = (*args)[i]; - if (v->as<QV4::Object>()) { - int groups = 1 << d->group; - if (++i < args->length()) { - QV4::ScopedValue val(scope, (*args)[i]); - groups |= model->m_cacheMetaType->parseGroups(val); - } - - Compositor::insert_iterator before = index < model->m_compositor.count(group) - ? model->m_compositor.findInsertPosition(group, index) - : model->m_compositor.end(); - - index = before.index[d->group]; - group = d->group; - - if (!model->insert(before, v, groups)) { - return; - } - } - } - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlWarning(this) << tr("create: index out of range"); - return; - } - - QObject *object = model->object(group, index, QQmlIncubator::AsynchronousIfNested); - if (object) { - QVector<Compositor::Insert> inserts; - Compositor::iterator it = model->m_compositor.find(group, index); - model->m_compositor.setFlags(it, 1, d->group, Compositor::PersistedFlag, &inserts); - model->itemsInserted(inserts); - model->m_cache.at(it.cacheIndex)->releaseObject(); - } - - args->setReturnValue(QV4::QObjectWrapper::wrap(args->v4engine(), object)); - model->emitChanges(); -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::resolve(int from, int to) - - Binds an unresolved item at \a from to an item in DelegateModel::model at index \a to. - - Unresolved items are entries whose data has been \l {insert()}{inserted} into a DelegateModelGroup - instead of being derived from a DelegateModel::model index. Resolving an item will replace - the item at the target index with the unresolved item. A resolved an item will reflect the data - of the source model at its bound index and will move when that index moves like any other item. - - If a new item is replaced in the DelegateModelGroup onChanged() handler its insertion and - replacement will be communicated to views as an atomic operation, creating the appearance - that the model contents have not changed, or if the unresolved and model item are not adjacent - that the previously unresolved item has simply moved. - -*/ -void QQmlDelegateModelGroup::resolve(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - if (args->length() < 2) - return; - - int from = -1; - int to = -1; - Compositor::Group fromGroup = d->group; - Compositor::Group toGroup = d->group; - - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[0]); - if (d->parseIndex(v, &from, &fromGroup)) { - if (from < 0 || from >= model->m_compositor.count(fromGroup)) { - qmlWarning(this) << tr("resolve: from index out of range"); - return; - } - } else { - qmlWarning(this) << tr("resolve: from index invalid"); - return; - } - - v = (*args)[1]; - if (d->parseIndex(v, &to, &toGroup)) { - if (to < 0 || to >= model->m_compositor.count(toGroup)) { - qmlWarning(this) << tr("resolve: to index out of range"); - return; - } - } else { - qmlWarning(this) << tr("resolve: to index invalid"); - return; - } - - Compositor::iterator fromIt = model->m_compositor.find(fromGroup, from); - Compositor::iterator toIt = model->m_compositor.find(toGroup, to); - - if (!fromIt->isUnresolved()) { - qmlWarning(this) << tr("resolve: from is not an unresolved item"); - return; - } - if (!toIt->list) { - qmlWarning(this) << tr("resolve: to is not a model item"); - return; - } - - const int unresolvedFlags = fromIt->flags; - const int resolvedFlags = toIt->flags; - const int resolvedIndex = toIt.modelIndex(); - void * const resolvedList = toIt->list; - - QQmlDelegateModelItem *cacheItem = model->m_cache.at(fromIt.cacheIndex); - cacheItem->groups &= ~Compositor::UnresolvedFlag; - - if (toIt.cacheIndex > fromIt.cacheIndex) - toIt.decrementIndexes(1, unresolvedFlags); - if (!toIt->inGroup(fromGroup) || toIt.index[fromGroup] > from) - from += 1; - - model->itemsMoved( - QVector<Compositor::Remove>(1, Compositor::Remove(fromIt, 1, unresolvedFlags, 0)), - QVector<Compositor::Insert>(1, Compositor::Insert(toIt, 1, unresolvedFlags, 0))); - model->itemsInserted( - QVector<Compositor::Insert>(1, Compositor::Insert(toIt, 1, (resolvedFlags & ~unresolvedFlags) | Compositor::CacheFlag))); - toIt.incrementIndexes(1, resolvedFlags | unresolvedFlags); - model->itemsRemoved(QVector<Compositor::Remove>(1, Compositor::Remove(toIt, 1, resolvedFlags))); - - model->m_compositor.setFlags(toGroup, to, 1, unresolvedFlags & ~Compositor::UnresolvedFlag); - model->m_compositor.clearFlags(fromGroup, from, 1, unresolvedFlags); - - if (resolvedFlags & Compositor::CacheFlag) - model->m_compositor.insert(Compositor::Cache, toIt.cacheIndex, resolvedList, resolvedIndex, 1, Compositor::CacheFlag); - - Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); - - if (!cacheItem->isReferenced()) { - Q_ASSERT(toIt.cacheIndex == model->m_cache.indexOf(cacheItem)); - model->m_cache.removeAt(toIt.cacheIndex); - model->m_compositor.clearFlags(Compositor::Cache, toIt.cacheIndex, 1, Compositor::CacheFlag); - delete cacheItem; - Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); - } else { - cacheItem->resolveIndex(model->m_adaptorModel, resolvedIndex); - if (cacheItem->attached) - cacheItem->attached->emitUnresolvedChanged(); - } - - model->emitChanges(); -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::remove(int index, int count) - - Removes \a count items starting at \a index from the group. -*/ - -void QQmlDelegateModelGroup::remove(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return; - Compositor::Group group = d->group; - int index = -1; - int count = 1; - - if (args->length() == 0) - return; - - int i = 0; - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[0]); - if (!d->parseIndex(v, &index, &group)) { - qmlWarning(this) << tr("remove: invalid index"); - return; - } - - if (++i < args->length()) { - v = (*args)[i]; - if (v->isNumber()) - count = v->toInt32(); - } - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlWarning(this) << tr("remove: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlWarning(this) << tr("remove: invalid count"); - } else { - model->removeGroups(it, count, d->group, 1 << d->group); - } - } -} - -bool QQmlDelegateModelGroupPrivate::parseGroupArgs( - QQmlV4Function *args, Compositor::Group *group, int *index, int *count, int *groups) const -{ - if (!model || !QQmlDelegateModelPrivate::get(model)->m_cacheMetaType) - return false; - - if (args->length() < 2) - return false; - - int i = 0; - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[i]); - if (!parseIndex(v, index, group)) - return false; - - v = (*args)[++i]; - if (v->isNumber()) { - *count = v->toInt32(); - - if (++i == args->length()) - return false; - v = (*args)[i]; - } - - *groups = QQmlDelegateModelPrivate::get(model)->m_cacheMetaType->parseGroups(v); - - return true; -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::addGroups(int index, int count, stringlist groups) - - Adds \a count items starting at \a index to \a groups. -*/ - -void QQmlDelegateModelGroup::addGroups(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlWarning(this) << tr("addGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlWarning(this) << tr("addGroups: invalid count"); - } else { - model->addGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::removeGroups(int index, int count, stringlist groups) - - Removes \a count items starting at \a index from \a groups. -*/ - -void QQmlDelegateModelGroup::removeGroups(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlWarning(this) << tr("removeGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlWarning(this) << tr("removeGroups: invalid count"); - } else { - model->removeGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::setGroups(int index, int count, stringlist groups) - - Sets the \a groups \a count items starting at \a index belong to. -*/ - -void QQmlDelegateModelGroup::setGroups(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlWarning(this) << tr("setGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlWarning(this) << tr("setGroups: invalid count"); - } else { - model->setGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::setGroups(int index, int count, stringlist groups) - - Sets the \a groups \a count items starting at \a index belong to. -*/ - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::move(var from, var to, int count) - - Moves \a count at \a from in a group \a to a new position. - - \note The DelegateModel acts as a proxy model: it holds the delegates in a - different order than the \l{dm-model-property}{underlying model} has them. - Any subsequent changes to the underlying model will not undo whatever - reordering you have done via this function. -*/ - -void QQmlDelegateModelGroup::move(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - - if (args->length() < 2) - return; - - Compositor::Group fromGroup = d->group; - Compositor::Group toGroup = d->group; - int from = -1; - int to = -1; - int count = 1; - - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[0]); - if (!d->parseIndex(v, &from, &fromGroup)) { - qmlWarning(this) << tr("move: invalid from index"); - return; - } - - v = (*args)[1]; - if (!d->parseIndex(v, &to, &toGroup)) { - qmlWarning(this) << tr("move: invalid to index"); - return; - } - - if (args->length() > 2) { - v = (*args)[2]; - if (v->isNumber()) - count = v->toInt32(); - } - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - if (count < 0) { - qmlWarning(this) << tr("move: invalid count"); - } else if (from < 0 || from + count > model->m_compositor.count(fromGroup)) { - qmlWarning(this) << tr("move: from index out of range"); - } else if (!model->m_compositor.verifyMoveTo(fromGroup, from, toGroup, to, count, d->group)) { - qmlWarning(this) << tr("move: to index out of range"); - } else if (count > 0) { - QVector<Compositor::Remove> removes; - QVector<Compositor::Insert> inserts; - - model->m_compositor.move(fromGroup, from, toGroup, to, count, d->group, &removes, &inserts); - model->itemsMoved(removes, inserts); - model->emitChanges(); - } - -} - -/*! - \qmlsignal QtQml.Models::DelegateModelGroup::changed(array removed, array inserted) - - This signal is emitted when items have been removed from or inserted into the group. - - Each object in the \a removed and \a inserted arrays has two values; the \e index of the first - item inserted or removed and a \e count of the number of consecutive items inserted or removed. - - Each index is adjusted for previous changes with all removed items preceding any inserted - items. - - The corresponding handler is \c onChanged. -*/ - -//============================================================================ - -QQmlPartsModel::QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent) - : QQmlInstanceModel(*new QObjectPrivate, parent) - , m_model(model) - , m_part(part) - , m_compositorGroup(Compositor::Cache) - , m_inheritGroup(true) -{ - QQmlDelegateModelPrivate *d = QQmlDelegateModelPrivate::get(m_model); - if (d->m_cacheMetaType) { - QQmlDelegateModelGroupPrivate::get(d->m_groups[1])->emitters.insert(this); - m_compositorGroup = Compositor::Default; - } else { - d->m_pendingParts.insert(this); - } -} - -QQmlPartsModel::~QQmlPartsModel() -{ -} - -QString QQmlPartsModel::filterGroup() const -{ - if (m_inheritGroup) - return m_model->filterGroup(); - return m_filterGroup; -} - -void QQmlPartsModel::setFilterGroup(const QString &group) -{ - if (QQmlDelegateModelPrivate::get(m_model)->m_transaction) { - qmlWarning(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); - return; - } - - if (m_filterGroup != group || m_inheritGroup) { - m_filterGroup = group; - m_inheritGroup = false; - updateFilterGroup(); - - emit filterGroupChanged(); - } -} - -void QQmlPartsModel::resetFilterGroup() -{ - if (!m_inheritGroup) { - m_inheritGroup = true; - updateFilterGroup(); - emit filterGroupChanged(); - } -} - -void QQmlPartsModel::updateFilterGroup() -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - if (!model->m_cacheMetaType) - return; - - if (m_inheritGroup) { - if (m_filterGroup == model->m_filterGroup) - return; - m_filterGroup = model->m_filterGroup; - } - - QQmlListCompositor::Group previousGroup = m_compositorGroup; - m_compositorGroup = Compositor::Default; - QQmlDelegateModelGroupPrivate::get(model->m_groups[Compositor::Default])->emitters.insert(this); - for (int i = 1; i < model->m_groupCount; ++i) { - if (m_filterGroup == model->m_cacheMetaType->groupNames.at(i - 1)) { - m_compositorGroup = Compositor::Group(i); - break; - } - } - - QQmlDelegateModelGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this); - if (m_compositorGroup != previousGroup) { - QVector<QQmlChangeSet::Change> removes; - QVector<QQmlChangeSet::Change> inserts; - model->m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); - - QQmlChangeSet changeSet; - changeSet.move(removes, inserts); - if (!changeSet.isEmpty()) - emit modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit countChanged(); - } -} - -void QQmlPartsModel::updateFilterGroup( - Compositor::Group group, const QQmlChangeSet &changeSet) -{ - if (!m_inheritGroup) - return; - - m_compositorGroup = group; - QQmlDelegateModelGroupPrivate::get(QQmlDelegateModelPrivate::get(m_model)->m_groups[m_compositorGroup])->emitters.insert(this); - - if (!changeSet.isEmpty()) - emit modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit countChanged(); - - emit filterGroupChanged(); -} - -int QQmlPartsModel::count() const -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - return model->m_delegate - ? model->m_compositor.count(m_compositorGroup) - : 0; -} - -bool QQmlPartsModel::isValid() const -{ - return m_model->isValid(); -} - -QObject *QQmlPartsModel::object(int index, QQmlIncubator::IncubationMode incubationMode) -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - - if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) { - qWarning() << "DelegateModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup); - return nullptr; - } - - QObject *object = model->object(m_compositorGroup, index, incubationMode); - - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) { - QObject *part = package->part(m_part); - if (!part) - return nullptr; - m_packaged.insertMulti(part, package); - return part; - } - - model->release(object); - if (!model->m_delegateValidated) { - if (object) - qmlWarning(model->m_delegate) << tr("Delegate component must be Package type."); - model->m_delegateValidated = true; - } - - return nullptr; -} - -QQmlInstanceModel::ReleaseFlags QQmlPartsModel::release(QObject *item) -{ - QQmlInstanceModel::ReleaseFlags flags = nullptr; - - QHash<QObject *, QQuickPackage *>::iterator it = m_packaged.find(item); - if (it != m_packaged.end()) { - QQuickPackage *package = *it; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - flags = model->release(package); - m_packaged.erase(it); - if (!m_packaged.contains(item)) - flags &= ~Referenced; - if (flags & Destroyed) - QQmlDelegateModelPrivate::get(m_model)->emitDestroyingPackage(package); - } - return flags; -} - -QString QQmlPartsModel::stringValue(int index, const QString &role) -{ - return QQmlDelegateModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role); -} - -void QQmlPartsModel::setWatchedRoles(const QList<QByteArray> &roles) -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - model->m_adaptorModel.replaceWatchedRoles(m_watchedRoles, roles); - m_watchedRoles = roles; -} - -QQmlIncubator::Status QQmlPartsModel::incubationStatus(int index) -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - Compositor::iterator it = model->m_compositor.find(model->m_compositorGroup, index); - if (!it->inCache()) - return QQmlIncubator::Null; - - if (auto incubationTask = model->m_cache.at(it.cacheIndex)->incubationTask) - return incubationTask->status(); - - return QQmlIncubator::Ready; -} - -int QQmlPartsModel::indexOf(QObject *item, QObject *) const -{ - QHash<QObject *, QQuickPackage *>::const_iterator it = m_packaged.find(item); - if (it != m_packaged.end()) { - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(*it)) - return cacheItem->groupIndex(m_compositorGroup); - } - return -1; -} - -void QQmlPartsModel::createdPackage(int index, QQuickPackage *package) -{ - emit createdItem(index, package->part(m_part)); -} - -void QQmlPartsModel::initPackage(int index, QQuickPackage *package) -{ - if (m_modelUpdatePending) - m_pendingPackageInitializations << index; - else - emit initItem(index, package->part(m_part)); -} - -void QQmlPartsModel::destroyingPackage(QQuickPackage *package) -{ - QObject *item = package->part(m_part); - Q_ASSERT(!m_packaged.contains(item)); - emit destroyingItem(item); -} - -void QQmlPartsModel::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) -{ - m_modelUpdatePending = false; - emit modelUpdated(changeSet, reset); - if (changeSet.difference() != 0) - emit countChanged(); - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - QVector<int> pendingPackageInitializations; - qSwap(pendingPackageInitializations, m_pendingPackageInitializations); - for (int index : pendingPackageInitializations) { - if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) - continue; - QObject *object = model->object(m_compositorGroup, index, QQmlIncubator::Asynchronous); - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) - emit initItem(index, package->part(m_part)); - model->release(object); - } -} - -//============================================================================ - -struct QQmlDelegateModelGroupChange : QV4::Object -{ - V4_OBJECT2(QQmlDelegateModelGroupChange, QV4::Object) - - static QV4::Heap::QQmlDelegateModelGroupChange *create(QV4::ExecutionEngine *e) { - return e->memoryManager->allocate<QQmlDelegateModelGroupChange>(); - } - - static QV4::ReturnedValue method_get_index(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) { - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, thisObject->as<QQmlDelegateModelGroupChange>()); - if (!that) - THROW_TYPE_ERROR(); - return QV4::Encode(that->d()->change.index); - } - static QV4::ReturnedValue method_get_count(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) { - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, thisObject->as<QQmlDelegateModelGroupChange>()); - if (!that) - THROW_TYPE_ERROR(); - return QV4::Encode(that->d()->change.count); - } - static QV4::ReturnedValue method_get_moveId(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) { - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, thisObject->as<QQmlDelegateModelGroupChange>()); - if (!that) - THROW_TYPE_ERROR(); - if (that->d()->change.moveId < 0) - RETURN_UNDEFINED(); - return QV4::Encode(that->d()->change.moveId); - } -}; - -DEFINE_OBJECT_VTABLE(QQmlDelegateModelGroupChange); - -struct QQmlDelegateModelGroupChangeArray : public QV4::Object -{ - V4_OBJECT2(QQmlDelegateModelGroupChangeArray, QV4::Object) - V4_NEEDS_DESTROY -public: - static QV4::Heap::QQmlDelegateModelGroupChangeArray *create(QV4::ExecutionEngine *engine, const QVector<QQmlChangeSet::Change> &changes) - { - return engine->memoryManager->allocate<QQmlDelegateModelGroupChangeArray>(changes); - } - - quint32 count() const { return d()->changes->count(); } - const QQmlChangeSet::Change &at(int index) const { return d()->changes->at(index); } - - static QV4::ReturnedValue virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty) - { - if (id.isArrayIndex()) { - uint index = id.asArrayIndex(); - Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); - QV4::ExecutionEngine *v4 = static_cast<const QQmlDelegateModelGroupChangeArray *>(m)->engine(); - QV4::Scope scope(v4); - QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, static_cast<const QQmlDelegateModelGroupChangeArray *>(m)); - - if (index >= array->count()) { - if (hasProperty) - *hasProperty = false; - return QV4::Value::undefinedValue().asReturnedValue(); - } - - const QQmlChangeSet::Change &change = array->at(index); - - QV4::ScopedObject changeProto(scope, engineData(v4)->changeProto.value()); - QV4::Scoped<QQmlDelegateModelGroupChange> object(scope, QQmlDelegateModelGroupChange::create(v4)); - object->setPrototypeOf(changeProto); - object->d()->change = change; - - if (hasProperty) - *hasProperty = true; - return object.asReturnedValue(); - } - - Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); - const QQmlDelegateModelGroupChangeArray *array = static_cast<const QQmlDelegateModelGroupChangeArray *>(m); - - if (id == array->engine()->id_length()->propertyKey()) { - if (hasProperty) - *hasProperty = true; - return QV4::Encode(array->count()); - } - - return Object::virtualGet(m, id, receiver, hasProperty); - } -}; - -void QV4::Heap::QQmlDelegateModelGroupChangeArray::init(const QVector<QQmlChangeSet::Change> &changes) -{ - Object::init(); - this->changes = new QVector<QQmlChangeSet::Change>(changes); - QV4::Scope scope(internalClass->engine); - QV4::ScopedObject o(scope, this); - o->setArrayType(QV4::Heap::ArrayData::Custom); -} - -DEFINE_OBJECT_VTABLE(QQmlDelegateModelGroupChangeArray); - -QQmlDelegateModelEngineData::QQmlDelegateModelEngineData(QV4::ExecutionEngine *v4) -{ - QV4::Scope scope(v4); - - QV4::ScopedObject proto(scope, v4->newObject()); - proto->defineAccessorProperty(QStringLiteral("index"), QQmlDelegateModelGroupChange::method_get_index, nullptr); - proto->defineAccessorProperty(QStringLiteral("count"), QQmlDelegateModelGroupChange::method_get_count, nullptr); - proto->defineAccessorProperty(QStringLiteral("moveId"), QQmlDelegateModelGroupChange::method_get_moveId, nullptr); - changeProto.set(v4, proto); -} - -QQmlDelegateModelEngineData::~QQmlDelegateModelEngineData() -{ -} - -QV4::ReturnedValue QQmlDelegateModelEngineData::array(QV4::ExecutionEngine *v4, - const QVector<QQmlChangeSet::Change> &changes) -{ - QV4::Scope scope(v4); - QV4::ScopedObject o(scope, QQmlDelegateModelGroupChangeArray::create(v4, changes)); - return o.asReturnedValue(); -} - -QT_END_NAMESPACE - -#include "moc_qqmldelegatemodel_p.cpp" |