diff options
Diffstat (limited to 'src/qml/types')
26 files changed, 2 insertions, 12246 deletions
diff --git a/src/qml/types/qqmldelegatecomponent.cpp b/src/qml/types/qqmldelegatecomponent.cpp deleted file mode 100644 index 470f6cab6a..0000000000 --- a/src/qml/types/qqmldelegatecomponent.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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 "qqmldelegatecomponent_p.h" -#include <QtQml/private/qqmladaptormodel_p.h> - -QT_BEGIN_NAMESPACE - -QQmlAbstractDelegateComponent::QQmlAbstractDelegateComponent(QObject *parent) - : QQmlComponent(parent) -{ -} - -QQmlAbstractDelegateComponent::~QQmlAbstractDelegateComponent() -{ -} - -QVariant QQmlAbstractDelegateComponent::value(QQmlAdaptorModel *adaptorModel, int row, int column, const QString &role) const -{ - if (!adaptorModel) - return QVariant(); - return adaptorModel->value(adaptorModel->indexAt(row, column), role); -} - -/*! - \qmlmodule Qt.labs.qmlmodels 1.0 - \title Qt Labs QML Models - QML Types - \ingroup qmlmodules - \brief The Qt Labs QML Models module provides various model-related types for use with views. - - To use this module, import the module with the following line: - - \qml - import Qt.labs.qmlmodels 1.0 - \endqml -*/ - -/*! - \qmltype DelegateChoice - \instantiates QQmlDelegateChoice - \inqmlmodule Qt.labs.qmlmodels - \brief Encapsulates a delegate and when to use it. - - The DelegateChoice type wraps a delegate and defines the circumstances - in which it should be chosen. - - DelegateChoices can be nested inside a DelegateChooser. - - \sa DelegateChooser -*/ - -/*! - \qmlproperty string QtQml.Models::DelegateChoice::roleValue - This property holds the value used to match the role data for the role provided by \l DelegateChooser::role. -*/ -QVariant QQmlDelegateChoice::roleValue() const -{ - return m_value; -} - -void QQmlDelegateChoice::setRoleValue(const QVariant &value) -{ - if (m_value == value) - return; - m_value = value; - emit roleValueChanged(); - emit changed(); -} - -/*! - \qmlproperty index QtQml.Models::DelegateChoice::row - This property holds the value used to match the row value of model elements. - With models that have only the index property (and thus only one column), this property - should be intended as an index, and set to the desired index value. - - \note Setting both row and index has undefined behavior. The two are equivalent and only - one should be used. - - \sa index -*/ - -/*! - \qmlproperty index QtQml.Models::DelegateChoice::index - This property holds the value used to match the index value of model elements. - This is effectively an alias for \l row. - - \sa row -*/ -int QQmlDelegateChoice::row() const -{ - return m_row; -} - -void QQmlDelegateChoice::setRow(int r) -{ - if (m_row == r) - return; - m_row = r; - emit rowChanged(); - emit indexChanged(); - emit changed(); -} - -/*! - \qmlproperty index QtQml.Models::DelegateChoice::column - This property holds the value used to match the column value of model elements. -*/ -int QQmlDelegateChoice::column() const -{ - return m_column; -} - -void QQmlDelegateChoice::setColumn(int c) -{ - if (m_column == c) - return; - m_column = c; - emit columnChanged(); - emit changed(); -} - -QQmlComponent *QQmlDelegateChoice::delegate() const -{ - return m_delegate; -} - -/*! - \qmlproperty Component QtQml.Models::DelegateChoice::delegate - This property holds the delegate to use if this choice matches the model item. -*/ -void QQmlDelegateChoice::setDelegate(QQmlComponent *delegate) -{ - if (m_delegate == delegate) - return; - QQmlAbstractDelegateComponent *adc = static_cast<QQmlAbstractDelegateComponent *>(m_delegate); - if (adc) - disconnect(adc, &QQmlAbstractDelegateComponent::delegateChanged, this, &QQmlDelegateChoice::delegateChanged); - m_delegate = delegate; - adc = static_cast<QQmlAbstractDelegateComponent *>(delegate); - if (adc) - connect(adc, &QQmlAbstractDelegateComponent::delegateChanged, this, &QQmlDelegateChoice::delegateChanged); - emit delegateChanged(); - emit changed(); -} - -bool QQmlDelegateChoice::match(int row, int column, const QVariant &value) const -{ - if (!m_value.isValid() && m_row < 0 && m_column < 0) - return true; - - const bool roleMatched = (m_value.isValid()) ? value == m_value : true; - const bool rowMatched = (m_row < 0 ) ? true : m_row == row; - const bool columnMatched = (m_column < 0 ) ? true : m_column == column; - return roleMatched && rowMatched && columnMatched; -} - -/*! - \qmltype DelegateChooser - \instantiates QQmlDelegateChooser - \inqmlmodule Qt.labs.qmlmodels - \brief Allows a view to use different delegates for different types of items in the model. - - The DelegateChooser is a special \l Component type intended for those scenarios where a Component is required - by a view and used as a delegate. - DelegateChooser encapsulates a set of \l {DelegateChoice}s. - These choices are used determine the delegate that will be instantiated for each - item in the model. - The selection of the choice is performed based on the value that a model item has for \l role, - and also based on index. - - \note This type is intended to transparently work only with TableView and any DelegateModel-based view. - Views (including user-defined views) that aren't internally based on a DelegateModel need to explicitly support - this type of component to make it function as described. - - \sa DelegateChoice -*/ - -/*! - \qmlproperty string QtQml.Models::DelegateChooser::role - This property holds the role used to determine the delegate for a given model item. - - \sa DelegateChoice -*/ -void QQmlDelegateChooser::setRole(const QString &role) -{ - if (m_role == role) - return; - m_role = role; - emit roleChanged(); -} - -/*! - \qmlproperty list<DelegateChoice> QtQml.Models::DelegateChooser::choices - \default - - The list of DelegateChoices for the chooser. - - The list is treated as an ordered list, where the first DelegateChoice to match - will be used be a view. - - It should not generally be necessary to refer to the \c choices property, - as it is the default property for DelegateChooser and thus all child items are - automatically assigned to this property. -*/ - -QQmlListProperty<QQmlDelegateChoice> QQmlDelegateChooser::choices() -{ - return QQmlListProperty<QQmlDelegateChoice>(this, nullptr, - QQmlDelegateChooser::choices_append, - QQmlDelegateChooser::choices_count, - QQmlDelegateChooser::choices_at, - QQmlDelegateChooser::choices_clear); -} - -void QQmlDelegateChooser::choices_append(QQmlListProperty<QQmlDelegateChoice> *prop, QQmlDelegateChoice *choice) -{ - QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object); - q->m_choices.append(choice); - connect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged); - q->delegateChanged(); -} - -int QQmlDelegateChooser::choices_count(QQmlListProperty<QQmlDelegateChoice> *prop) -{ - QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser*>(prop->object); - return q->m_choices.count(); -} - -QQmlDelegateChoice *QQmlDelegateChooser::choices_at(QQmlListProperty<QQmlDelegateChoice> *prop, int index) -{ - QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser*>(prop->object); - return q->m_choices.at(index); -} - -void QQmlDelegateChooser::choices_clear(QQmlListProperty<QQmlDelegateChoice> *prop) -{ - QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object); - for (QQmlDelegateChoice *choice : q->m_choices) - disconnect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged); - q->m_choices.clear(); - q->delegateChanged(); -} - -QQmlComponent *QQmlDelegateChooser::delegate(QQmlAdaptorModel *adaptorModel, int row, int column) const -{ - QVariant v; - if (!m_role.isNull()) - v = value(adaptorModel, row, column, m_role); - if (!v.isValid()) { // check if the row only has modelData, for example if the row is a QVariantMap - v = value(adaptorModel, row, column, QStringLiteral("modelData")); - if (v.isValid()) - v = v.toMap().value(m_role); - } - // loop through choices, finding first one that fits - for (int i = 0; i < m_choices.count(); ++i) { - const QQmlDelegateChoice *choice = m_choices.at(i); - if (choice->match(row, column, v)) - return choice->delegate(); - } - - return nullptr; -} - -QT_END_NAMESPACE diff --git a/src/qml/types/qqmldelegatecomponent_p.h b/src/qml/types/qqmldelegatecomponent_p.h deleted file mode 100644 index c925ed9a60..0000000000 --- a/src/qml/types/qqmldelegatecomponent_p.h +++ /dev/null @@ -1,155 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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$ -** -****************************************************************************/ - -#ifndef QQMLDELEGATECOMPONENT_P_H -#define QQMLDELEGATECOMPONENT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtqmlglobal_p.h> -#include <qqmlcomponent.h> - -QT_REQUIRE_CONFIG(qml_delegate_model); - -QT_BEGIN_NAMESPACE - -// TODO: consider making QQmlAbstractDelegateComponent public API -class QQmlAbstractDelegateComponentPrivate; -class QQmlAdaptorModel; -class Q_QML_PRIVATE_EXPORT QQmlAbstractDelegateComponent : public QQmlComponent -{ - Q_OBJECT -public: - QQmlAbstractDelegateComponent(QObject *parent = nullptr); - ~QQmlAbstractDelegateComponent() override; - - virtual QQmlComponent *delegate(QQmlAdaptorModel *adaptorModel, int row, int column = 0) const = 0; - -signals: - void delegateChanged(); - -protected: - QVariant value(QQmlAdaptorModel *adaptorModel,int row, int column, const QString &role) const; - -private: - Q_DECLARE_PRIVATE(QQmlAbstractDelegateComponent) - Q_DISABLE_COPY(QQmlAbstractDelegateComponent) -}; - -class Q_QML_PRIVATE_EXPORT QQmlDelegateChoice : public QObject -{ - Q_OBJECT - Q_PROPERTY(QVariant roleValue READ roleValue WRITE setRoleValue NOTIFY roleValueChanged) - Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged) - Q_PROPERTY(int index READ row WRITE setRow NOTIFY indexChanged) - Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged) - Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) - Q_CLASSINFO("DefaultProperty", "delegate") -public: - QVariant roleValue() const; - void setRoleValue(const QVariant &roleValue); - - int row() const; - void setRow(int r); - - int column() const; - void setColumn(int c); - - QQmlComponent *delegate() const; - void setDelegate(QQmlComponent *delegate); - - virtual bool match(int row, int column, const QVariant &value) const; - -signals: - void roleValueChanged(); - void rowChanged(); - void indexChanged(); - void columnChanged(); - void delegateChanged(); - void changed(); - -private: - QVariant m_value; - int m_row = -1; - int m_column = -1; - QQmlComponent *m_delegate = nullptr; -}; - -class Q_QML_PRIVATE_EXPORT QQmlDelegateChooser : public QQmlAbstractDelegateComponent -{ - Q_OBJECT - Q_PROPERTY(QString role READ role WRITE setRole NOTIFY roleChanged) - Q_PROPERTY(QQmlListProperty<QQmlDelegateChoice> choices READ choices CONSTANT) - Q_CLASSINFO("DefaultProperty", "choices") - -public: - QString role() const { return m_role; } - void setRole(const QString &role); - - virtual QQmlListProperty<QQmlDelegateChoice> choices(); - static void choices_append(QQmlListProperty<QQmlDelegateChoice> *, QQmlDelegateChoice *); - static int choices_count(QQmlListProperty<QQmlDelegateChoice> *); - static QQmlDelegateChoice *choices_at(QQmlListProperty<QQmlDelegateChoice> *, int); - static void choices_clear(QQmlListProperty<QQmlDelegateChoice> *); - - QQmlComponent *delegate(QQmlAdaptorModel *adaptorModel, int row, int column = -1) const override; - -signals: - void roleChanged(); - -private: - QString m_role; - QList<QQmlDelegateChoice *> m_choices; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlDelegateChoice) -QML_DECLARE_TYPE(QQmlDelegateChooser) - -#endif // QQMLDELEGATECOMPONENT_P_H diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp deleted file mode 100644 index 572f58339f..0000000000 --- a/src/qml/types/qqmldelegatemodel.cpp +++ /dev/null @@ -1,3542 +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); - 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.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); - 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); - 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); - QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(cacheItem)); - QV4::ScopedObject p(scope, model->m_cacheMetaType->modelItemProto.value()); - o->setPrototypeOf(p); - ++cacheItem->scriptRef; - - 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" diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h deleted file mode 100644 index 0ad8939732..0000000000 --- a/src/qml/types/qqmldelegatemodel_p.h +++ /dev/null @@ -1,249 +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$ -** -****************************************************************************/ - -#ifndef QQMLDATAMODEL_P_H -#define QQMLDATAMODEL_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtqmlglobal_p.h> -#include <private/qqmllistcompositor_p.h> -#include <private/qqmlobjectmodel_p.h> -#include <private/qqmlincubator_p.h> - -#include <QtCore/qabstractitemmodel.h> -#include <QtCore/qstringlist.h> - -#include <private/qv8engine_p.h> -#include <private/qqmlglobal_p.h> - -QT_REQUIRE_CONFIG(qml_delegate_model); - -QT_BEGIN_NAMESPACE - -class QQmlChangeSet; -class QQuickPackage; -class QQmlV4Function; -class QQmlDelegateModelGroup; -class QQmlDelegateModelAttached; -class QQmlDelegateModelPrivate; - - -class Q_QML_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public QQmlParserStatus -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlDelegateModel) - - Q_PROPERTY(QVariant model READ model WRITE setModel) - Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate) - Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) - Q_PROPERTY(QQmlDelegateModelGroup *items READ items CONSTANT) //TODO : worth renaming? - Q_PROPERTY(QQmlDelegateModelGroup *persistedItems READ persistedItems CONSTANT) - Q_PROPERTY(QQmlListProperty<QQmlDelegateModelGroup> groups READ groups CONSTANT) - Q_PROPERTY(QObject *parts READ parts CONSTANT) - Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) - Q_CLASSINFO("DefaultProperty", "delegate") - Q_INTERFACES(QQmlParserStatus) -public: - QQmlDelegateModel(); - QQmlDelegateModel(QQmlContext *, QObject *parent=nullptr); - ~QQmlDelegateModel(); - - void classBegin() override; - void componentComplete() override; - - QVariant model() const; - void setModel(const QVariant &); - - QQmlComponent *delegate() const; - void setDelegate(QQmlComponent *); - - QVariant rootIndex() const; - void setRootIndex(const QVariant &root); - - Q_INVOKABLE QVariant modelIndex(int idx) const; - Q_INVOKABLE QVariant parentModelIndex() const; - - int count() const override; - bool isValid() const override { return delegate() != nullptr; } - QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; - ReleaseFlags release(QObject *object) override; - void cancel(int index) override; - QString stringValue(int index, const QString &role) override; - void setWatchedRoles(const QList<QByteArray> &roles) override; - QQmlIncubator::Status incubationStatus(int index) override; - - int indexOf(QObject *object, QObject *objectContext) const override; - - QString filterGroup() const; - void setFilterGroup(const QString &group); - void resetFilterGroup(); - - QQmlDelegateModelGroup *items(); - QQmlDelegateModelGroup *persistedItems(); - QQmlListProperty<QQmlDelegateModelGroup> groups(); - QObject *parts(); - - const QAbstractItemModel *abstractItemModel() const override; - - bool event(QEvent *) override; - - static QQmlDelegateModelAttached *qmlAttachedProperties(QObject *obj); - -Q_SIGNALS: - void filterGroupChanged(); - void defaultGroupsChanged(); - void rootIndexChanged(); - -private Q_SLOTS: - void _q_itemsChanged(int index, int count, const QVector<int> &roles); - void _q_itemsInserted(int index, int count); - void _q_itemsRemoved(int index, int count); - void _q_itemsMoved(int from, int to, int count); - void _q_modelReset(); - void _q_rowsInserted(const QModelIndex &,int,int); - void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end); - void _q_rowsRemoved(const QModelIndex &,int,int); - void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int); - void _q_dataChanged(const QModelIndex&,const QModelIndex&,const QVector<int> &); - void _q_layoutChanged(const QList<QPersistentModelIndex>&, QAbstractItemModel::LayoutChangeHint); - -private: - bool isDescendantOf(const QPersistentModelIndex &desc, const QList<QPersistentModelIndex> &parents) const; - - Q_DISABLE_COPY(QQmlDelegateModel) -}; - -class QQmlDelegateModelGroupPrivate; -class Q_QML_PRIVATE_EXPORT QQmlDelegateModelGroup : public QObject -{ - Q_OBJECT - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - Q_PROPERTY(bool includeByDefault READ defaultInclude WRITE setDefaultInclude NOTIFY defaultIncludeChanged) -public: - QQmlDelegateModelGroup(QObject *parent = nullptr); - QQmlDelegateModelGroup(const QString &name, QQmlDelegateModel *model, int compositorType, QObject *parent = nullptr); - ~QQmlDelegateModelGroup(); - - QString name() const; - void setName(const QString &name); - - int count() const; - - bool defaultInclude() const; - void setDefaultInclude(bool include); - - Q_INVOKABLE QQmlV4Handle get(int index); - -public Q_SLOTS: - void insert(QQmlV4Function *); - void create(QQmlV4Function *); - void resolve(QQmlV4Function *); - void remove(QQmlV4Function *); - void addGroups(QQmlV4Function *); - void removeGroups(QQmlV4Function *); - void setGroups(QQmlV4Function *); - void move(QQmlV4Function *); - -Q_SIGNALS: - void countChanged(); - void nameChanged(); - void defaultIncludeChanged(); - void changed(const QQmlV4Handle &removed, const QQmlV4Handle &inserted); -private: - Q_DECLARE_PRIVATE(QQmlDelegateModelGroup) -}; - -class QQmlDelegateModelItem; -class QQmlDelegateModelAttachedMetaObject; -class QQmlDelegateModelAttached : public QObject -{ - Q_OBJECT - Q_PROPERTY(QQmlDelegateModel *model READ model CONSTANT) - Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged) - Q_PROPERTY(bool isUnresolved READ isUnresolved NOTIFY unresolvedChanged) -public: - QQmlDelegateModelAttached(QObject *parent); - QQmlDelegateModelAttached(QQmlDelegateModelItem *cacheItem, QObject *parent); - ~QQmlDelegateModelAttached() {} - - void resetCurrentIndex(); - void setCacheItem(QQmlDelegateModelItem *item); - - QQmlDelegateModel *model() const; - - QStringList groups() const; - void setGroups(const QStringList &groups); - - bool isUnresolved() const; - - void emitChanges(); - - void emitUnresolvedChanged() { Q_EMIT unresolvedChanged(); } - -Q_SIGNALS: - void groupsChanged(); - void unresolvedChanged(); - -public: - QQmlDelegateModelItem *m_cacheItem; - int m_previousGroups; - int m_currentIndex[QQmlListCompositor::MaximumGroupCount]; - int m_previousIndex[QQmlListCompositor::MaximumGroupCount]; - - friend class QQmlDelegateModelAttachedMetaObject; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlDelegateModel) -QML_DECLARE_TYPEINFO(QQmlDelegateModel, QML_HAS_ATTACHED_PROPERTIES) -QML_DECLARE_TYPE(QQmlDelegateModelGroup) - -#endif // QQMLDATAMODEL_P_H diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h deleted file mode 100644 index 0028849828..0000000000 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ /dev/null @@ -1,450 +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$ -** -****************************************************************************/ - -#ifndef QQMLDATAMODEL_P_P_H -#define QQMLDATAMODEL_P_P_H - -#include "qqmldelegatemodel_p.h" -#include <private/qv4qobjectwrapper_p.h> - -#include <QtQml/qqmlcontext.h> -#include <QtQml/qqmlincubator.h> - -#include <private/qqmladaptormodel_p.h> -#include <private/qqmlopenmetaobject_p.h> - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -QT_REQUIRE_CONFIG(qml_delegate_model); - -QT_BEGIN_NAMESPACE - -typedef QQmlListCompositor Compositor; - -class QQmlDelegateModelAttachedMetaObject; -class QQmlAbstractDelegateComponent; - -class Q_QML_PRIVATE_EXPORT QQmlDelegateModelItemMetaType : public QQmlRefCount -{ -public: - QQmlDelegateModelItemMetaType(QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames); - ~QQmlDelegateModelItemMetaType(); - - void initializeMetaObject(); - void initializePrototype(); - - int parseGroups(const QStringList &groupNames) const; - int parseGroups(const QV4::Value &groupNames) const; - - QPointer<QQmlDelegateModel> model; - const int groupCount; - QV4::ExecutionEngine * const v4Engine; - QQmlDelegateModelAttachedMetaObject *metaObject; - const QStringList groupNames; - QV4::PersistentValue modelItemProto; -}; - -class QQmlAdaptorModel; -class QQDMIncubationTask; - -class QQmlDelegateModelItem : public QObject -{ - Q_OBJECT - Q_PROPERTY(int index READ modelIndex NOTIFY modelIndexChanged) - Q_PROPERTY(int row READ modelRow NOTIFY rowChanged REVISION 12) - Q_PROPERTY(int column READ modelColumn NOTIFY columnChanged REVISION 12) - Q_PROPERTY(QObject *model READ modelObject CONSTANT) -public: - QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, - QQmlAdaptorModel::Accessors *accessor, int modelIndex, - int row, int column); - ~QQmlDelegateModelItem(); - - void referenceObject() { ++objectRef; } - bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); } - bool isObjectReferenced() const { return objectRef != 0 || (groups & Compositor::PersistedFlag); } - void childContextObjectDestroyed(QObject *childContextObject); - - bool isReferenced() const { - return scriptRef - || incubationTask - || ((groups & Compositor::UnresolvedFlag) && (groups & Compositor::GroupMask)); - } - - void Dispose(); - - QObject *modelObject() { return this; } - - void destroyObject(); - - static QQmlDelegateModelItem *dataForObject(QObject *object); - - int groupIndex(Compositor::Group group); - - int modelRow() const { return row; } - int modelColumn() const { return column; } - int modelIndex() const { return index; } - virtual void setModelIndex(int idx, int newRow, int newColumn); - - virtual QV4::ReturnedValue get() { return QV4::QObjectWrapper::wrap(v4, this); } - - virtual void setValue(const QString &role, const QVariant &value) { Q_UNUSED(role); Q_UNUSED(value); } - virtual bool resolveIndex(const QQmlAdaptorModel &, int) { return false; } - - static QV4::ReturnedValue get_model(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - static QV4::ReturnedValue get_groups(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - static QV4::ReturnedValue set_groups(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - static QV4::ReturnedValue get_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &); - static QV4::ReturnedValue set_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &arg); - static QV4::ReturnedValue get_index(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &arg); - - QV4::ExecutionEngine *v4; - QQmlDelegateModelItemMetaType * const metaType; - QQmlContextDataRef contextData; - QPointer<QObject> object; - QPointer<QQmlDelegateModelAttached> attached; - QQDMIncubationTask *incubationTask; - QQmlComponent *delegate; - int poolTime; - int objectRef; - int scriptRef; - int groups; - int index; - -Q_SIGNALS: - void modelIndexChanged(); - Q_REVISION(12) void rowChanged(); - Q_REVISION(12) void columnChanged(); - -protected: - void objectDestroyed(QObject *); - int row; - int column; -}; - -namespace QV4 { -namespace Heap { -struct QQmlDelegateModelItemObject : Object { - inline void init(QQmlDelegateModelItem *item); - void destroy(); - QQmlDelegateModelItem *item; -}; - -} -} - -struct QQmlDelegateModelItemObject : QV4::Object -{ - V4_OBJECT2(QQmlDelegateModelItemObject, QV4::Object) - V4_NEEDS_DESTROY -}; - -void QV4::Heap::QQmlDelegateModelItemObject::init(QQmlDelegateModelItem *item) -{ - Object::init(); - this->item = item; -} - - - -class QQmlDelegateModelPrivate; -class QQDMIncubationTask : public QQmlIncubator -{ -public: - QQDMIncubationTask(QQmlDelegateModelPrivate *l, IncubationMode mode) - : QQmlIncubator(mode) - , incubating(nullptr) - , vdm(l) {} - - void statusChanged(Status) override; - void setInitialState(QObject *) override; - - QQmlDelegateModelItem *incubating = nullptr; - QQmlDelegateModelPrivate *vdm = nullptr; - int index[QQmlListCompositor::MaximumGroupCount]; -}; - - -class QQmlDelegateModelGroupEmitter -{ -public: - virtual ~QQmlDelegateModelGroupEmitter() {} - virtual void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) = 0; - virtual void createdPackage(int, QQuickPackage *) {} - virtual void initPackage(int, QQuickPackage *) {} - virtual void destroyingPackage(QQuickPackage *) {} - - QIntrusiveListNode emitterNode; -}; - -typedef QIntrusiveList<QQmlDelegateModelGroupEmitter, &QQmlDelegateModelGroupEmitter::emitterNode> QQmlDelegateModelGroupEmitterList; - -class QQmlDelegateModelGroupPrivate : public QObjectPrivate -{ -public: - Q_DECLARE_PUBLIC(QQmlDelegateModelGroup) - - QQmlDelegateModelGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {} - - static QQmlDelegateModelGroupPrivate *get(QQmlDelegateModelGroup *group) { - return static_cast<QQmlDelegateModelGroupPrivate *>(QObjectPrivate::get(group)); } - - void setModel(QQmlDelegateModel *model, Compositor::Group group); - bool isChangedConnected(); - void emitChanges(QV4::ExecutionEngine *engine); - void emitModelUpdated(bool reset); - - void createdPackage(int index, QQuickPackage *package); - void initPackage(int index, QQuickPackage *package); - void destroyingPackage(QQuickPackage *package); - - bool parseIndex(const QV4::Value &value, int *index, Compositor::Group *group) const; - bool parseGroupArgs( - QQmlV4Function *args, Compositor::Group *group, int *index, int *count, int *groups) const; - - Compositor::Group group; - QPointer<QQmlDelegateModel> model; - QQmlDelegateModelGroupEmitterList emitters; - QQmlChangeSet changeSet; - QString name; - bool defaultInclude; -}; - -class QQmlDelegateModelParts; - -class QQmlDelegateModelPrivate : public QObjectPrivate, public QQmlDelegateModelGroupEmitter -{ - Q_DECLARE_PUBLIC(QQmlDelegateModel) -public: - QQmlDelegateModelPrivate(QQmlContext *); - ~QQmlDelegateModelPrivate(); - - static QQmlDelegateModelPrivate *get(QQmlDelegateModel *m) { - return static_cast<QQmlDelegateModelPrivate *>(QObjectPrivate::get(m)); - } - - void init(); - void connectModel(QQmlAdaptorModel *model); - void connectToAbstractItemModel(); - void disconnectFromAbstractItemModel(); - - void requestMoreIfNecessary(); - QObject *object(Compositor::Group group, int index, QQmlIncubator::IncubationMode incubationMode); - QQmlDelegateModel::ReleaseFlags release(QObject *object); - QString stringValue(Compositor::Group group, int index, const QString &name); - void emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); - void emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); - void emitCreatedItem(QQDMIncubationTask *incubationTask, QObject *item) { - Q_EMIT q_func()->createdItem(incubationTask->index[m_compositorGroup], item); } - void emitInitItem(QQDMIncubationTask *incubationTask, QObject *item) { - Q_EMIT q_func()->initItem(incubationTask->index[m_compositorGroup], item); } - void emitDestroyingPackage(QQuickPackage *package); - void emitDestroyingItem(QObject *item) { Q_EMIT q_func()->destroyingItem(item); } - void addCacheItem(QQmlDelegateModelItem *item, Compositor::iterator it); - void removeCacheItem(QQmlDelegateModelItem *cacheItem); - - void updateFilterGroup(); - - void addGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - void removeGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - void setGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - - void itemsInserted( - const QVector<Compositor::Insert> &inserts, - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> *translatedInserts, - QHash<int, QList<QQmlDelegateModelItem *> > *movedItems = nullptr); - void itemsInserted(const QVector<Compositor::Insert> &inserts); - void itemsRemoved( - const QVector<Compositor::Remove> &removes, - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> *translatedRemoves, - QHash<int, QList<QQmlDelegateModelItem *> > *movedItems = nullptr); - void itemsRemoved(const QVector<Compositor::Remove> &removes); - void itemsMoved( - const QVector<Compositor::Remove> &removes, const QVector<Compositor::Insert> &inserts); - void itemsChanged(const QVector<Compositor::Change> &changes); - void emitChanges(); - void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) override; - void delegateChanged(bool add = true, bool remove = true); - - bool insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups); - - int adaptorModelCount() const; - - static void group_append(QQmlListProperty<QQmlDelegateModelGroup> *property, QQmlDelegateModelGroup *group); - static int group_count(QQmlListProperty<QQmlDelegateModelGroup> *property); - static QQmlDelegateModelGroup *group_at(QQmlListProperty<QQmlDelegateModelGroup> *property, int index); - - void releaseIncubator(QQDMIncubationTask *incubationTask); - void incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status); - void setInitialState(QQDMIncubationTask *incubationTask, QObject *o); - - QQmlAdaptorModel m_adaptorModel; - QQmlListCompositor m_compositor; - QQmlStrongJSQObjectReference<QQmlComponent> m_delegate; - QQmlAbstractDelegateComponent *m_delegateChooser; - QMetaObject::Connection m_delegateChooserChanged; - QQmlDelegateModelItemMetaType *m_cacheMetaType; - QPointer<QQmlContext> m_context; - QQmlDelegateModelParts *m_parts; - QQmlDelegateModelGroupEmitterList m_pendingParts; - - QList<QQmlDelegateModelItem *> m_cache; - QList<QQDMIncubationTask *> m_finishedIncubating; - QList<QByteArray> m_watchedRoles; - - QString m_filterGroup; - - int m_count; - int m_groupCount; - - QQmlListCompositor::Group m_compositorGroup; - bool m_complete : 1; - bool m_delegateValidated : 1; - bool m_reset : 1; - bool m_transaction : 1; - bool m_incubatorCleanupScheduled : 1; - bool m_waitingToFetchMore : 1; - - union { - struct { - QQmlDelegateModelGroup *m_cacheItems; - QQmlDelegateModelGroup *m_items; - QQmlDelegateModelGroup *m_persistedItems; - }; - QQmlDelegateModelGroup *m_groups[Compositor::MaximumGroupCount]; - }; -}; - -class QQmlPartsModel : public QQmlInstanceModel, public QQmlDelegateModelGroupEmitter -{ - Q_OBJECT - Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) -public: - QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent = nullptr); - ~QQmlPartsModel(); - - QString filterGroup() const; - void setFilterGroup(const QString &group); - void resetFilterGroup(); - void updateFilterGroup(); - void updateFilterGroup(Compositor::Group group, const QQmlChangeSet &changeSet); - - int count() const override; - bool isValid() const override; - QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; - ReleaseFlags release(QObject *item) override; - QString stringValue(int index, const QString &role) override; - QList<QByteArray> watchedRoles() const { return m_watchedRoles; } - void setWatchedRoles(const QList<QByteArray> &roles) override; - QQmlIncubator::Status incubationStatus(int index) override; - - int indexOf(QObject *item, QObject *objectContext) const override; - - void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) override; - - void createdPackage(int index, QQuickPackage *package) override; - void initPackage(int index, QQuickPackage *package) override; - void destroyingPackage(QQuickPackage *package) override; - -Q_SIGNALS: - void filterGroupChanged(); - -private: - QQmlDelegateModel *m_model; - QHash<QObject *, QQuickPackage *> m_packaged; - QString m_part; - QString m_filterGroup; - QList<QByteArray> m_watchedRoles; - QVector<int> m_pendingPackageInitializations; // vector holds model indices - Compositor::Group m_compositorGroup; - bool m_inheritGroup; - bool m_modelUpdatePending = true; -}; - -class QMetaPropertyBuilder; - -class QQmlDelegateModelPartsMetaObject : public QQmlOpenMetaObject -{ -public: - QQmlDelegateModelPartsMetaObject(QObject *parent) - : QQmlOpenMetaObject(parent) {} - - void propertyCreated(int, QMetaPropertyBuilder &) override; - QVariant initialValue(int) override; -}; - -class QQmlDelegateModelParts : public QObject -{ -Q_OBJECT -public: - QQmlDelegateModelParts(QQmlDelegateModel *parent); - - QQmlDelegateModel *model; - QList<QQmlPartsModel *> models; -}; - -class QQmlDelegateModelAttachedMetaObject : public QAbstractDynamicMetaObject, public QQmlRefCount -{ -public: - QQmlDelegateModelAttachedMetaObject( - QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject); - ~QQmlDelegateModelAttachedMetaObject(); - - void objectDestroyed(QObject *) override; - int metaCall(QObject *, QMetaObject::Call, int _id, void **) override; - -private: - QQmlDelegateModelItemMetaType * const metaType; - QMetaObject * const metaObject; - const int memberPropertyOffset; - const int indexPropertyOffset; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/types/qqmlinstantiator.cpp b/src/qml/types/qqmlinstantiator.cpp deleted file mode 100644 index a23ec0f2b4..0000000000 --- a/src/qml/types/qqmlinstantiator.cpp +++ /dev/null @@ -1,509 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion. -** 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 "qqmlinstantiator_p.h" -#include "qqmlinstantiator_p_p.h" -#include <QtQml/QQmlContext> -#include <QtQml/QQmlComponent> -#include <QtQml/QQmlInfo> -#include <QtQml/QQmlError> -#include <QtQml/private/qqmlobjectmodel_p.h> -#if QT_CONFIG(qml_delegate_model) -#include <QtQml/private/qqmldelegatemodel_p.h> -#endif - -QT_BEGIN_NAMESPACE - -QQmlInstantiatorPrivate::QQmlInstantiatorPrivate() - : componentComplete(true) - , effectiveReset(false) - , active(true) - , async(false) -#if QT_CONFIG(qml_delegate_model) - , ownModel(false) -#endif - , requestedIndex(-1) - , model(QVariant(1)) - , instanceModel(nullptr) - , delegate(nullptr) -{ -} - -QQmlInstantiatorPrivate::~QQmlInstantiatorPrivate() -{ - qDeleteAll(objects); -} - -void QQmlInstantiatorPrivate::clear() -{ - Q_Q(QQmlInstantiator); - if (!instanceModel) - return; - if (!objects.count()) - return; - - for (int i=0; i < objects.count(); i++) { - q->objectRemoved(i, objects[i]); - instanceModel->release(objects[i]); - } - objects.clear(); - q->objectChanged(); -} - -QObject *QQmlInstantiatorPrivate::modelObject(int index, bool async) -{ - requestedIndex = index; - QObject *o = instanceModel->object(index, async ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested); - requestedIndex = -1; - return o; -} - - -void QQmlInstantiatorPrivate::regenerate() -{ - Q_Q(QQmlInstantiator); - if (!componentComplete) - return; - - int prevCount = q->count(); - - clear(); - - if (!active || !instanceModel || !instanceModel->count() || !instanceModel->isValid()) { - if (prevCount) - q->countChanged(); - return; - } - - for (int i = 0; i < instanceModel->count(); i++) { - QObject *object = modelObject(i, async); - // If the item was already created we won't get a createdItem - if (object) - _q_createdItem(i, object); - } - if (q->count() != prevCount) - q->countChanged(); -} - -void QQmlInstantiatorPrivate::_q_createdItem(int idx, QObject* item) -{ - Q_Q(QQmlInstantiator); - if (objects.contains(item)) //Case when it was created synchronously in regenerate - return; - if (requestedIndex != idx) // Asynchronous creation, reference the object - (void)instanceModel->object(idx); - item->setParent(q); - if (objects.size() < idx + 1) { - int modelCount = instanceModel->count(); - if (objects.capacity() < modelCount) - objects.reserve(modelCount); - objects.resize(idx + 1); - } - if (QObject *o = objects.at(idx)) - instanceModel->release(o); - objects.replace(idx, item); - if (objects.count() == 1) - q->objectChanged(); - q->objectAdded(idx, item); -} - -void QQmlInstantiatorPrivate::_q_modelUpdated(const QQmlChangeSet &changeSet, bool reset) -{ - Q_Q(QQmlInstantiator); - - if (!componentComplete || effectiveReset) - return; - - if (reset) { - regenerate(); - if (changeSet.difference() != 0) - q->countChanged(); - return; - } - - int difference = 0; - QHash<int, QVector<QPointer<QObject> > > moved; - const QVector<QQmlChangeSet::Change> &removes = changeSet.removes(); - for (const QQmlChangeSet::Change &remove : removes) { - int index = qMin(remove.index, objects.count()); - int count = qMin(remove.index + remove.count, objects.count()) - index; - if (remove.isMove()) { - moved.insert(remove.moveId, objects.mid(index, count)); - objects.erase( - objects.begin() + index, - objects.begin() + index + count); - } else while (count--) { - QObject *obj = objects.at(index); - objects.remove(index); - q->objectRemoved(index, obj); - if (obj) - instanceModel->release(obj); - } - - difference -= remove.count; - } - - const QVector<QQmlChangeSet::Change> &inserts = changeSet.inserts(); - for (const QQmlChangeSet::Change &insert : inserts) { - int index = qMin(insert.index, objects.count()); - if (insert.isMove()) { - QVector<QPointer<QObject> > movedObjects = moved.value(insert.moveId); - objects = objects.mid(0, index) + movedObjects + objects.mid(index); - } else { - if (insert.index <= objects.size()) - objects.insert(insert.index, insert.count, nullptr); - for (int i = 0; i < insert.count; ++i) { - int modelIndex = index + i; - QObject* obj = modelObject(modelIndex, async); - if (obj) - _q_createdItem(modelIndex, obj); - } - } - difference += insert.count; - } - - if (difference != 0) - q->countChanged(); -} - -#if QT_CONFIG(qml_delegate_model) -void QQmlInstantiatorPrivate::makeModel() -{ - Q_Q(QQmlInstantiator); - QQmlDelegateModel* delegateModel = new QQmlDelegateModel(qmlContext(q), q); - instanceModel = delegateModel; - ownModel = true; - delegateModel->setDelegate(delegate); - delegateModel->classBegin(); //Pretend it was made in QML - if (componentComplete) - delegateModel->componentComplete(); -} -#endif - - -/*! - \qmltype Instantiator - \instantiates QQmlInstantiator - \inqmlmodule QtQml - \brief Dynamically creates objects. - - A Instantiator can be used to control the dynamic creation of objects, or to dynamically - create multiple objects from a template. - - The Instantiator element will manage the objects it creates. Those objects are parented to the - Instantiator and can also be deleted by the Instantiator if the Instantiator's properties change. Objects - can also be destroyed dynamically through other means, and the Instantiator will not recreate - them unless the properties of the Instantiator change. - -*/ -QQmlInstantiator::QQmlInstantiator(QObject *parent) - : QObject(*(new QQmlInstantiatorPrivate), parent) -{ -} - -QQmlInstantiator::~QQmlInstantiator() -{ -} - -/*! - \qmlsignal QtQml::Instantiator::objectAdded(int index, QtObject object) - - This signal is emitted when an object is added to the Instantiator. The \a index - parameter holds the index which the object has been given, and the \a object - parameter holds the \l QtObject that has been added. - - The corresponding handler is \c onObjectAdded. -*/ - -/*! - \qmlsignal QtQml::Instantiator::objectRemoved(int index, QtObject object) - - This signal is emitted when an object is removed from the Instantiator. The \a index - parameter holds the index which the object had been given, and the \a object - parameter holds the \l QtObject that has been removed. - - Do not keep a reference to \a object if it was created by this Instantiator, as - in these cases it will be deleted shortly after the signal is handled. - - The corresponding handler is \c onObjectRemoved. -*/ -/*! - \qmlproperty bool QtQml::Instantiator::active - - When active is true, and the delegate component is ready, the Instantiator will - create objects according to the model. When active is false, no objects - will be created and any previously created objects will be destroyed. - - Default is true. -*/ -bool QQmlInstantiator::isActive() const -{ - Q_D(const QQmlInstantiator); - return d->active; -} - -void QQmlInstantiator::setActive(bool newVal) -{ - Q_D(QQmlInstantiator); - if (newVal == d->active) - return; - d->active = newVal; - emit activeChanged(); - d->regenerate(); -} - -/*! - \qmlproperty bool QtQml::Instantiator::asynchronous - - When asynchronous is true the Instantiator will attempt to create objects - asynchronously. This means that objects may not be available immediately, - even if active is set to true. - - You can use the objectAdded signal to respond to items being created. - - Default is false. -*/ -bool QQmlInstantiator::isAsync() const -{ - Q_D(const QQmlInstantiator); - return d->async; -} - -void QQmlInstantiator::setAsync(bool newVal) -{ - Q_D(QQmlInstantiator); - if (newVal == d->async) - return; - d->async = newVal; - emit asynchronousChanged(); -} - - -/*! - \qmlproperty int QtQml::Instantiator::count - - The number of objects the Instantiator is currently managing. -*/ - -int QQmlInstantiator::count() const -{ - Q_D(const QQmlInstantiator); - return d->objects.count(); -} - -/*! - \qmlproperty QtQml::Component QtQml::Instantiator::delegate - \default - - The component used to create all objects. - - Note that an extra variable, index, will be available inside instances of the - delegate. This variable refers to the index of the instance inside the Instantiator, - and can be used to obtain the object through the objectAt method of the Instantiator. - - If this property is changed, all instances using the old delegate will be destroyed - and new instances will be created using the new delegate. -*/ -QQmlComponent* QQmlInstantiator::delegate() -{ - Q_D(QQmlInstantiator); - return d->delegate; -} - -void QQmlInstantiator::setDelegate(QQmlComponent* c) -{ - Q_D(QQmlInstantiator); - if (c == d->delegate) - return; - - d->delegate = c; - emit delegateChanged(); - -#if QT_CONFIG(qml_delegate_model) - if (!d->ownModel) - return; - - if (QQmlDelegateModel *dModel = qobject_cast<QQmlDelegateModel*>(d->instanceModel)) - dModel->setDelegate(c); - if (d->componentComplete) - d->regenerate(); -#endif -} - -/*! - \qmlproperty variant QtQml::Instantiator::model - - This property can be set to any of the supported \l {qml-data-models}{data models}: - - \list - \li A number that indicates the number of delegates to be created by the repeater - \li A model (e.g. a ListModel item, or a QAbstractItemModel subclass) - \li A string list - \li An object list - \endlist - - The type of model affects the properties that are exposed to the \l delegate. - - Default value is 1, which creates a single delegate instance. - - \sa {qml-data-models}{Data Models} -*/ - -QVariant QQmlInstantiator::model() const -{ - Q_D(const QQmlInstantiator); - return d->model; -} - -void QQmlInstantiator::setModel(const QVariant &v) -{ - Q_D(QQmlInstantiator); - if (d->model == v) - return; - - d->model = v; - //Don't actually set model until componentComplete in case it wants to create its delegates immediately - if (!d->componentComplete) - return; - - QQmlInstanceModel *prevModel = d->instanceModel; - QObject *object = qvariant_cast<QObject*>(v); - QQmlInstanceModel *vim = nullptr; - if (object && (vim = qobject_cast<QQmlInstanceModel *>(object))) { -#if QT_CONFIG(qml_delegate_model) - if (d->ownModel) { - delete d->instanceModel; - prevModel = nullptr; - d->ownModel = false; - } -#endif - d->instanceModel = vim; -#if QT_CONFIG(qml_delegate_model) - } else if (v != QVariant(0)){ - if (!d->ownModel) - d->makeModel(); - - if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel *>(d->instanceModel)) { - d->effectiveReset = true; - dataModel->setModel(v); - d->effectiveReset = false; - } -#endif - } - - if (d->instanceModel != prevModel) { - if (prevModel) { - disconnect(prevModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), - this, SLOT(_q_modelUpdated(QQmlChangeSet,bool))); - disconnect(prevModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(_q_createdItem(int,QObject*))); - //disconnect(prevModel, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*))); - } - - if (d->instanceModel) { - connect(d->instanceModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), - this, SLOT(_q_modelUpdated(QQmlChangeSet,bool))); - connect(d->instanceModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(_q_createdItem(int,QObject*))); - //connect(d->instanceModel, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*))); - } - } - - d->regenerate(); - emit modelChanged(); -} - -/*! - \qmlproperty QtObject QtQml::Instantiator::object - - This is a reference to the first created object, intended as a convenience - for the case where only one object has been created. -*/ -QObject *QQmlInstantiator::object() const -{ - Q_D(const QQmlInstantiator); - if (d->objects.count()) - return d->objects[0]; - return nullptr; -} - -/*! - \qmlmethod QtObject QtQml::Instantiator::objectAt(int index) - - Returns a reference to the object with the given \a index. -*/ -QObject *QQmlInstantiator::objectAt(int index) const -{ - Q_D(const QQmlInstantiator); - if (index >= 0 && index < d->objects.count()) - return d->objects[index]; - return nullptr; -} - -/*! - \internal -*/ -void QQmlInstantiator::classBegin() -{ - Q_D(QQmlInstantiator); - d->componentComplete = false; -} - -/*! - \internal -*/ -void QQmlInstantiator::componentComplete() -{ - Q_D(QQmlInstantiator); - d->componentComplete = true; -#if QT_CONFIG(qml_delegate_model) - if (d->ownModel) { - static_cast<QQmlDelegateModel*>(d->instanceModel)->componentComplete(); - d->regenerate(); - } else -#endif - { - QVariant realModel = d->model; - d->model = QVariant(0); - setModel(realModel); //If realModel == d->model this won't do anything, but that's fine since the model's 0 - //setModel calls regenerate - } -} - -QT_END_NAMESPACE - -#include "moc_qqmlinstantiator_p.cpp" diff --git a/src/qml/types/qqmlinstantiator_p.h b/src/qml/types/qqmlinstantiator_p.h deleted file mode 100644 index ca371adc23..0000000000 --- a/src/qml/types/qqmlinstantiator_p.h +++ /dev/null @@ -1,119 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion. -** 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$ -** -****************************************************************************/ - -#ifndef QQMLINSTANTIATOR_P_H -#define QQMLINSTANTIATOR_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtQml/qqmlcomponent.h> -#include <QtQml/qqmlparserstatus.h> -#include <QtQml/private/qtqmlglobal_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlInstantiatorPrivate; -class Q_QML_PRIVATE_EXPORT QQmlInstantiator : public QObject, public QQmlParserStatus -{ - Q_OBJECT - Q_INTERFACES(QQmlParserStatus) - - Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) - Q_PROPERTY(bool asynchronous READ isAsync WRITE setAsync NOTIFY asynchronousChanged) - Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) - Q_PROPERTY(QObject *object READ object NOTIFY objectChanged) - Q_CLASSINFO("DefaultProperty", "delegate") - -public: - QQmlInstantiator(QObject *parent = nullptr); - ~QQmlInstantiator(); - - bool isActive() const; - void setActive(bool newVal); - - bool isAsync() const; - void setAsync(bool newVal); - - int count() const; - - QQmlComponent* delegate(); - void setDelegate(QQmlComponent* c); - - QVariant model() const; - void setModel(const QVariant &v); - - QObject *object() const; - - Q_INVOKABLE QObject *objectAt(int index) const; - - void classBegin() override; - void componentComplete() override; - -Q_SIGNALS: - void modelChanged(); - void delegateChanged(); - void countChanged(); - void objectChanged(); - void activeChanged(); - void asynchronousChanged(); - - void objectAdded(int index, QObject* object); - void objectRemoved(int index, QObject* object); - -private: - Q_DISABLE_COPY(QQmlInstantiator) - Q_DECLARE_PRIVATE(QQmlInstantiator) - Q_PRIVATE_SLOT(d_func(), void _q_createdItem(int, QObject *)) - Q_PRIVATE_SLOT(d_func(), void _q_modelUpdated(const QQmlChangeSet &, bool)) -}; - -QT_END_NAMESPACE - -#endif // QQMLCREATOR_P_H diff --git a/src/qml/types/qqmlinstantiator_p_p.h b/src/qml/types/qqmlinstantiator_p_p.h deleted file mode 100644 index 4c76d5c689..0000000000 --- a/src/qml/types/qqmlinstantiator_p_p.h +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion. -** 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$ -** -****************************************************************************/ - -#ifndef QQMLINSTANTIATOR_P_P_H -#define QQMLINSTANTIATOR_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qqmlinstantiator_p.h" -#include <QObject> -#include <private/qobject_p.h> -#include <private/qqmlchangeset_p.h> -#include <private/qqmlobjectmodel_p.h> - -QT_BEGIN_NAMESPACE - -class Q_QML_PRIVATE_EXPORT QQmlInstantiatorPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QQmlInstantiator) - -public: - QQmlInstantiatorPrivate(); - ~QQmlInstantiatorPrivate(); - - void clear(); - void regenerate(); -#if QT_CONFIG(qml_delegate_model) - void makeModel(); -#endif - void _q_createdItem(int, QObject *); - void _q_modelUpdated(const QQmlChangeSet &, bool); - QObject *modelObject(int index, bool async); - - static QQmlInstantiatorPrivate *get(QQmlInstantiator *instantiator) { return instantiator->d_func(); } - static const QQmlInstantiatorPrivate *get(const QQmlInstantiator *instantiator) { return instantiator->d_func(); } - - bool componentComplete:1; - bool effectiveReset:1; - bool active:1; - bool async:1; -#if QT_CONFIG(qml_delegate_model) - bool ownModel:1; -#endif - int requestedIndex; - QVariant model; - QQmlInstanceModel *instanceModel; - QQmlComponent *delegate; - QVector<QPointer<QObject> > objects; -}; - -QT_END_NAMESPACE - -#endif // QQMLCREATOR_P_P_H diff --git a/src/qml/types/qqmlitemmodels.qdoc b/src/qml/types/qqmlitemmodels.qdoc deleted file mode 100644 index f6e1b0b1b9..0000000000 --- a/src/qml/types/qqmlitemmodels.qdoc +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** 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 Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \page qmodelindex-and-related-classes-in-qml.html - \title QModelIndex and related Classes in QML - - Since Qt 5.5, QModelIndex and QPersistentModelIndex are exposed in QML as - value-based types. Also exposed in a similar fashion are QModelIndexList, - QItemSelectionRange and QItemSelection. All objects from these types can - be passed back and forth between QML and C++ as \c var properties or plain - JavaScript variables. - - Below you will find an overview of the API exposed to QML for these classes. - For more information, refer to their C++ documentation. - - \note Since all these types are exposed as \l{Q_GADGET}{gadgets}, there are no property - change notification signals emitted. Therefore binding to their properties - may not give the expected results. This is especially true for QPersistentModelIndex. - - \section1 QModelIndex and QPersistentModelIndex Types - - \list - \li \b row : int - \li \b column : int - \li \b parent : QModelIndex - \li \b valid : bool - \li \b model : QAbstractItemModel - \li \b internalId : quint64 - \endlist - - All these properties are read-only, as are their C++ counterparts. - - \note The usual caveats apply to QModelIndex in QML. If the underlying model changes - or gets deleted, it may become dangerous to access its properties. Therefore, you - should not store any QModelIndex objects. You can, however, store QPersistentModelIndexe - objects in a safe way. - - \section1 QModelIndexList Type - - \l QModelIndexList is exposed in QML as a JavaScript array. Conversions are - automatically made from and to C++. In fact, any JavaScript array can be - converted back to QModelIndexList, with non-QModelIndex objects replaced by - invalid \l{QModelIndex}es. - - \note QModelIndex to QPersistentModelIndex conversion happens when accessing - the array elements because any QModelIndexList property retains reference - semantics when exposed this way. - - \section1 \l QItemSelectionRange Type - - \list - \li \b top : int - \li \b left : int - \li \b bottom : int - \li \b right : int - \li \b width : int - \li \b height : int - \li \b topLeft : QPersistentModelIndex - \li \b bottomRight : QPersistentModelIndex - \li \b parent : QModelIndex - \li \b valid : bool - \li \b empty : bool - \li \b model : QAbstractItemModel - \endlist - - All these properties are read-only, as are their C++ counterparts. In addition, - we also expose the following functions: - - \list - \li bool \b{contains}(QModelIndex \e index) - \li bool \b{contains}(int \e row, int \e column, QModelIndex \e parentIndex) - \li bool \b{intersects}(QItemSelectionRange \e other) - \li QItemSelectionRange \b{intersected}(QItemSelectionRange \e other) - \endlist - - \section1 QItemSelection Type - - Similarly to QModelIndexList, \l QItemSelection is exposed in QML as a JavaScript - array of QItemSelectionRange objects. Conversions are automatically made from and to C++. - In fact, any JavaScript array can be converted back to QItemSelection, with - non-QItemSelectionRange objects replaced by empty \l {QItemSelectionRange}s. - - - \sa ItemSelectionModel -*/ diff --git a/src/qml/types/qqmlitemselectionmodel.qdoc b/src/qml/types/qqmlitemselectionmodel.qdoc deleted file mode 100644 index 43da4f7a55..0000000000 --- a/src/qml/types/qqmlitemselectionmodel.qdoc +++ /dev/null @@ -1,239 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** 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 Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \qmltype ItemSelectionModel - \instantiates QItemSelectionModel - \inqmlmodule QtQml.Models - \since 5.5 - \ingroup qtquick-models - - \brief Instantiates a QItemSelectionModel to be used in conjunction - with a QAbstractItemModel and any view supporting it. - - \sa QItemSelectionModel, {Models and Views in Qt Quick} -*/ - - -/*! - \qmlproperty QAbstractItemModel ItemSelectionModel::model - - This property's value must match the view's model. -*/ - -/*! - \qmlproperty bool ItemSelectionModel::hasSelection - \readonly - - It will trigger property binding updates every time \l selectionChanged() - is emitted, even though its value hasn't changed. - - \sa selection, selectedIndexes, select(), selectionChanged() -*/ - -/*! - \qmlproperty QModelIndex ItemSelectionModel::currentIndex - \readonly - - Use \l setCurrentIndex() to set its value. - - \sa setCurrentIndex(), currentChanged() -*/ - -/*! - \qmlproperty QModelIndexList ItemSelectionModel::selectedIndexes - \readonly - - Contains the list of all the indexes in the selection model. -*/ - -/*! - \qmlmethod bool ItemSelectionModel::isSelected(QModelIndex index) - - Returns \c true if the given model item \a index is selected. -*/ - -/*! - \qmlmethod bool ItemSelectionModel::isRowSelected(int row, QModelIndex parent) - - Returns \c true if all items are selected in the \a row with the given - \a parent. - - Note that this function is usually faster than calling isSelected() - on all items in the same row, and that unselectable items are ignored. -*/ - -/*! - \qmlmethod bool ItemSelectionModel::isColumnSelected(int column, QModelIndex parent) - - Returns \c true if all items are selected in the \a column with the given - \a parent. - - Note that this function is usually faster than calling isSelected() - on all items in the same column, and that unselectable items are ignored. -*/ - -/*! - \qmlmethod bool ItemSelectionModel::rowIntersectsSelection(int row, QModelIndex parent) - - Returns \c true if there are any items selected in the \a row with the - given \a parent. -*/ - -/*! - \qmlmethod bool ItemSelectionModel::columnIntersectsSelection(int column, QModelIndex parent) - - Returns \c true if there are any items selected in the \a column with the - given \a parent. -*/ - -/*! - \qmlmethod QModelIndexList ItemSelectionModel::selectedRows(int column) - - Returns the indexes in the given \a column for the rows where all columns - are selected. - - \sa selectedColumns() -*/ - -/*! - \qmlmethod QModelIndexList ItemSelectionModel::selectedColumns(int row) - - Returns the indexes in the given \a row for columns where all rows are - selected. - - \sa selectedRows() -*/ - -/*! - \qmlproperty object ItemSelectionModel::selection - \readonly - - Holds the selection ranges stored in the selection model. -*/ - -/*! - \qmlmethod void ItemSelectionModel::setCurrentIndex(QModelIndex index, SelectionFlags command) - - Sets the model item \a index to be the current item, and emits - currentChanged(). The current item is used for keyboard navigation and - focus indication; it is independent of any selected items, although a - selected item can also be the current item. - - Depending on the specified \a command, the \a index can also become part - of the current selection. - - Valid \a command values are described in \l {itemselectionmodelselectindex} - {select(\e index, \e command)}. - - \sa select() -*/ - -/*! - \qmlmethod void ItemSelectionModel::select(QModelIndex index, SelectionFlags command) - \keyword itemselectionmodelselectindex - - Selects the model item \a index using the specified \a command, and emits - selectionChanged(). - - Valid values for the \a command parameter, are: - - \value NoUpdate No selection will be made. - \value Clear The complete selection will be cleared. - \value Select All specified indexes will be selected. - \value Deselect All specified indexes will be deselected. - \value Toggle All specified indexes will be selected or - deselected depending on their current state. - \value Current The current selection will be updated. - \value Rows All indexes will be expanded to span rows. - \value Columns All indexes will be expanded to span columns. - \value SelectCurrent A combination of Select and Current, provided for - convenience. - \value ToggleCurrent A combination of Toggle and Current, provided for - convenience. - \value ClearAndSelect A combination of Clear and Select, provided for - convenience. -*/ - -/*! - \qmlmethod void ItemSelectionModel::select(QItemSelection selection, SelectionFlags command) - - Selects the item \a selection using the specified \a command, and emits - selectionChanged(). - - Valid \a command values are described in \l {itemselectionmodelselectindex} - {select(\e index, \e command)}. -*/ - -/*! - \qmlmethod void ItemSelectionModel::clear() - - Clears the selection model. Emits selectionChanged() and currentChanged(). -*/ - -/*! - \qmlmethod void ItemSelectionModel::reset() - - Clears the selection model. Does not emit any signals. -*/ - -/*! - \qmlmethod void ItemSelectionModel::clearSelection() - - Clears the selection in the selection model. Emits selectionChanged(). -*/ - -/*! - \qmlmethod void ItemSelectionModel::clearCurrentIndex() - - Clears the current index. Emits currentChanged(). -*/ - -/*! - \qmlsignal ItemSelectionModel::selectionChanged(QItemSelection selected, QItemSelection deselected) - - This signal is emitted whenever the selection changes. The change in the - selection is represented as an item selection of \a deselected items and - an item selection of \a selected items. - - Note the that the current index changes independently from the selection. - Also note that this signal will not be emitted when the item model is reset. - - \sa select(), currentChanged() -*/ - -/*! - \qmlsignal ItemSelectionModel::currentChanged(QModelIndex current, QModelIndex previous) - - This signal is emitted whenever the current item changes. The \a previous - model item index is replaced by the \a current index as the selection's - current item. - - Note that this signal will not be emitted when the item model is reset. - - \sa currentIndex, setCurrentIndex(), selectionChanged() -*/ diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp deleted file mode 100644 index 565e60b3c1..0000000000 --- a/src/qml/types/qqmllistmodel.cpp +++ /dev/null @@ -1,2900 +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 "qqmllistmodel_p_p.h" -#include "qqmllistmodelworkeragent_p.h" -#include <private/qqmlopenmetaobject_p.h> -#include <private/qqmljsast_p.h> -#include <private/qqmljsengine_p.h> -#include <private/qjsvalue_p.h> - -#include <private/qqmlcustomparser_p.h> -#include <private/qqmlengine_p.h> -#include <private/qqmlnotifier_p.h> - -#include <private/qv4object_p.h> -#include <private/qv4dateobject_p.h> -#include <private/qv4objectiterator_p.h> -#include <private/qv4alloca_p.h> -#include <private/qv4lookup_p.h> - -#include <qqmlcontext.h> -#include <qqmlinfo.h> - -#include <QtCore/qdebug.h> -#include <QtCore/qstack.h> -#include <QXmlStreamReader> -#include <QtCore/qdatetime.h> -#include <QScopedValueRollback> - -Q_DECLARE_METATYPE(const QV4::CompiledData::Binding*); - -QT_BEGIN_NAMESPACE - -// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models. -enum { MIN_LISTMODEL_UID = 1024 }; - -static QAtomicInt uidCounter(MIN_LISTMODEL_UID); - -template <typename T> -static bool isMemoryUsed(const char *mem) -{ - for (size_t i=0 ; i < sizeof(T) ; ++i) { - if (mem[i] != 0) - return true; - } - - return false; -} - -static QString roleTypeName(ListLayout::Role::DataType t) -{ - static const QString roleTypeNames[] = { - QStringLiteral("String"), QStringLiteral("Number"), QStringLiteral("Bool"), - QStringLiteral("List"), QStringLiteral("QObject"), QStringLiteral("VariantMap"), - QStringLiteral("DateTime"), QStringLiteral("Function") - }; - - if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType) - return roleTypeNames[t]; - - return QString(); -} - -const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type) -{ - QStringHash<Role *>::Node *node = roleHash.findNode(key); - if (node) { - const Role &r = *node->value; - if (type != r.type) - qmlWarning(nullptr) << QStringLiteral("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); - return r; - } - - return createRole(key, type); -} - -const ListLayout::Role &ListLayout::getRoleOrCreate(QV4::String *key, Role::DataType type) -{ - QStringHash<Role *>::Node *node = roleHash.findNode(key); - if (node) { - const Role &r = *node->value; - if (type != r.type) - qmlWarning(nullptr) << QStringLiteral("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); - return r; - } - - QString qkey = key->toQString(); - - return createRole(qkey, type); -} - -const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type) -{ - const int dataSizes[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; - const int dataAlignments[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; - - Role *r = new Role; - r->name = key; - r->type = type; - - if (type == Role::List) { - r->subLayout = new ListLayout; - } else { - r->subLayout = nullptr; - } - - int dataSize = dataSizes[type]; - int dataAlignment = dataAlignments[type]; - - int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1); - if (dataOffset + dataSize > ListElement::BLOCK_SIZE) { - r->blockIndex = ++currentBlock; - r->blockOffset = 0; - currentBlockOffset = dataSize; - } else { - r->blockIndex = currentBlock; - r->blockOffset = dataOffset; - currentBlockOffset = dataOffset + dataSize; - } - - int roleIndex = roles.count(); - r->index = roleIndex; - - roles.append(r); - roleHash.insert(key, r); - - return *r; -} - -ListLayout::ListLayout(const ListLayout *other) : currentBlock(0), currentBlockOffset(0) -{ - const int otherRolesCount = other->roles.count(); - roles.reserve(otherRolesCount); - for (int i=0 ; i < otherRolesCount; ++i) { - Role *role = new Role(other->roles[i]); - roles.append(role); - roleHash.insert(role->name, role); - } - currentBlockOffset = other->currentBlockOffset; - currentBlock = other->currentBlock; -} - -ListLayout::~ListLayout() -{ - qDeleteAll(roles); -} - -void ListLayout::sync(ListLayout *src, ListLayout *target) -{ - int roleOffset = target->roles.count(); - int newRoleCount = src->roles.count() - roleOffset; - - for (int i=0 ; i < newRoleCount ; ++i) { - Role *role = new Role(src->roles[roleOffset + i]); - target->roles.append(role); - target->roleHash.insert(role->name, role); - } - - target->currentBlockOffset = src->currentBlockOffset; - target->currentBlock = src->currentBlock; -} - -ListLayout::Role::Role(const Role *other) -{ - name = other->name; - type = other->type; - blockIndex = other->blockIndex; - blockOffset = other->blockOffset; - index = other->index; - if (other->subLayout) - subLayout = new ListLayout(other->subLayout); - else - subLayout = nullptr; -} - -ListLayout::Role::~Role() -{ - delete subLayout; -} - -const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QVariant &data) -{ - Role::DataType type; - - switch (data.type()) { - case QVariant::Double: type = Role::Number; break; - case QVariant::Int: type = Role::Number; break; - case QVariant::Bool: type = Role::Bool; break; - case QVariant::String: type = Role::String; break; - case QVariant::Map: type = Role::VariantMap; break; - case QVariant::DateTime: type = Role::DateTime; break; - case QVariant::UserType: { - if (data.userType() == qMetaTypeId<QJSValue>() && - data.value<QJSValue>().isCallable()) { - type = Role::Function; - break; - } else if (data.userType() == qMetaTypeId<const QV4::CompiledData::Binding*>() - && data.value<const QV4::CompiledData::Binding*>()->isTranslationBinding()) { - type = Role::String; - break; - } else { - type = Role::List; - break; - } - } - default: type = Role::Invalid; break; - } - - if (type == Role::Invalid) { - qmlWarning(nullptr) << "Can't create role for unsupported data type"; - return nullptr; - } - - return &getRoleOrCreate(key, type); -} - -const ListLayout::Role *ListLayout::getExistingRole(const QString &key) const -{ - Role *r = nullptr; - QStringHash<Role *>::Node *node = roleHash.findNode(key); - if (node) - r = node->value; - return r; -} - -const ListLayout::Role *ListLayout::getExistingRole(QV4::String *key) const -{ - Role *r = nullptr; - QStringHash<Role *>::Node *node = roleHash.findNode(key); - if (node) - r = node->value; - return r; -} - -StringOrTranslation::StringOrTranslation(const QString &s) -{ - d.setFlag(); - setString(s); -} - -StringOrTranslation::StringOrTranslation(const QV4::CompiledData::Binding *binding) -{ - d.setFlag(); - clear(); - d = binding; -} - -StringOrTranslation::~StringOrTranslation() -{ - clear(); -} - -void StringOrTranslation::setString(const QString &s) -{ - d.setFlag(); - clear(); - QStringData *stringData = const_cast<QString &>(s).data_ptr(); - d = stringData; - if (stringData) - stringData->ref.ref(); -} - -void StringOrTranslation::setTranslation(const QV4::CompiledData::Binding *binding) -{ - d.setFlag(); - clear(); - d = binding; -} - -QString StringOrTranslation::toString(const QQmlListModel *owner) const -{ - if (d.isNull()) - return QString(); - if (d.isT1()) { - QStringDataPtr holder = { d.asT1() }; - holder.ptr->ref.ref(); - return QString(holder); - } - if (!owner) - return QString(); - return d.asT2()->valueAsString(owner->m_compilationUnit.data()); -} - -QString StringOrTranslation::asString() const -{ - if (d.isNull()) - return QString(); - if (!d.isT1()) - return QString(); - QStringDataPtr holder = { d.asT1() }; - holder.ptr->ref.ref(); - return QString(holder); -} - -void StringOrTranslation::clear() -{ - if (QStringData *strData = d.isT1() ? d.asT1() : nullptr) { - if (!strData->ref.deref()) - QStringData::deallocate(strData); - } - d = static_cast<QStringData *>(nullptr); -} - -QObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex) -{ - ListElement *e = elements[elementIndex]; - if (e->m_objectCache == nullptr) { - void *memory = operator new(sizeof(QObject) + sizeof(QQmlData)); - void *ddataMemory = ((char *)memory) + sizeof(QObject); - e->m_objectCache = new (memory) QObject; - QQmlData *ddata = new (ddataMemory) QQmlData; - ddata->ownMemory = false; - QObjectPrivate::get(e->m_objectCache)->declarativeData = ddata; - (void)new ModelNodeMetaObject(e->m_objectCache, model, elementIndex); - } - return e->m_objectCache; -} - -bool ListModel::sync(ListModel *src, ListModel *target) -{ - // Sanity check - - bool hasChanges = false; - - // Build hash of elements <-> uid for each of the lists - QHash<int, ElementSync> elementHash; - for (int i = 0; i < target->elements.count(); ++i) { - ListElement *e = target->elements.at(i); - int uid = e->getUid(); - ElementSync sync; - sync.target = e; - sync.targetIndex = i; - elementHash.insert(uid, sync); - } - for (int i = 0; i < src->elements.count(); ++i) { - ListElement *e = src->elements.at(i); - int uid = e->getUid(); - - QHash<int, ElementSync>::iterator it = elementHash.find(uid); - if (it == elementHash.end()) { - ElementSync sync; - sync.src = e; - sync.srcIndex = i; - elementHash.insert(uid, sync); - } else { - ElementSync &sync = it.value(); - sync.src = e; - sync.srcIndex = i; - } - } - - QQmlListModel *targetModel = target->m_modelCache; - - // Get list of elements that are in the target but no longer in the source. These get deleted first. - int rowsRemoved = 0; - for (int i = 0 ; i < target->elements.count() ; ++i) { - ListElement *element = target->elements.at(i); - ElementSync &s = elementHash.find(element->getUid()).value(); - Q_ASSERT(s.targetIndex >= 0); - // need to update the targetIndex, to keep it correct after removals - s.targetIndex -= rowsRemoved; - if (s.src == nullptr) { - Q_ASSERT(s.targetIndex == i); - hasChanges = true; - if (targetModel) - targetModel->beginRemoveRows(QModelIndex(), i, i); - s.target->destroy(target->m_layout); - target->elements.removeOne(s.target); - delete s.target; - if (targetModel) - targetModel->endRemoveRows(); - ++rowsRemoved; - --i; - continue; - } - } - - // Sync the layouts - ListLayout::sync(src->m_layout, target->m_layout); - - // Clear the target list, and append in correct order from the source - target->elements.clear(); - for (int i = 0; i < src->elements.count(); ++i) { - ListElement *srcElement = src->elements.at(i); - ElementSync &s = elementHash.find(srcElement->getUid()).value(); - Q_ASSERT(s.srcIndex >= 0); - ListElement *targetElement = s.target; - if (targetElement == nullptr) { - targetElement = new ListElement(srcElement->getUid()); - } - s.changedRoles = ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout); - target->elements.append(targetElement); - } - - target->updateCacheIndices(); - - // Update values stored in target meta objects - for (int i=0 ; i < target->elements.count() ; ++i) { - ListElement *e = target->elements[i]; - if (ModelNodeMetaObject *mo = e->objectCache()) - mo->updateValues(); - } - - // now emit the change notifications required. This can be safely done, as we're only emitting changes, moves and inserts, - // so the model indices can't be out of bounds - // - // to ensure things are kept in the correct order, emit inserts and moves first. This shouls ensure all persistent - // model indices are updated correctly - int rowsInserted = 0; - for (int i = 0 ; i < target->elements.count() ; ++i) { - ListElement *element = target->elements.at(i); - ElementSync &s = elementHash.find(element->getUid()).value(); - Q_ASSERT(s.srcIndex >= 0); - s.srcIndex += rowsInserted; - if (s.srcIndex != s.targetIndex) { - if (targetModel) { - if (s.targetIndex == -1) { - targetModel->beginInsertRows(QModelIndex(), i, i); - targetModel->endInsertRows(); - } else { - targetModel->beginMoveRows(QModelIndex(), i, i, QModelIndex(), s.srcIndex); - targetModel->endMoveRows(); - } - } - hasChanges = true; - ++rowsInserted; - } - if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) { - QModelIndex idx = targetModel->createIndex(i, 0); - if (targetModel) - targetModel->dataChanged(idx, idx, s.changedRoles); - hasChanges = true; - } - } - return hasChanges; -} - -ListModel::ListModel(ListLayout *layout, QQmlListModel *modelCache) : m_layout(layout), m_modelCache(modelCache) -{ -} - -void ListModel::destroy() -{ - for (const auto &destroyer : remove(0, elements.count())) - destroyer(); - - m_layout = nullptr; - if (m_modelCache && m_modelCache->m_primary == false) - delete m_modelCache; - m_modelCache = nullptr; -} - -int ListModel::appendElement() -{ - int elementIndex = elements.count(); - newElement(elementIndex); - return elementIndex; -} - -void ListModel::insertElement(int index) -{ - newElement(index); - updateCacheIndices(index); -} - -void ListModel::move(int from, int to, int n) -{ - if (from > to) { - // Only move forwards - flip if backwards moving - int tfrom = from; - int tto = to; - from = tto; - to = tto+n; - n = tfrom-tto; - } - - QPODVector<ListElement *, 4> store; - for (int i=0 ; i < (to-from) ; ++i) - store.append(elements[from+n+i]); - for (int i=0 ; i < n ; ++i) - store.append(elements[from+i]); - for (int i=0 ; i < store.count() ; ++i) - elements[from+i] = store[i]; - - updateCacheIndices(from, to + n); -} - -void ListModel::newElement(int index) -{ - ListElement *e = new ListElement; - elements.insert(index, e); -} - -void ListModel::updateCacheIndices(int start, int end) -{ - int count = elements.count(); - - if (end < 0 || end > count) - end = count; - - for (int i = start; i < end; ++i) { - ListElement *e = elements.at(i); - if (ModelNodeMetaObject *mo = e->objectCache()) - mo->m_elementIndex = i; - } -} - -QVariant ListModel::getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV4::ExecutionEngine *eng) -{ - if (roleIndex >= m_layout->roleCount()) - return QVariant(); - ListElement *e = elements[elementIndex]; - const ListLayout::Role &r = m_layout->getExistingRole(roleIndex); - return e->getProperty(r, owner, eng); -} - -ListModel *ListModel::getListProperty(int elementIndex, const ListLayout::Role &role) -{ - ListElement *e = elements[elementIndex]; - return e->getListProperty(role); -} - -void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles) -{ - ListElement *e = elements[elementIndex]; - - QV4::ExecutionEngine *v4 = object->engine(); - QV4::Scope scope(v4); - QV4::ScopedObject o(scope); - - QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly); - QV4::ScopedString propertyName(scope); - QV4::ScopedValue propertyValue(scope); - while (1) { - propertyName = it.nextPropertyNameAsString(propertyValue); - if (!propertyName) - break; - - // Check if this key exists yet - int roleIndex = -1; - - // Add the value now - if (const QV4::String *s = propertyValue->as<QV4::String>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); - roleIndex = e->setStringProperty(r, s->toQString()); - } else if (propertyValue->isNumber()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); - roleIndex = e->setDoubleProperty(r, propertyValue->asDouble()); - } else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); - ListModel *subModel = new ListModel(r.subLayout, nullptr); - - int arrayLength = a->getLength(); - for (int j=0 ; j < arrayLength ; ++j) { - o = a->get(j); - subModel->append(o); - } - - roleIndex = e->setListProperty(r, subModel); - } else if (propertyValue->isBoolean()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); - roleIndex = e->setBoolProperty(r, propertyValue->booleanValue()); - } else if (QV4::DateObject *dd = propertyValue->as<QV4::DateObject>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); - QDateTime dt = dd->toQDateTime(); - roleIndex = e->setDateTimeProperty(r, dt); - } else if (QV4::FunctionObject *f = propertyValue->as<QV4::FunctionObject>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Function); - QV4::ScopedFunctionObject func(scope, f); - QJSValue jsv; - QJSValuePrivate::setValue(&jsv, v4, func); - roleIndex = e->setFunctionProperty(r, jsv); - } else if (QV4::Object *o = propertyValue->as<QV4::Object>()) { - if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) { - QObject *o = wrapper->object(); - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); - if (role.type == ListLayout::Role::QObject) - roleIndex = e->setQObjectProperty(role, o); - } else { - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); - if (role.type == ListLayout::Role::VariantMap) { - QV4::ScopedObject obj(scope, o); - roleIndex = e->setVariantMapProperty(role, obj); - } - } - } else if (propertyValue->isNullOrUndefined()) { - const ListLayout::Role *r = m_layout->getExistingRole(propertyName); - if (r) - e->clearProperty(*r); - } - - if (roleIndex != -1) - roles->append(roleIndex); - } - - if (ModelNodeMetaObject *mo = e->objectCache()) - mo->updateValues(*roles); -} - -void ListModel::set(int elementIndex, QV4::Object *object) -{ - if (!object) - return; - - ListElement *e = elements[elementIndex]; - - QV4::ExecutionEngine *v4 = object->engine(); - QV4::Scope scope(v4); - - QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly); - QV4::ScopedString propertyName(scope); - QV4::ScopedValue propertyValue(scope); - QV4::ScopedObject o(scope); - while (1) { - propertyName = it.nextPropertyNameAsString(propertyValue); - if (!propertyName) - break; - - // Add the value now - if (QV4::String *s = propertyValue->stringValue()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); - if (r.type == ListLayout::Role::String) - e->setStringPropertyFast(r, s->toQString()); - } else if (propertyValue->isNumber()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); - if (r.type == ListLayout::Role::Number) { - e->setDoublePropertyFast(r, propertyValue->asDouble()); - } - } else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); - if (r.type == ListLayout::Role::List) { - ListModel *subModel = new ListModel(r.subLayout, nullptr); - - int arrayLength = a->getLength(); - for (int j=0 ; j < arrayLength ; ++j) { - o = a->get(j); - subModel->append(o); - } - - e->setListPropertyFast(r, subModel); - } - } else if (propertyValue->isBoolean()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); - if (r.type == ListLayout::Role::Bool) { - e->setBoolPropertyFast(r, propertyValue->booleanValue()); - } - } else if (QV4::DateObject *date = propertyValue->as<QV4::DateObject>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); - if (r.type == ListLayout::Role::DateTime) { - QDateTime dt = date->toQDateTime();; - e->setDateTimePropertyFast(r, dt); - } - } else if (QV4::Object *o = propertyValue->as<QV4::Object>()) { - if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) { - QObject *o = wrapper->object(); - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); - if (r.type == ListLayout::Role::QObject) - e->setQObjectPropertyFast(r, o); - } else { - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); - if (role.type == ListLayout::Role::VariantMap) - e->setVariantMapFast(role, o); - } - } else if (propertyValue->isNullOrUndefined()) { - const ListLayout::Role *r = m_layout->getExistingRole(propertyName); - if (r) - e->clearProperty(*r); - } - } -} - -QVector<std::function<void()>> ListModel::remove(int index, int count) -{ - QVector<std::function<void()>> toDestroy; - auto layout = m_layout; - for (int i=0 ; i < count ; ++i) { - auto element = elements[index+i]; - toDestroy.append([element, layout](){ - element->destroy(layout); - delete element; - }); - } - elements.remove(index, count); - updateCacheIndices(index); - return toDestroy; -} - -void ListModel::insert(int elementIndex, QV4::Object *object) -{ - insertElement(elementIndex); - set(elementIndex, object); -} - -int ListModel::append(QV4::Object *object) -{ - int elementIndex = appendElement(); - set(elementIndex, object); - return elementIndex; -} - -int ListModel::setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data) -{ - int roleIndex = -1; - - if (elementIndex >= 0 && elementIndex < elements.count()) { - ListElement *e = elements[elementIndex]; - - const ListLayout::Role *r = m_layout->getRoleOrCreate(key, data); - if (r) { - roleIndex = e->setVariantProperty(*r, data); - - ModelNodeMetaObject *cache = e->objectCache(); - - if (roleIndex != -1 && cache) - cache->updateValues(QVector<int>(1, roleIndex)); - } - } - - return roleIndex; -} - -int ListModel::setExistingProperty(int elementIndex, const QString &key, const QV4::Value &data, QV4::ExecutionEngine *eng) -{ - int roleIndex = -1; - - if (elementIndex >= 0 && elementIndex < elements.count()) { - ListElement *e = elements[elementIndex]; - const ListLayout::Role *r = m_layout->getExistingRole(key); - if (r) - roleIndex = e->setJsProperty(*r, data, eng); - } - - return roleIndex; -} - -inline char *ListElement::getPropertyMemory(const ListLayout::Role &role) -{ - ListElement *e = this; - int blockIndex = 0; - while (blockIndex < role.blockIndex) { - if (e->next == nullptr) { - e->next = new ListElement; - e->next->uid = uid; - } - e = e->next; - ++blockIndex; - } - - char *mem = &e->data[role.blockOffset]; - return mem; -} - -ModelNodeMetaObject *ListElement::objectCache() -{ - if (!m_objectCache) - return nullptr; - return ModelNodeMetaObject::get(m_objectCache); -} - -StringOrTranslation *ListElement::getStringProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem); - return s; -} - -QObject *ListElement::getQObjectProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - QPointer<QObject> *o = reinterpret_cast<QPointer<QObject> *>(mem); - return o->data(); -} - -QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role) -{ - QVariantMap *map = nullptr; - - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QVariantMap>(mem)) - map = reinterpret_cast<QVariantMap *>(mem); - - return map; -} - -QDateTime *ListElement::getDateTimeProperty(const ListLayout::Role &role) -{ - QDateTime *dt = nullptr; - - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QDateTime>(mem)) - dt = reinterpret_cast<QDateTime *>(mem); - - return dt; -} - -QJSValue *ListElement::getFunctionProperty(const ListLayout::Role &role) -{ - QJSValue *f = nullptr; - - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QJSValue>(mem)) - f = reinterpret_cast<QJSValue *>(mem); - - return f; -} - -QPointer<QObject> *ListElement::getGuardProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - - bool existingGuard = false; - for (size_t i=0 ; i < sizeof(QPointer<QObject>) ; ++i) { - if (mem[i] != 0) { - existingGuard = true; - break; - } - } - - QPointer<QObject> *o = nullptr; - - if (existingGuard) - o = reinterpret_cast<QPointer<QObject> *>(mem); - - return o; -} - -ListModel *ListElement::getListProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - ListModel **value = reinterpret_cast<ListModel **>(mem); - return *value; -} - -QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng) -{ - char *mem = getPropertyMemory(role); - - QVariant data; - - switch (role.type) { - case ListLayout::Role::Number: - { - double *value = reinterpret_cast<double *>(mem); - data = *value; - } - break; - case ListLayout::Role::String: - { - StringOrTranslation *value = reinterpret_cast<StringOrTranslation *>(mem); - if (value->isSet()) - data = value->toString(owner); - } - break; - case ListLayout::Role::Bool: - { - bool *value = reinterpret_cast<bool *>(mem); - data = *value; - } - break; - case ListLayout::Role::List: - { - ListModel **value = reinterpret_cast<ListModel **>(mem); - ListModel *model = *value; - - if (model) { - if (model->m_modelCache == nullptr) { - model->m_modelCache = new QQmlListModel(owner, model, eng); - QQmlEngine::setContextForObject(model->m_modelCache, QQmlEngine::contextForObject(owner)); - } - - QObject *object = model->m_modelCache; - data = QVariant::fromValue(object); - } - } - break; - case ListLayout::Role::QObject: - { - QPointer<QObject> *guard = reinterpret_cast<QPointer<QObject> *>(mem); - QObject *object = guard->data(); - if (object) - data = QVariant::fromValue(object); - } - break; - case ListLayout::Role::VariantMap: - { - if (isMemoryUsed<QVariantMap>(mem)) { - QVariantMap *map = reinterpret_cast<QVariantMap *>(mem); - data = *map; - } - } - break; - case ListLayout::Role::DateTime: - { - if (isMemoryUsed<QDateTime>(mem)) { - QDateTime *dt = reinterpret_cast<QDateTime *>(mem); - data = *dt; - } - } - break; - case ListLayout::Role::Function: - { - if (isMemoryUsed<QJSValue>(mem)) { - QJSValue *func = reinterpret_cast<QJSValue *>(mem); - data = QVariant::fromValue(*func); - } - } - break; - default: - break; - } - - return data; -} - -int ListElement::setStringProperty(const ListLayout::Role &role, const QString &s) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::String) { - char *mem = getPropertyMemory(role); - StringOrTranslation *c = reinterpret_cast<StringOrTranslation *>(mem); - bool changed; - if (!c->isSet() || c->isTranslation()) - changed = true; - else - changed = c->asString().compare(s) != 0; - c->setString(s); - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setDoubleProperty(const ListLayout::Role &role, double d) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::Number) { - char *mem = getPropertyMemory(role); - double *value = reinterpret_cast<double *>(mem); - bool changed = *value != d; - *value = d; - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setBoolProperty(const ListLayout::Role &role, bool b) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::Bool) { - char *mem = getPropertyMemory(role); - bool *value = reinterpret_cast<bool *>(mem); - bool changed = *value != b; - *value = b; - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setListProperty(const ListLayout::Role &role, ListModel *m) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::List) { - char *mem = getPropertyMemory(role); - ListModel **value = reinterpret_cast<ListModel **>(mem); - if (*value && *value != m) { - (*value)->destroy(); - delete *value; - } - *value = m; - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::QObject) { - char *mem = getPropertyMemory(role); - QPointer<QObject> *g = reinterpret_cast<QPointer<QObject> *>(mem); - bool existingGuard = false; - for (size_t i=0 ; i < sizeof(QPointer<QObject>) ; ++i) { - if (mem[i] != 0) { - existingGuard = true; - break; - } - } - bool changed; - if (existingGuard) { - changed = g->data() != o; - g->~QPointer(); - } else { - changed = true; - } - new (mem) QPointer<QObject>(o); - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setVariantMapProperty(const ListLayout::Role &role, QV4::Object *o) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::VariantMap) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QVariantMap>(mem)) { - QVariantMap *map = reinterpret_cast<QVariantMap *>(mem); - map->~QMap(); - } - new (mem) QVariantMap(o->engine()->variantMapFromJS(o)); - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::VariantMap) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QVariantMap>(mem)) { - QVariantMap *map = reinterpret_cast<QVariantMap *>(mem); - if (m && map->isSharedWith(*m)) - return roleIndex; - map->~QMap(); - } else if (!m) { - return roleIndex; - } - if (m) - new (mem) QVariantMap(*m); - else - new (mem) QVariantMap; - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::DateTime) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QDateTime>(mem)) { - QDateTime *dt = reinterpret_cast<QDateTime *>(mem); - dt->~QDateTime(); - } - new (mem) QDateTime(dt); - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setFunctionProperty(const ListLayout::Role &role, const QJSValue &f) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::Function) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QJSValue>(mem)) { - QJSValue *f = reinterpret_cast<QJSValue *>(mem); - f->~QJSValue(); - } - new (mem) QJSValue(f); - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::String) { - char *mem = getPropertyMemory(role); - StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem); - s->setTranslation(b); - roleIndex = role.index; - } - - return roleIndex; -} - - -void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s) -{ - char *mem = getPropertyMemory(role); - new (mem) StringOrTranslation(s); -} - -void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d) -{ - char *mem = getPropertyMemory(role); - double *value = new (mem) double; - *value = d; -} - -void ListElement::setBoolPropertyFast(const ListLayout::Role &role, bool b) -{ - char *mem = getPropertyMemory(role); - bool *value = new (mem) bool; - *value = b; -} - -void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QObject *o) -{ - char *mem = getPropertyMemory(role); - new (mem) QPointer<QObject>(o); -} - -void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m) -{ - char *mem = getPropertyMemory(role); - ListModel **value = new (mem) ListModel *; - *value = m; -} - -void ListElement::setVariantMapFast(const ListLayout::Role &role, QV4::Object *o) -{ - char *mem = getPropertyMemory(role); - QVariantMap *map = new (mem) QVariantMap; - *map = o->engine()->variantMapFromJS(o); -} - -void ListElement::setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt) -{ - char *mem = getPropertyMemory(role); - new (mem) QDateTime(dt); -} - -void ListElement::setFunctionPropertyFast(const ListLayout::Role &role, const QJSValue &f) -{ - char *mem = getPropertyMemory(role); - new (mem) QJSValue(f); -} - -void ListElement::clearProperty(const ListLayout::Role &role) -{ - switch (role.type) { - case ListLayout::Role::String: - setStringProperty(role, QString()); - break; - case ListLayout::Role::Number: - setDoubleProperty(role, 0.0); - break; - case ListLayout::Role::Bool: - setBoolProperty(role, false); - break; - case ListLayout::Role::List: - setListProperty(role, nullptr); - break; - case ListLayout::Role::QObject: - setQObjectProperty(role, nullptr); - break; - case ListLayout::Role::DateTime: - setDateTimeProperty(role, QDateTime()); - break; - case ListLayout::Role::VariantMap: - setVariantMapProperty(role, (QVariantMap *)nullptr); - break; - case ListLayout::Role::Function: - setFunctionProperty(role, QJSValue()); - break; - default: - break; - } -} - -ListElement::ListElement() -{ - m_objectCache = nullptr; - uid = uidCounter.fetchAndAddOrdered(1); - next = nullptr; - memset(data, 0, sizeof(data)); -} - -ListElement::ListElement(int existingUid) -{ - m_objectCache = nullptr; - uid = existingUid; - next = nullptr; - memset(data, 0, sizeof(data)); -} - -ListElement::~ListElement() -{ - delete next; -} - -QVector<int> ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout) -{ - QVector<int> changedRoles; - for (int i=0 ; i < srcLayout->roleCount() ; ++i) { - const ListLayout::Role &srcRole = srcLayout->getExistingRole(i); - const ListLayout::Role &targetRole = targetLayout->getExistingRole(i); - - int roleIndex = -1; - switch (srcRole.type) { - case ListLayout::Role::List: - { - ListModel *srcSubModel = src->getListProperty(srcRole); - ListModel *targetSubModel = target->getListProperty(targetRole); - - if (srcSubModel) { - if (targetSubModel == nullptr) { - targetSubModel = new ListModel(targetRole.subLayout, nullptr); - target->setListPropertyFast(targetRole, targetSubModel); - } - if (ListModel::sync(srcSubModel, targetSubModel)) - roleIndex = targetRole.index; - } - } - break; - case ListLayout::Role::QObject: - { - QObject *object = src->getQObjectProperty(srcRole); - roleIndex = target->setQObjectProperty(targetRole, object); - } - break; - case ListLayout::Role::String: - case ListLayout::Role::Number: - case ListLayout::Role::Bool: - case ListLayout::Role::DateTime: - case ListLayout::Role::Function: - { - QVariant v = src->getProperty(srcRole, nullptr, nullptr); - roleIndex = target->setVariantProperty(targetRole, v); - } - break; - case ListLayout::Role::VariantMap: - { - QVariantMap *map = src->getVariantMapProperty(srcRole); - roleIndex = target->setVariantMapProperty(targetRole, map); - } - break; - default: - break; - } - if (roleIndex >= 0) - changedRoles << roleIndex; - } - - return changedRoles; -} - -void ListElement::destroy(ListLayout *layout) -{ - if (layout) { - for (int i=0 ; i < layout->roleCount() ; ++i) { - const ListLayout::Role &r = layout->getExistingRole(i); - - switch (r.type) { - case ListLayout::Role::String: - { - StringOrTranslation *string = getStringProperty(r); - if (string) - string->~StringOrTranslation(); - } - break; - case ListLayout::Role::List: - { - ListModel *model = getListProperty(r); - if (model) { - model->destroy(); - delete model; - } - } - break; - case ListLayout::Role::QObject: - { - QPointer<QObject> *guard = getGuardProperty(r); - if (guard) - guard->~QPointer(); - } - break; - case ListLayout::Role::VariantMap: - { - QVariantMap *map = getVariantMapProperty(r); - if (map) - map->~QMap(); - } - break; - case ListLayout::Role::DateTime: - { - QDateTime *dt = getDateTimeProperty(r); - if (dt) - dt->~QDateTime(); - } - break; - case ListLayout::Role::Function: - { - QJSValue *f = getFunctionProperty(r); - if (f) - f->~QJSValue(); - } - break; - default: - // other types don't need explicit cleanup. - break; - } - } - - if (m_objectCache) { - m_objectCache->~QObject(); - operator delete(m_objectCache); - } - } - - if (next) - next->destroy(nullptr); - uid = -1; -} - -int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant &d) -{ - int roleIndex = -1; - - switch (role.type) { - case ListLayout::Role::Number: - roleIndex = setDoubleProperty(role, d.toDouble()); - break; - case ListLayout::Role::String: - if (d.userType() == qMetaTypeId<const QV4::CompiledData::Binding *>()) - roleIndex = setTranslationProperty(role, d.value<const QV4::CompiledData::Binding*>()); - else - roleIndex = setStringProperty(role, d.toString()); - break; - case ListLayout::Role::Bool: - roleIndex = setBoolProperty(role, d.toBool()); - break; - case ListLayout::Role::List: - roleIndex = setListProperty(role, d.value<ListModel *>()); - break; - case ListLayout::Role::VariantMap: { - QVariantMap map = d.toMap(); - roleIndex = setVariantMapProperty(role, &map); - } - break; - case ListLayout::Role::DateTime: - roleIndex = setDateTimeProperty(role, d.toDateTime()); - break; - case ListLayout::Role::Function: - roleIndex = setFunctionProperty(role, d.value<QJSValue>()); - break; - default: - break; - } - - return roleIndex; -} - -int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d, QV4::ExecutionEngine *eng) -{ - // Check if this key exists yet - int roleIndex = -1; - - QV4::Scope scope(eng); - - // Add the value now - if (d.isString()) { - QString qstr = d.toQString(); - roleIndex = setStringProperty(role, qstr); - } else if (d.isNumber()) { - roleIndex = setDoubleProperty(role, d.asDouble()); - } else if (d.as<QV4::ArrayObject>()) { - QV4::ScopedArrayObject a(scope, d); - if (role.type == ListLayout::Role::List) { - QV4::Scope scope(a->engine()); - QV4::ScopedObject o(scope); - - ListModel *subModel = new ListModel(role.subLayout, nullptr); - int arrayLength = a->getLength(); - for (int j=0 ; j < arrayLength ; ++j) { - o = a->get(j); - subModel->append(o); - } - roleIndex = setListProperty(role, subModel); - } else { - qmlWarning(nullptr) << QStringLiteral("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List)); - } - } else if (d.isBoolean()) { - roleIndex = setBoolProperty(role, d.booleanValue()); - } else if (d.as<QV4::DateObject>()) { - QV4::Scoped<QV4::DateObject> dd(scope, d); - QDateTime dt = dd->toQDateTime(); - roleIndex = setDateTimeProperty(role, dt); - } else if (d.as<QV4::FunctionObject>()) { - QV4::ScopedFunctionObject f(scope, d); - QJSValue jsv; - QJSValuePrivate::setValue(&jsv, eng, f); - roleIndex = setFunctionProperty(role, jsv); - } else if (d.isObject()) { - QV4::ScopedObject o(scope, d); - QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>(); - if (role.type == ListLayout::Role::QObject && wrapper) { - QObject *o = wrapper->object(); - roleIndex = setQObjectProperty(role, o); - } else if (role.type == ListLayout::Role::VariantMap) { - roleIndex = setVariantMapProperty(role, o); - } - } else if (d.isNullOrUndefined()) { - clearProperty(role); - } - - return roleIndex; -} - -ModelNodeMetaObject::ModelNodeMetaObject(QObject *object, QQmlListModel *model, int elementIndex) -: QQmlOpenMetaObject(object), m_enabled(false), m_model(model), m_elementIndex(elementIndex), m_initialized(false) -{} - -void ModelNodeMetaObject::initialize() -{ - const int roleCount = m_model->m_listModel->roleCount(); - QVector<QByteArray> properties; - properties.reserve(roleCount); - for (int i = 0 ; i < roleCount ; ++i) { - const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i); - QByteArray name = role.name.toUtf8(); - properties << name; - } - type()->createProperties(properties); - updateValues(); - m_enabled = true; -} - -ModelNodeMetaObject::~ModelNodeMetaObject() -{ -} - -QAbstractDynamicMetaObject *ModelNodeMetaObject::toDynamicMetaObject(QObject *object) -{ - if (!m_initialized) { - m_initialized = true; - initialize(); - } - return QQmlOpenMetaObject::toDynamicMetaObject(object); -} - -ModelNodeMetaObject *ModelNodeMetaObject::get(QObject *obj) -{ - QObjectPrivate *op = QObjectPrivate::get(obj); - return static_cast<ModelNodeMetaObject*>(op->metaObject); -} - -void ModelNodeMetaObject::updateValues() -{ - const int roleCount = m_model->m_listModel->roleCount(); - if (!m_initialized) { - if (roleCount) { - Q_ALLOCA_VAR(int, changedRoles, roleCount * sizeof(int)); - for (int i = 0; i < roleCount; ++i) - changedRoles[i] = i; - emitDirectNotifies(changedRoles, roleCount); - } - return; - } - for (int i=0 ; i < roleCount ; ++i) { - const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i); - QByteArray name = role.name.toUtf8(); - const QVariant &data = m_model->data(m_elementIndex, i); - setValue(name, data, role.type == ListLayout::Role::List); - } -} - -void ModelNodeMetaObject::updateValues(const QVector<int> &roles) -{ - if (!m_initialized) { - emitDirectNotifies(roles.constData(), roles.count()); - return; - } - int roleCount = roles.count(); - for (int i=0 ; i < roleCount ; ++i) { - int roleIndex = roles.at(i); - const ListLayout::Role &role = m_model->m_listModel->getExistingRole(roleIndex); - QByteArray name = role.name.toUtf8(); - const QVariant &data = m_model->data(m_elementIndex, roleIndex); - setValue(name, data, role.type == ListLayout::Role::List); - } -} - -void ModelNodeMetaObject::propertyWritten(int index) -{ - if (!m_enabled) - return; - - QString propName = QString::fromUtf8(name(index)); - const QVariant value = this->value(index); - - QV4::Scope scope(m_model->engine()); - QV4::ScopedValue v(scope, scope.engine->fromVariant(value)); - - int roleIndex = m_model->m_listModel->setExistingProperty(m_elementIndex, propName, v, scope.engine); - if (roleIndex != -1) - m_model->emitItemsChanged(m_elementIndex, 1, QVector<int>(1, roleIndex)); -} - -// Does the emission of the notifiers when we haven't created the meta-object yet -void ModelNodeMetaObject::emitDirectNotifies(const int *changedRoles, int roleCount) -{ - Q_ASSERT(!m_initialized); - QQmlData *ddata = QQmlData::get(object(), /*create*/false); - if (!ddata) - return; - // There's nothing to emit if we're a list model in a worker thread. - if (!qmlEngine(m_model)) - return; - for (int i = 0; i < roleCount; ++i) { - const int changedRole = changedRoles[i]; - QQmlNotifier::notify(ddata, changedRole); - } -} - -namespace QV4 { - -bool ModelObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) -{ - if (!id.isString()) - return Object::virtualPut(m, id, value, receiver); - QString propName = id.toQString(); - - ModelObject *that = static_cast<ModelObject*>(m); - - ExecutionEngine *eng = that->engine(); - const int elementIndex = that->d()->elementIndex(); - int roleIndex = that->d()->m_model->m_listModel->setExistingProperty(elementIndex, propName, value, eng); - if (roleIndex != -1) - that->d()->m_model->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex)); - - ModelNodeMetaObject *mo = ModelNodeMetaObject::get(that->object()); - if (mo->initialized()) - mo->emitPropertyNotification(propName.toUtf8()); - return true; -} - -ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) -{ - if (!id.isString()) - return QObjectWrapper::virtualGet(m, id, receiver, hasProperty); - - const ModelObject *that = static_cast<const ModelObject*>(m); - Scope scope(that); - ScopedString name(scope, id.asStringOrSymbol()); - const ListLayout::Role *role = that->d()->m_model->m_listModel->getExistingRole(name); - if (!role) - return QObjectWrapper::virtualGet(m, id, receiver, hasProperty); - if (hasProperty) - *hasProperty = true; - - if (QQmlEngine *qmlEngine = that->engine()->qmlEngine()) { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine); - if (ep && ep->propertyCapture) - ep->propertyCapture->captureProperty(that->object(), -1, role->index, /*doNotify=*/ false); - } - - const int elementIndex = that->d()->elementIndex(); - QVariant value = that->d()->m_model->data(elementIndex, role->index); - return that->engine()->fromVariant(value); -} - -ReturnedValue ModelObject::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) -{ - lookup->getter = Lookup::getterFallback; - return lookup->getter(lookup, engine, *object); -} - -struct ModelObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator -{ - int roleNameIndex = 0; - ~ModelObjectOwnPropertyKeyIterator() override = default; - PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; - -}; - -PropertyKey ModelObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) -{ - const ModelObject *that = static_cast<const ModelObject *>(o); - - ExecutionEngine *v4 = that->engine(); - if (roleNameIndex < that->listModel()->roleCount()) { - Scope scope(that->engine()); - const ListLayout::Role &role = that->listModel()->getExistingRole(roleNameIndex); - ++roleNameIndex; - ScopedString roleName(scope, v4->newString(role.name)); - if (attrs) - *attrs = QV4::Attr_Data; - if (pd) { - QVariant value = that->d()->m_model->data(that->d()->elementIndex(), role.index); - pd->value = v4->fromVariant(value); - } - return roleName->toPropertyKey(); - } - - // Fall back to QV4::Object as opposed to QV4::QObjectWrapper otherwise it will add - // unnecessary entries that relate to the roles used. These just create extra work - // later on as they will just be ignored. - return ObjectOwnPropertyKeyIterator::next(o, pd, attrs); -} - -OwnPropertyKeyIterator *ModelObject::virtualOwnPropertyKeys(const Object *m, Value *target) -{ - *target = *m; - return new ModelObjectOwnPropertyKeyIterator; -} - -DEFINE_OBJECT_VTABLE(ModelObject); - -} // namespace QV4 - -DynamicRoleModelNode::DynamicRoleModelNode(QQmlListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this)) -{ - setNodeUpdatesEnabled(true); -} - -DynamicRoleModelNode *DynamicRoleModelNode::create(const QVariantMap &obj, QQmlListModel *owner) -{ - DynamicRoleModelNode *object = new DynamicRoleModelNode(owner, uidCounter.fetchAndAddOrdered(1)); - QVector<int> roles; - object->updateValues(obj, roles); - return object; -} - -QVector<int> DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target) -{ - QVector<int> changedRoles; - for (int i = 0; i < src->m_meta->count(); ++i) { - const QByteArray &name = src->m_meta->name(i); - QVariant value = src->m_meta->value(i); - - QQmlListModel *srcModel = qobject_cast<QQmlListModel *>(value.value<QObject *>()); - QQmlListModel *targetModel = qobject_cast<QQmlListModel *>(target->m_meta->value(i).value<QObject *>()); - - bool modelHasChanges = false; - if (srcModel) { - if (targetModel == nullptr) - targetModel = QQmlListModel::createWithOwner(target->m_owner); - - modelHasChanges = QQmlListModel::sync(srcModel, targetModel); - - QObject *targetModelObject = targetModel; - value = QVariant::fromValue(targetModelObject); - } else if (targetModel) { - delete targetModel; - } - - if (target->setValue(name, value) || modelHasChanges) - changedRoles << target->m_owner->m_roles.indexOf(QString::fromUtf8(name)); - } - return changedRoles; -} - -void DynamicRoleModelNode::updateValues(const QVariantMap &object, QVector<int> &roles) -{ - for (auto it = object.cbegin(), end = object.cend(); it != end; ++it) { - const QString &key = it.key(); - - int roleIndex = m_owner->m_roles.indexOf(key); - if (roleIndex == -1) { - roleIndex = m_owner->m_roles.count(); - m_owner->m_roles.append(key); - } - - QVariant value = it.value(); - - // A JS array/object is translated into a (hierarchical) QQmlListModel, - // so translate to a variant map/list first with toVariant(). - if (value.userType() == qMetaTypeId<QJSValue>()) - value = value.value<QJSValue>().toVariant(); - - if (value.type() == QVariant::List) { - QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner); - - QVariantList subArray = value.toList(); - QVariantList::const_iterator subIt = subArray.cbegin(); - QVariantList::const_iterator subEnd = subArray.cend(); - while (subIt != subEnd) { - const QVariantMap &subObject = subIt->toMap(); - subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); - ++subIt; - } - - QObject *subModelObject = subModel; - value = QVariant::fromValue(subModelObject); - } - - const QByteArray &keyUtf8 = key.toUtf8(); - - QQmlListModel *existingModel = qobject_cast<QQmlListModel *>(m_meta->value(keyUtf8).value<QObject *>()); - delete existingModel; - - if (m_meta->setValue(keyUtf8, value)) - roles << roleIndex; - } -} - -DynamicRoleModelNodeMetaObject::DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object) - : QQmlOpenMetaObject(object), m_enabled(false), m_owner(object) -{ -} - -DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject() -{ - for (int i=0 ; i < count() ; ++i) { - QQmlListModel *subModel = qobject_cast<QQmlListModel *>(value(i).value<QObject *>()); - delete subModel; - } -} - -void DynamicRoleModelNodeMetaObject::propertyWrite(int index) -{ - if (!m_enabled) - return; - - QVariant v = value(index); - QQmlListModel *model = qobject_cast<QQmlListModel *>(v.value<QObject *>()); - delete model; -} - -void DynamicRoleModelNodeMetaObject::propertyWritten(int index) -{ - if (!m_enabled) - return; - - QQmlListModel *parentModel = m_owner->m_owner; - - QVariant v = value(index); - - // A JS array/object is translated into a (hierarchical) QQmlListModel, - // so translate to a variant map/list first with toVariant(). - if (v.userType() == qMetaTypeId<QJSValue>()) - v= v.value<QJSValue>().toVariant(); - - if (v.type() == QVariant::List) { - QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel); - - QVariantList subArray = v.toList(); - QVariantList::const_iterator subIt = subArray.cbegin(); - QVariantList::const_iterator subEnd = subArray.cend(); - while (subIt != subEnd) { - const QVariantMap &subObject = subIt->toMap(); - subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); - ++subIt; - } - - QObject *subModelObject = subModel; - v = QVariant::fromValue(subModelObject); - - setValue(index, v); - } - - int elementIndex = parentModel->m_modelObjects.indexOf(m_owner); - if (elementIndex != -1) { - int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData())); - if (roleIndex != -1) - parentModel->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex)); - } -} - -/*! - \qmltype ListModel - \instantiates QQmlListModel - \inqmlmodule QtQml.Models - \ingroup qtquick-models - \brief Defines a free-form list data source. - - The ListModel is a simple container of ListElement definitions, each - containing data roles. The contents can be defined dynamically, or - explicitly in QML. - - The number of elements in the model can be obtained from its \l count property. - A number of familiar methods are also provided to manipulate the contents of the - model, including append(), insert(), move(), remove() and set(). These methods - accept dictionaries as their arguments; these are translated to ListElement objects - by the model. - - Elements can be manipulated via the model using the setProperty() method, which - allows the roles of the specified element to be set and changed. - - \section1 Example Usage - - The following example shows a ListModel containing three elements, with the roles - "name" and "cost". - - \div {class="float-right"} - \inlineimage listmodel.png - \enddiv - - \snippet qml/listmodel/listmodel.qml 0 - - Roles (properties) in each element must begin with a lower-case letter and - should be common to all elements in a model. The ListElement documentation - provides more guidelines for how elements should be defined. - - Since the example model contains an \c id property, it can be referenced - by views, such as the ListView in this example: - - \snippet qml/listmodel/listmodel-simple.qml 0 - \dots 8 - \snippet qml/listmodel/listmodel-simple.qml 1 - - It is possible for roles to contain list data. In the following example we - create a list of fruit attributes: - - \snippet qml/listmodel/listmodel-nested.qml model - - The delegate displays all the fruit attributes: - - \div {class="float-right"} - \inlineimage listmodel-nested.png - \enddiv - - \snippet qml/listmodel/listmodel-nested.qml delegate - - \section1 Modifying List Models - - The content of a ListModel may be created and modified using the clear(), - append(), set(), insert() and setProperty() methods. For example: - - \snippet qml/listmodel/listmodel-modify.qml delegate - - Note that when creating content dynamically the set of available properties - cannot be changed once set. Whatever properties are first added to the model - are the only permitted properties in the model. - - \section1 Using Threaded List Models with WorkerScript - - ListModel can be used together with WorkerScript access a list model - from multiple threads. This is useful if list modifications are - synchronous and take some time: the list operations can be moved to a - different thread to avoid blocking of the main GUI thread. - - Here is an example that uses WorkerScript to periodically append the - current time to a list model: - - \snippet ../quick/threading/threadedlistmodel/timedisplay.qml 0 - - The included file, \tt dataloader.mjs, looks like this: - - \snippet ../quick/threading/threadedlistmodel/dataloader.mjs 0 - - The timer in the main example sends messages to the worker script by calling - \l WorkerScript::sendMessage(). When this message is received, - \c WorkerScript.onMessage() is invoked in \c dataloader.mjs, - which appends the current time to the list model. - - Note the call to sync() from the external thread. - You must call sync() or else the changes made to the list from that - thread will not be reflected in the list model in the main thread. - - \sa {qml-data-models}{Data Models}, {Qt Quick Examples - Threading}, {Qt QML} -*/ - -QQmlListModel::QQmlListModel(QObject *parent) -: QAbstractListModel(parent) -{ - m_mainThread = true; - m_primary = true; - m_agent = nullptr; - m_dynamicRoles = false; - - m_layout = new ListLayout; - m_listModel = new ListModel(m_layout, this); - - m_engine = nullptr; -} - -QQmlListModel::QQmlListModel(const QQmlListModel *owner, ListModel *data, QV4::ExecutionEngine *engine, QObject *parent) -: QAbstractListModel(parent) -{ - m_mainThread = owner->m_mainThread; - m_primary = false; - m_agent = owner->m_agent; - - Q_ASSERT(owner->m_dynamicRoles == false); - m_dynamicRoles = false; - m_layout = nullptr; - m_listModel = data; - - m_engine = engine; - m_compilationUnit = owner->m_compilationUnit; -} - -QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent) -: QAbstractListModel(agent) -{ - m_mainThread = false; - m_primary = true; - m_agent = agent; - m_dynamicRoles = orig->m_dynamicRoles; - - m_layout = new ListLayout(orig->m_layout); - m_listModel = new ListModel(m_layout, this); - - if (m_dynamicRoles) - sync(orig, this); - else - ListModel::sync(orig->m_listModel, m_listModel); - - m_engine = nullptr; - m_compilationUnit = orig->m_compilationUnit; -} - -QQmlListModel::~QQmlListModel() -{ - qDeleteAll(m_modelObjects); - - if (m_primary) { - m_listModel->destroy(); - delete m_listModel; - - if (m_mainThread && m_agent) { - m_agent->modelDestroyed(); - m_agent->release(); - } - } - - m_listModel = nullptr; - - delete m_layout; - m_layout = nullptr; -} - -QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner) -{ - QQmlListModel *model = new QQmlListModel; - - model->m_mainThread = newOwner->m_mainThread; - model->m_engine = newOwner->m_engine; - model->m_agent = newOwner->m_agent; - model->m_dynamicRoles = newOwner->m_dynamicRoles; - - if (model->m_mainThread && model->m_agent) - model->m_agent->addref(); - - QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner)); - - return model; -} - -QV4::ExecutionEngine *QQmlListModel::engine() const -{ - if (m_engine == nullptr) { - m_engine = qmlEngine(this)->handle(); - } - - return m_engine; -} - -bool QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target) -{ - Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles); - - bool hasChanges = false; - - target->m_roles = src->m_roles; - - // Build hash of elements <-> uid for each of the lists - QHash<int, ElementSync> elementHash; - for (int i = 0 ; i < target->m_modelObjects.count(); ++i) { - DynamicRoleModelNode *e = target->m_modelObjects.at(i); - int uid = e->getUid(); - ElementSync sync; - sync.target = e; - sync.targetIndex = i; - elementHash.insert(uid, sync); - } - for (int i = 0 ; i < src->m_modelObjects.count(); ++i) { - DynamicRoleModelNode *e = src->m_modelObjects.at(i); - int uid = e->getUid(); - - QHash<int, ElementSync>::iterator it = elementHash.find(uid); - if (it == elementHash.end()) { - ElementSync sync; - sync.src = e; - sync.srcIndex = i; - elementHash.insert(uid, sync); - } else { - ElementSync &sync = it.value(); - sync.src = e; - sync.srcIndex = i; - } - } - - // Get list of elements that are in the target but no longer in the source. These get deleted first. - int rowsRemoved = 0; - for (int i = 0 ; i < target->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *element = target->m_modelObjects.at(i); - ElementSync &s = elementHash.find(element->getUid()).value(); - Q_ASSERT(s.targetIndex >= 0); - // need to update the targetIndex, to keep it correct after removals - s.targetIndex -= rowsRemoved; - if (s.src == nullptr) { - Q_ASSERT(s.targetIndex == i); - hasChanges = true; - target->beginRemoveRows(QModelIndex(), i, i); - target->m_modelObjects.remove(i, 1); - target->endRemoveRows(); - delete s.target; - ++rowsRemoved; - --i; - continue; - } - } - - // Clear the target list, and append in correct order from the source - target->m_modelObjects.clear(); - for (int i = 0 ; i < src->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *element = src->m_modelObjects.at(i); - ElementSync &s = elementHash.find(element->getUid()).value(); - Q_ASSERT(s.srcIndex >= 0); - DynamicRoleModelNode *targetElement = s.target; - if (targetElement == nullptr) { - targetElement = new DynamicRoleModelNode(target, element->getUid()); - } - s.changedRoles = DynamicRoleModelNode::sync(element, targetElement); - target->m_modelObjects.append(targetElement); - } - - // now emit the change notifications required. This can be safely done, as we're only emitting changes, moves and inserts, - // so the model indices can't be out of bounds - // - // to ensure things are kept in the correct order, emit inserts and moves first. This shouls ensure all persistent - // model indices are updated correctly - int rowsInserted = 0; - for (int i = 0 ; i < target->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *element = target->m_modelObjects.at(i); - ElementSync &s = elementHash.find(element->getUid()).value(); - Q_ASSERT(s.srcIndex >= 0); - s.srcIndex += rowsInserted; - if (s.srcIndex != s.targetIndex) { - if (s.targetIndex == -1) { - target->beginInsertRows(QModelIndex(), i, i); - target->endInsertRows(); - } else { - target->beginMoveRows(QModelIndex(), i, i, QModelIndex(), s.srcIndex); - target->endMoveRows(); - } - hasChanges = true; - ++rowsInserted; - } - if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) { - QModelIndex idx = target->createIndex(i, 0); - emit target->dataChanged(idx, idx, s.changedRoles); - hasChanges = true; - } - } - return hasChanges; -} - -void QQmlListModel::emitItemsChanged(int index, int count, const QVector<int> &roles) -{ - if (count <= 0) - return; - - if (m_mainThread) - emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);; -} - -void QQmlListModel::emitItemsAboutToBeInserted(int index, int count) -{ - Q_ASSERT(index >= 0 && count >= 0); - if (m_mainThread) - beginInsertRows(QModelIndex(), index, index + count - 1); -} - -void QQmlListModel::emitItemsInserted() -{ - if (m_mainThread) { - endInsertRows(); - emit countChanged(); - } -} - -QQmlListModelWorkerAgent *QQmlListModel::agent() -{ - if (m_agent) - return m_agent; - - m_agent = new QQmlListModelWorkerAgent(this); - return m_agent; -} - -QModelIndex QQmlListModel::index(int row, int column, const QModelIndex &parent) const -{ - return row >= 0 && row < count() && column == 0 && !parent.isValid() - ? createIndex(row, column) - : QModelIndex(); -} - -int QQmlListModel::rowCount(const QModelIndex &parent) const -{ - return !parent.isValid() ? count() : 0; -} - -QVariant QQmlListModel::data(const QModelIndex &index, int role) const -{ - return data(index.row(), role); -} - -bool QQmlListModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - const int row = index.row(); - if (row >= count() || row < 0) - return false; - - if (m_dynamicRoles) { - const QByteArray property = m_roles.at(role).toUtf8(); - if (m_modelObjects[row]->setValue(property, value)) { - emitItemsChanged(row, 1, QVector<int>(1, role)); - return true; - } - } else { - const ListLayout::Role &r = m_listModel->getExistingRole(role); - const int roleIndex = m_listModel->setOrCreateProperty(row, r.name, value); - if (roleIndex != -1) { - emitItemsChanged(row, 1, QVector<int>(1, role)); - return true; - } - } - - return false; -} - -QVariant QQmlListModel::data(int index, int role) const -{ - QVariant v; - - if (index >= count() || index < 0) - return v; - - if (m_dynamicRoles) - v = m_modelObjects[index]->getValue(m_roles[role]); - else - v = m_listModel->getProperty(index, role, this, engine()); - - return v; -} - -QHash<int, QByteArray> QQmlListModel::roleNames() const -{ - QHash<int, QByteArray> roleNames; - - if (m_dynamicRoles) { - for (int i = 0 ; i < m_roles.count() ; ++i) - roleNames.insert(i, m_roles.at(i).toUtf8()); - } else { - for (int i = 0 ; i < m_listModel->roleCount() ; ++i) { - const ListLayout::Role &r = m_listModel->getExistingRole(i); - roleNames.insert(i, r.name.toUtf8()); - } - } - - return roleNames; -} - -/*! - \qmlproperty bool ListModel::dynamicRoles - - By default, the type of a role is fixed the first time - the role is used. For example, if you create a role called - "data" and assign a number to it, you can no longer assign - a string to the "data" role. However, when the dynamicRoles - property is enabled, the type of a given role is not fixed - and can be different between elements. - - The dynamicRoles property must be set before any data is - added to the ListModel, and must be set from the main - thread. - - A ListModel that has data statically defined (via the - ListElement QML syntax) cannot have the dynamicRoles - property enabled. - - There is a significant performance cost to using a - ListModel with dynamic roles enabled. The cost varies - from platform to platform but is typically somewhere - between 4-6x slower than using static role types. - - Due to the performance cost of using dynamic roles, - they are disabled by default. -*/ -void QQmlListModel::setDynamicRoles(bool enableDynamicRoles) -{ - if (m_mainThread && m_agent == nullptr) { - if (enableDynamicRoles) { - if (m_layout->roleCount()) - qmlWarning(this) << tr("unable to enable dynamic roles as this model is not empty"); - else - m_dynamicRoles = true; - } else { - if (m_roles.count()) { - qmlWarning(this) << tr("unable to enable static roles as this model is not empty"); - } else { - m_dynamicRoles = false; - } - } - } else { - qmlWarning(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created"); - } -} - -/*! - \qmlproperty int ListModel::count - The number of data entries in the model. -*/ -int QQmlListModel::count() const -{ - return m_dynamicRoles ? m_modelObjects.count() : m_listModel->elementCount(); -} - -/*! - \qmlmethod ListModel::clear() - - Deletes all content from the model. - - \sa append(), remove() -*/ -void QQmlListModel::clear() -{ - removeElements(0, count()); -} - -/*! - \qmlmethod ListModel::remove(int index, int count = 1) - - Deletes the content at \a index from the model. - - \sa clear() -*/ -void QQmlListModel::remove(QQmlV4Function *args) -{ - int argLength = args->length(); - - if (argLength == 1 || argLength == 2) { - QV4::Scope scope(args->v4engine()); - int index = QV4::ScopedValue(scope, (*args)[0])->toInt32(); - int removeCount = (argLength == 2 ? QV4::ScopedValue(scope, (*args)[1])->toInt32() : 1); - - if (index < 0 || index+removeCount > count() || removeCount <= 0) { - qmlWarning(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count()); - return; - } - - removeElements(index, removeCount); - } else { - qmlWarning(this) << tr("remove: incorrect number of arguments"); - } -} - -void QQmlListModel::removeElements(int index, int removeCount) -{ - Q_ASSERT(index >= 0 && removeCount >= 0); - - if (!removeCount) - return; - - if (m_mainThread) - beginRemoveRows(QModelIndex(), index, index + removeCount - 1); - - QVector<std::function<void()>> toDestroy; - if (m_dynamicRoles) { - for (int i=0 ; i < removeCount ; ++i) { - auto modelObject = m_modelObjects[index+i]; - toDestroy.append([modelObject](){ - delete modelObject; - }); - } - m_modelObjects.remove(index, removeCount); - } else { - toDestroy = m_listModel->remove(index, removeCount); - } - - if (m_mainThread) { - endRemoveRows(); - emit countChanged(); - } - for (const auto &destroyer : toDestroy) - destroyer(); -} - -/*! - \qmlmethod ListModel::insert(int index, jsobject dict) - - Adds a new item to the list model at position \a index, with the - values in \a dict. - - \code - fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"}) - \endcode - - The \a index must be to an existing item in the list, or one past - the end of the list (equivalent to append). - - \sa set(), append() -*/ - -void QQmlListModel::insert(QQmlV4Function *args) -{ - if (args->length() == 2) { - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue arg0(scope, (*args)[0]); - int index = arg0->toInt32(); - - if (index < 0 || index > count()) { - qmlWarning(this) << tr("insert: index %1 out of range").arg(index); - return; - } - - QV4::ScopedObject argObject(scope, (*args)[1]); - QV4::ScopedArrayObject objectArray(scope, (*args)[1]); - if (objectArray) { - QV4::ScopedObject argObject(scope); - - int objectArrayLength = objectArray->getLength(); - emitItemsAboutToBeInserted(index, objectArrayLength); - for (int i=0 ; i < objectArrayLength ; ++i) { - argObject = objectArray->get(i); - - if (m_dynamicRoles) { - m_modelObjects.insert(index+i, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); - } else { - m_listModel->insert(index+i, argObject); - } - } - emitItemsInserted(); - } else if (argObject) { - emitItemsAboutToBeInserted(index, 1); - - if (m_dynamicRoles) { - m_modelObjects.insert(index, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); - } else { - m_listModel->insert(index, argObject); - } - - emitItemsInserted(); - } else { - qmlWarning(this) << tr("insert: value is not an object"); - } - } else { - qmlWarning(this) << tr("insert: value is not an object"); - } -} - -/*! - \qmlmethod ListModel::move(int from, int to, int n) - - Moves \a n items \a from one position \a to another. - - The from and to ranges must exist; for example, to move the first 3 items - to the end of the list: - - \code - fruitModel.move(0, fruitModel.count - 3, 3) - \endcode - - \sa append() -*/ -void QQmlListModel::move(int from, int to, int n) -{ - if (n == 0 || from == to) - return; - if (!canMove(from, to, n)) { - qmlWarning(this) << tr("move: out of range"); - return; - } - - if (m_mainThread) - beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to); - - if (m_dynamicRoles) { - - int realFrom = from; - int realTo = to; - int realN = n; - - if (from > to) { - // Only move forwards - flip if backwards moving - int tfrom = from; - int tto = to; - realFrom = tto; - realTo = tto+n; - realN = tfrom-tto; - } - - QPODVector<DynamicRoleModelNode *, 4> store; - for (int i=0 ; i < (realTo-realFrom) ; ++i) - store.append(m_modelObjects[realFrom+realN+i]); - for (int i=0 ; i < realN ; ++i) - store.append(m_modelObjects[realFrom+i]); - for (int i=0 ; i < store.count() ; ++i) - m_modelObjects[realFrom+i] = store[i]; - - } else { - m_listModel->move(from, to, n); - } - - if (m_mainThread) - endMoveRows(); -} - -/*! - \qmlmethod ListModel::append(jsobject dict) - - Adds a new item to the end of the list model, with the - values in \a dict. - - \code - fruitModel.append({"cost": 5.95, "name":"Pizza"}) - \endcode - - \sa set(), remove() -*/ -void QQmlListModel::append(QQmlV4Function *args) -{ - if (args->length() == 1) { - QV4::Scope scope(args->v4engine()); - QV4::ScopedObject argObject(scope, (*args)[0]); - QV4::ScopedArrayObject objectArray(scope, (*args)[0]); - - if (objectArray) { - QV4::ScopedObject argObject(scope); - - int objectArrayLength = objectArray->getLength(); - if (objectArrayLength > 0) { - int index = count(); - emitItemsAboutToBeInserted(index, objectArrayLength); - - for (int i=0 ; i < objectArrayLength ; ++i) { - argObject = objectArray->get(i); - - if (m_dynamicRoles) { - m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); - } else { - m_listModel->append(argObject); - } - } - - emitItemsInserted(); - } - } else if (argObject) { - int index; - - if (m_dynamicRoles) { - index = m_modelObjects.count(); - emitItemsAboutToBeInserted(index, 1); - m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); - } else { - index = m_listModel->elementCount(); - emitItemsAboutToBeInserted(index, 1); - m_listModel->append(argObject); - } - - emitItemsInserted(); - } else { - qmlWarning(this) << tr("append: value is not an object"); - } - } else { - qmlWarning(this) << tr("append: value is not an object"); - } -} - -/*! - \qmlmethod object ListModel::get(int index) - - Returns the item at \a index in the list model. This allows the item - data to be accessed or modified from JavaScript: - - \code - Component.onCompleted: { - fruitModel.append({"cost": 5.95, "name":"Jackfruit"}); - console.log(fruitModel.get(0).cost); - fruitModel.get(0).cost = 10.95; - } - \endcode - - The \a index must be an element in the list. - - Note that properties of the returned object that are themselves objects - will also be models, and this get() method is used to access elements: - - \code - fruitModel.append(..., "attributes": - [{"name":"spikes","value":"7mm"}, - {"name":"color","value":"green"}]); - fruitModel.get(0).attributes.get(1).value; // == "green" - \endcode - - \warning The returned object is not guaranteed to remain valid. It - should not be used in \l{Property Binding}{property bindings}. - - \sa append() -*/ -QQmlV4Handle QQmlListModel::get(int index) const -{ - QV4::Scope scope(engine()); - QV4::ScopedValue result(scope, QV4::Value::undefinedValue()); - - if (index >= 0 && index < count()) { - - if (m_dynamicRoles) { - DynamicRoleModelNode *object = m_modelObjects[index]; - result = QV4::QObjectWrapper::wrap(scope.engine, object); - } else { - QObject *object = m_listModel->getOrCreateModelObject(const_cast<QQmlListModel *>(this), index); - QQmlData *ddata = QQmlData::get(object); - if (ddata->jsWrapper.isNullOrUndefined()) { - result = scope.engine->memoryManager->allocate<QV4::ModelObject>(object, const_cast<QQmlListModel *>(this)); - // Keep track of the QObjectWrapper in persistent value storage - ddata->jsWrapper.set(scope.engine, result); - } else { - result = ddata->jsWrapper.value(); - } - } - } - - return QQmlV4Handle(result); -} - -/*! - \qmlmethod ListModel::set(int index, jsobject dict) - - Changes the item at \a index in the list model with the - values in \a dict. Properties not appearing in \a dict - are left unchanged. - - \code - fruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) - \endcode - - If \a index is equal to count() then a new item is appended to the - list. Otherwise, \a index must be an element in the list. - - \sa append() -*/ -void QQmlListModel::set(int index, const QQmlV4Handle &handle) -{ - QV4::Scope scope(engine()); - QV4::ScopedObject object(scope, handle); - - if (!object) { - qmlWarning(this) << tr("set: value is not an object"); - return; - } - if (index > count() || index < 0) { - qmlWarning(this) << tr("set: index %1 out of range").arg(index); - return; - } - - - if (index == count()) { - emitItemsAboutToBeInserted(index, 1); - - if (m_dynamicRoles) { - m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(object), this)); - } else { - m_listModel->insert(index, object); - } - - emitItemsInserted(); - } else { - - QVector<int> roles; - - if (m_dynamicRoles) { - m_modelObjects[index]->updateValues(scope.engine->variantMapFromJS(object), roles); - } else { - m_listModel->set(index, object, &roles); - } - - if (roles.count()) - emitItemsChanged(index, 1, roles); - } -} - -/*! - \qmlmethod ListModel::setProperty(int index, string property, variant value) - - Changes the \a property of the item at \a index in the list model to \a value. - - \code - fruitModel.setProperty(3, "cost", 5.95) - \endcode - - The \a index must be an element in the list. - - \sa append() -*/ -void QQmlListModel::setProperty(int index, const QString& property, const QVariant& value) -{ - if (count() == 0 || index >= count() || index < 0) { - qmlWarning(this) << tr("set: index %1 out of range").arg(index); - return; - } - - if (m_dynamicRoles) { - int roleIndex = m_roles.indexOf(property); - if (roleIndex == -1) { - roleIndex = m_roles.count(); - m_roles.append(property); - } - if (m_modelObjects[index]->setValue(property.toUtf8(), value)) - emitItemsChanged(index, 1, QVector<int>(1, roleIndex)); - } else { - int roleIndex = m_listModel->setOrCreateProperty(index, property, value); - if (roleIndex != -1) - emitItemsChanged(index, 1, QVector<int>(1, roleIndex)); - } -} - -/*! - \qmlmethod ListModel::sync() - - Writes any unsaved changes to the list model after it has been modified - from a worker script. -*/ -void QQmlListModel::sync() -{ - // This is just a dummy method to make it look like sync() exists in - // ListModel (and not just QQmlListModelWorkerAgent) and to let - // us document sync(). - qmlWarning(this) << "List sync() can only be called from a WorkerScript"; -} - -bool QQmlListModelParser::verifyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding) -{ - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - const quint32 targetObjectIndex = binding->value.objectIndex; - const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex); - QString objName = compilationUnit->stringAt(target->inheritedTypeNameIndex); - if (objName != listElementTypeName) { - const QMetaObject *mo = resolveType(objName); - if (mo != &QQmlListElement::staticMetaObject) { - error(target, QQmlListModel::tr("ListElement: cannot contain nested elements")); - return false; - } - listElementTypeName = objName; // cache right name for next time - } - - if (!compilationUnit->stringAt(target->idNameIndex).isEmpty()) { - error(target->locationOfIdProperty, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property")); - return false; - } - - const QV4::CompiledData::Binding *binding = target->bindingTable(); - for (quint32 i = 0; i < target->nBindings; ++i, ++binding) { - QString propName = compilationUnit->stringAt(binding->propertyNameIndex); - if (propName.isEmpty()) { - error(binding, QQmlListModel::tr("ListElement: cannot contain nested elements")); - return false; - } - if (!verifyProperty(compilationUnit, binding)) - return false; - } - } else if (binding->type == QV4::CompiledData::Binding::Type_Script) { - QString scriptStr = binding->valueAsScriptString(compilationUnit.data()); - if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) { - QByteArray script = scriptStr.toUtf8(); - bool ok; - evaluateEnum(script, &ok); - if (!ok) { - error(binding, QQmlListModel::tr("ListElement: cannot use script for property value")); - return false; - } - } - } - - return true; -} - -bool QQmlListModelParser::applyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex) -{ - const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex); - - bool roleSet = false; - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - const quint32 targetObjectIndex = binding->value.objectIndex; - const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex); - - ListModel *subModel = nullptr; - if (outterElementIndex == -1) { - subModel = model; - } else { - const ListLayout::Role &role = model->getOrCreateListRole(elementName); - if (role.type == ListLayout::Role::List) { - subModel = model->getListProperty(outterElementIndex, role); - if (subModel == nullptr) { - subModel = new ListModel(role.subLayout, nullptr); - QVariant vModel = QVariant::fromValue(subModel); - model->setOrCreateProperty(outterElementIndex, elementName, vModel); - } - } - } - - int elementIndex = subModel ? subModel->appendElement() : -1; - - const QV4::CompiledData::Binding *subBinding = target->bindingTable(); - for (quint32 i = 0; i < target->nBindings; ++i, ++subBinding) { - roleSet |= applyProperty(compilationUnit, subBinding, subModel, elementIndex); - } - - } else { - QVariant value; - - if (binding->isTranslationBinding()) { - value = QVariant::fromValue<const QV4::CompiledData::Binding*>(binding); - } else if (binding->evaluatesToString()) { - value = binding->valueAsString(compilationUnit.data()); - } else if (binding->type == QV4::CompiledData::Binding::Type_Number) { - value = binding->valueAsNumber(compilationUnit->constants); - } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { - value = binding->valueAsBoolean(); - } else if (binding->type == QV4::CompiledData::Binding::Type_Null) { - value = QVariant::fromValue(nullptr); - } else if (binding->type == QV4::CompiledData::Binding::Type_Script) { - QString scriptStr = binding->valueAsScriptString(compilationUnit.data()); - if (definesEmptyList(scriptStr)) { - const ListLayout::Role &role = model->getOrCreateListRole(elementName); - ListModel *emptyModel = new ListModel(role.subLayout, nullptr); - value = QVariant::fromValue(emptyModel); - } else if (binding->isFunctionExpression()) { - QQmlBinding::Identifier id = binding->value.compiledScriptIndex; - Q_ASSERT(id != QQmlBinding::Invalid); - - auto v4 = compilationUnit->engine; - QV4::Scope scope(v4); - // for now we do not provide a context object; data from the ListElement must be passed to the function - QV4::ScopedContext context(scope, QV4::QmlContext::create(v4->rootContext(), QQmlContextData::get(qmlContext(model->m_modelCache)), nullptr)); - QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(context, compilationUnit->runtimeFunctions[id])); - - QV4::ReturnedValue result = function->call(v4->globalObject, nullptr, 0); - - QJSValue v; - QJSValuePrivate::setValue(&v, v4, result); - value.setValue<QJSValue>(v); - } else { - QByteArray script = scriptStr.toUtf8(); - bool ok; - value = evaluateEnum(script, &ok); - } - } else { - Q_UNREACHABLE(); - } - - model->setOrCreateProperty(outterElementIndex, elementName, value); - roleSet = true; - } - return roleSet; -} - -void QQmlListModelParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) -{ - listElementTypeName = QString(); // unknown - - for (const QV4::CompiledData::Binding *binding : bindings) { - QString propName = compilationUnit->stringAt(binding->propertyNameIndex); - if (!propName.isEmpty()) { // isn't default property - error(binding, QQmlListModel::tr("ListModel: undefined property '%1'").arg(propName)); - return; - } - if (!verifyProperty(compilationUnit, binding)) - return; - } -} - -void QQmlListModelParser::applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) -{ - QQmlListModel *rv = static_cast<QQmlListModel *>(obj); - - rv->m_engine = qmlEngine(rv)->handle(); - rv->m_compilationUnit = compilationUnit; - - bool setRoles = false; - - for (const QV4::CompiledData::Binding *binding : bindings) { - if (binding->type != QV4::CompiledData::Binding::Type_Object) - continue; - setRoles |= applyProperty(compilationUnit, binding, rv->m_listModel, /*outter element index*/-1); - } - - if (setRoles == false) - qmlWarning(obj) << "All ListElement declarations are empty, no roles can be created unless dynamicRoles is set."; -} - -bool QQmlListModelParser::definesEmptyList(const QString &s) -{ - if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) { - for (int i=1; i<s.length()-1; i++) { - if (!s[i].isSpace()) - return false; - } - return true; - } - return false; -} - - -/*! - \qmltype ListElement - \instantiates QQmlListElement - \inqmlmodule QtQml.Models - \brief Defines a data item in a ListModel. - \ingroup qtquick-models - - List elements are defined inside ListModel definitions, and represent items in a - list that will be displayed using ListView or \l Repeater items. - - List elements are defined like other QML elements except that they contain - a collection of \e role definitions instead of properties. Using the same - syntax as property definitions, roles both define how the data is accessed - and include the data itself. - - The names used for roles must begin with a lower-case letter and should be - common to all elements in a given model. Values must be simple constants; either - strings (quoted and optionally within a call to QT_TR_NOOP), boolean values - (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter). - - Beginning with Qt 5.11 ListElement also allows assigning a function declaration to - a role. This allows the definition of ListElements with callable actions. - - \section1 Referencing Roles - - The role names are used by delegates to obtain data from list elements. - Each role name is accessible in the delegate's scope, and refers to the - corresponding role in the current element. Where a role name would be - ambiguous to use, it can be accessed via the \l{ListView::}{model} - property (e.g., \c{model.cost} instead of \c{cost}). - - \section1 Example Usage - - The following model defines a series of list elements, each of which - contain "name" and "cost" roles and their associated values. - - \snippet qml/listmodel/listelements.qml model - - The delegate obtains the name and cost for each element by simply referring - to \c name and \c cost: - - \snippet qml/listmodel/listelements.qml view - - \sa ListModel -*/ - -QT_END_NAMESPACE - -#include "moc_qqmllistmodel_p.cpp" diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h deleted file mode 100644 index 95b797c898..0000000000 --- a/src/qml/types/qqmllistmodel_p.h +++ /dev/null @@ -1,208 +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$ -** -****************************************************************************/ - -#ifndef QQMLLISTMODEL_H -#define QQMLLISTMODEL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qqml.h> -#include <private/qqmlcustomparser_p.h> - -#include <QtCore/QObject> -#include <QtCore/QStringList> -#include <QtCore/QHash> -#include <QtCore/QList> -#include <QtCore/QVariant> -#include <QtCore/qabstractitemmodel.h> - -#include <private/qv4engine_p.h> -#include <private/qpodvector_p.h> - -QT_REQUIRE_CONFIG(qml_list_model); - -QT_BEGIN_NAMESPACE - - -class QQmlListModelWorkerAgent; -class ListModel; -class ListLayout; - -namespace QV4 { -struct ModelObject; -} - -class Q_QML_PRIVATE_EXPORT QQmlListModel : public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(bool dynamicRoles READ dynamicRoles WRITE setDynamicRoles) - -public: - QQmlListModel(QObject *parent=nullptr); - ~QQmlListModel(); - - QModelIndex index(int row, int column, const QModelIndex &parent) const override; - int rowCount(const QModelIndex &parent) const override; - QVariant data(const QModelIndex &index, int role) const override; - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; - QHash<int,QByteArray> roleNames() const override; - - QVariant data(int index, int role) const; - int count() const; - - Q_INVOKABLE void clear(); - Q_INVOKABLE void remove(QQmlV4Function *args); - Q_INVOKABLE void append(QQmlV4Function *args); - Q_INVOKABLE void insert(QQmlV4Function *args); - Q_INVOKABLE QQmlV4Handle get(int index) const; - Q_INVOKABLE void set(int index, const QQmlV4Handle &); - Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); - Q_INVOKABLE void move(int from, int to, int count); - Q_INVOKABLE void sync(); - - QQmlListModelWorkerAgent *agent(); - - bool dynamicRoles() const { return m_dynamicRoles; } - void setDynamicRoles(bool enableDynamicRoles); - -Q_SIGNALS: - void countChanged(); - -private: - friend class QQmlListModelParser; - friend class QQmlListModelWorkerAgent; - friend class ModelObject; - friend struct QV4::ModelObject; - friend class ModelNodeMetaObject; - friend class ListModel; - friend class ListElement; - friend class DynamicRoleModelNode; - friend class DynamicRoleModelNodeMetaObject; - friend struct StringOrTranslation; - - // Constructs a flat list model for a worker agent - QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent); - QQmlListModel(const QQmlListModel *owner, ListModel *data, QV4::ExecutionEngine *engine, QObject *parent=nullptr); - - QV4::ExecutionEngine *engine() const; - - inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); } - - QQmlListModelWorkerAgent *m_agent; - mutable QV4::ExecutionEngine *m_engine; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit; - bool m_mainThread; - bool m_primary; - - bool m_dynamicRoles; - - ListLayout *m_layout; - ListModel *m_listModel; - - QVector<class DynamicRoleModelNode *> m_modelObjects; - QVector<QString> m_roles; - - struct ElementSync - { - DynamicRoleModelNode *src = nullptr; - DynamicRoleModelNode *target = nullptr; - int srcIndex = -1; - int targetIndex = -1; - QVector<int> changedRoles; - }; - - static bool sync(QQmlListModel *src, QQmlListModel *target); - static QQmlListModel *createWithOwner(QQmlListModel *newOwner); - - void emitItemsChanged(int index, int count, const QVector<int> &roles); - void emitItemsAboutToBeInserted(int index, int count); - void emitItemsInserted(); - - void removeElements(int index, int removeCount); -}; - -// ### FIXME -class QQmlListElement : public QObject -{ -Q_OBJECT -}; - -class QQmlListModelParser : public QQmlCustomParser -{ -public: - enum PropertyType { - Invalid, - Boolean, - Number, - String, - Script - }; - - - QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {} - - void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; - void applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; - -private: - bool verifyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding); - // returns true if a role was set - bool applyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex); - - static bool definesEmptyList(const QString &); - - QString listElementTypeName; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlListModel) -QML_DECLARE_TYPE(QQmlListElement) - -#endif // QQMLLISTMODEL_H diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h deleted file mode 100644 index 2876c71de6..0000000000 --- a/src/qml/types/qqmllistmodel_p_p.h +++ /dev/null @@ -1,428 +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$ -** -****************************************************************************/ - -#ifndef QQMLLISTMODEL_P_P_H -#define QQMLLISTMODEL_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qqmllistmodel_p.h" -#include <private/qqmlengine_p.h> -#include <private/qqmlopenmetaobject_p.h> -#include <private/qv4qobjectwrapper_p.h> -#include <qqml.h> - -QT_REQUIRE_CONFIG(qml_list_model); - -QT_BEGIN_NAMESPACE - - -class DynamicRoleModelNode; - -class DynamicRoleModelNodeMetaObject : public QQmlOpenMetaObject -{ -public: - DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object); - ~DynamicRoleModelNodeMetaObject(); - - bool m_enabled; - -protected: - void propertyWrite(int index) override; - void propertyWritten(int index) override; - -private: - DynamicRoleModelNode *m_owner; -}; - -class DynamicRoleModelNode : public QObject -{ - Q_OBJECT -public: - DynamicRoleModelNode(QQmlListModel *owner, int uid); - - static DynamicRoleModelNode *create(const QVariantMap &obj, QQmlListModel *owner); - - void updateValues(const QVariantMap &object, QVector<int> &roles); - - QVariant getValue(const QString &name) const - { - return m_meta->value(name.toUtf8()); - } - - bool setValue(const QByteArray &name, const QVariant &val) - { - return m_meta->setValue(name, val); - } - - void setNodeUpdatesEnabled(bool enable) - { - m_meta->m_enabled = enable; - } - - int getUid() const - { - return m_uid; - } - - static QVector<int> sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target); - -private: - QQmlListModel *m_owner; - int m_uid; - DynamicRoleModelNodeMetaObject *m_meta; - - friend class DynamicRoleModelNodeMetaObject; -}; - -class ModelNodeMetaObject : public QQmlOpenMetaObject -{ -public: - ModelNodeMetaObject(QObject *object, QQmlListModel *model, int elementIndex); - ~ModelNodeMetaObject(); - - QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *object) override; - - static ModelNodeMetaObject *get(QObject *obj); - - bool m_enabled; - QQmlListModel *m_model; - int m_elementIndex; - - void updateValues(); - void updateValues(const QVector<int> &roles); - - bool initialized() const { return m_initialized; } - -protected: - void propertyWritten(int index) override; - -private: - using QQmlOpenMetaObject::setValue; - - void emitDirectNotifies(const int *changedRoles, int roleCount); - - void initialize(); - bool m_initialized; -}; - -namespace QV4 { - -namespace Heap { - -struct ModelObject : public QObjectWrapper { - void init(QObject *object, QQmlListModel *model) - { - QObjectWrapper::init(object); - m_model = model; - QObjectPrivate *op = QObjectPrivate::get(object); - m_nodeModelMetaObject = static_cast<ModelNodeMetaObject *>(op->metaObject); - } - void destroy() { QObjectWrapper::destroy(); } - int elementIndex() const { return m_nodeModelMetaObject->m_elementIndex; } - QQmlListModel *m_model; - ModelNodeMetaObject *m_nodeModelMetaObject; -}; - -} - -struct ModelObject : public QObjectWrapper -{ - V4_OBJECT2(ModelObject, QObjectWrapper) - V4_NEEDS_DESTROY - - ListModel *listModel() const { return d()->m_model->m_listModel; } - -protected: - static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver); - static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); - static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); - static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object); - static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target); -}; - -} // namespace QV4 - -class ListLayout -{ -public: - ListLayout() : currentBlock(0), currentBlockOffset(0) {} - ListLayout(const ListLayout *other); - ~ListLayout(); - - class Role - { - public: - - Role() : type(Invalid), blockIndex(-1), blockOffset(-1), index(-1), subLayout(0) {} - explicit Role(const Role *other); - ~Role(); - - // This enum must be kept in sync with the roleTypeNames variable in qqmllistmodel.cpp - enum DataType - { - Invalid = -1, - - String, - Number, - Bool, - List, - QObject, - VariantMap, - DateTime, - Function, - - MaxDataType - }; - - QString name; - DataType type; - int blockIndex; - int blockOffset; - int index; - ListLayout *subLayout; - }; - - const Role *getRoleOrCreate(const QString &key, const QVariant &data); - const Role &getRoleOrCreate(QV4::String *key, Role::DataType type); - const Role &getRoleOrCreate(const QString &key, Role::DataType type); - - const Role &getExistingRole(int index) const { return *roles.at(index); } - const Role *getExistingRole(const QString &key) const; - const Role *getExistingRole(QV4::String *key) const; - - int roleCount() const { return roles.count(); } - - static void sync(ListLayout *src, ListLayout *target); - -private: - const Role &createRole(const QString &key, Role::DataType type); - - int currentBlock; - int currentBlockOffset; - QVector<Role *> roles; - QStringHash<Role *> roleHash; -}; - -struct StringOrTranslation -{ - explicit StringOrTranslation(const QString &s); - explicit StringOrTranslation(const QV4::CompiledData::Binding *binding); - ~StringOrTranslation(); - bool isSet() const { return d.flag(); } - bool isTranslation() const { return d.isT2(); } - void setString(const QString &s); - void setTranslation(const QV4::CompiledData::Binding *binding); - QString toString(const QQmlListModel *owner) const; - QString asString() const; -private: - void clear(); - QBiPointer<QStringData, const QV4::CompiledData::Binding> d; -}; - -/*! -\internal -*/ -class ListElement -{ -public: - - ListElement(); - ListElement(int existingUid); - ~ListElement(); - - static QVector<int> sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout); - - enum - { - BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelNodeMetaObject *) - }; - -private: - - void destroy(ListLayout *layout); - - int setVariantProperty(const ListLayout::Role &role, const QVariant &d); - - int setJsProperty(const ListLayout::Role &role, const QV4::Value &d, QV4::ExecutionEngine *eng); - - int setStringProperty(const ListLayout::Role &role, const QString &s); - int setDoubleProperty(const ListLayout::Role &role, double n); - int setBoolProperty(const ListLayout::Role &role, bool b); - int setListProperty(const ListLayout::Role &role, ListModel *m); - int setQObjectProperty(const ListLayout::Role &role, QObject *o); - int setVariantMapProperty(const ListLayout::Role &role, QV4::Object *o); - int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m); - int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt); - int setFunctionProperty(const ListLayout::Role &role, const QJSValue &f); - int setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b); - - void setStringPropertyFast(const ListLayout::Role &role, const QString &s); - void setDoublePropertyFast(const ListLayout::Role &role, double n); - void setBoolPropertyFast(const ListLayout::Role &role, bool b); - void setQObjectPropertyFast(const ListLayout::Role &role, QObject *o); - void setListPropertyFast(const ListLayout::Role &role, ListModel *m); - void setVariantMapFast(const ListLayout::Role &role, QV4::Object *o); - void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt); - void setFunctionPropertyFast(const ListLayout::Role &role, const QJSValue &f); - - void clearProperty(const ListLayout::Role &role); - - QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng); - ListModel *getListProperty(const ListLayout::Role &role); - StringOrTranslation *getStringProperty(const ListLayout::Role &role); - QObject *getQObjectProperty(const ListLayout::Role &role); - QPointer<QObject> *getGuardProperty(const ListLayout::Role &role); - QVariantMap *getVariantMapProperty(const ListLayout::Role &role); - QDateTime *getDateTimeProperty(const ListLayout::Role &role); - QJSValue *getFunctionProperty(const ListLayout::Role &role); - - inline char *getPropertyMemory(const ListLayout::Role &role); - - int getUid() const { return uid; } - - ModelNodeMetaObject *objectCache(); - - char data[BLOCK_SIZE]; - ListElement *next; - - int uid; - QObject *m_objectCache; - - friend class ListModel; -}; - -/*! -\internal -*/ -class ListModel -{ -public: - - ListModel(ListLayout *layout, QQmlListModel *modelCache); - ~ListModel() {} - - void destroy(); - - int setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data); - int setExistingProperty(int uid, const QString &key, const QV4::Value &data, QV4::ExecutionEngine *eng); - - QVariant getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV4::ExecutionEngine *eng); - ListModel *getListProperty(int elementIndex, const ListLayout::Role &role); - - int roleCount() const - { - return m_layout->roleCount(); - } - - const ListLayout::Role &getExistingRole(int index) const - { - return m_layout->getExistingRole(index); - } - - const ListLayout::Role *getExistingRole(QV4::String *key) const - { - return m_layout->getExistingRole(key); - } - - const ListLayout::Role &getOrCreateListRole(const QString &name) - { - return m_layout->getRoleOrCreate(name, ListLayout::Role::List); - } - - int elementCount() const - { - return elements.count(); - } - - void set(int elementIndex, QV4::Object *object, QVector<int> *roles); - void set(int elementIndex, QV4::Object *object); - - int append(QV4::Object *object); - void insert(int elementIndex, QV4::Object *object); - - Q_REQUIRED_RESULT QVector<std::function<void()>> remove(int index, int count); - - int appendElement(); - void insertElement(int index); - - void move(int from, int to, int n); - - static bool sync(ListModel *src, ListModel *target); - - QObject *getOrCreateModelObject(QQmlListModel *model, int elementIndex); - -private: - QPODVector<ListElement *, 4> elements; - ListLayout *m_layout; - - QQmlListModel *m_modelCache; - - struct ElementSync - { - ListElement *src = nullptr; - ListElement *target = nullptr; - int srcIndex = -1; - int targetIndex = -1; - QVector<int> changedRoles; - }; - - void newElement(int index); - - void updateCacheIndices(int start = 0, int end = -1); - - friend class ListElement; - friend class QQmlListModelWorkerAgent; - friend class QQmlListModelParser; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(ListModel *); - -#endif // QQUICKLISTMODEL_P_P_H diff --git a/src/qml/types/qqmllistmodelworkeragent.cpp b/src/qml/types/qqmllistmodelworkeragent.cpp deleted file mode 100644 index fe3eaa3198..0000000000 --- a/src/qml/types/qqmllistmodelworkeragent.cpp +++ /dev/null @@ -1,177 +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 "qqmllistmodelworkeragent_p.h" -#include "qqmllistmodel_p_p.h" -#include <private/qqmldata_p.h> -#include <private/qqmlengine_p.h> -#include <qqmlinfo.h> - -#include <QtCore/qcoreevent.h> -#include <QtCore/qcoreapplication.h> -#include <QtCore/qdebug.h> - - -QT_BEGIN_NAMESPACE - -QQmlListModelWorkerAgent::Sync::~Sync() -{ -} - -QQmlListModelWorkerAgent::QQmlListModelWorkerAgent(QQmlListModel *model) -: m_ref(1), m_orig(model), m_copy(new QQmlListModel(model, this)) -{ -} - -QQmlListModelWorkerAgent::~QQmlListModelWorkerAgent() -{ - mutex.lock(); - syncDone.wakeAll(); - mutex.unlock(); -} - -void QQmlListModelWorkerAgent::setEngine(QV4::ExecutionEngine *eng) -{ - m_copy->m_engine = eng; -} - -void QQmlListModelWorkerAgent::addref() -{ - m_ref.ref(); -} - -void QQmlListModelWorkerAgent::release() -{ - bool del = !m_ref.deref(); - - if (del) - deleteLater(); -} - -void QQmlListModelWorkerAgent::modelDestroyed() -{ - m_orig = nullptr; -} - -int QQmlListModelWorkerAgent::count() const -{ - return m_copy->count(); -} - -void QQmlListModelWorkerAgent::clear() -{ - m_copy->clear(); -} - -void QQmlListModelWorkerAgent::remove(QQmlV4Function *args) -{ - m_copy->remove(args); -} - -void QQmlListModelWorkerAgent::append(QQmlV4Function *args) -{ - m_copy->append(args); -} - -void QQmlListModelWorkerAgent::insert(QQmlV4Function *args) -{ - m_copy->insert(args); -} - -QQmlV4Handle QQmlListModelWorkerAgent::get(int index) const -{ - return m_copy->get(index); -} - -void QQmlListModelWorkerAgent::set(int index, const QQmlV4Handle &value) -{ - m_copy->set(index, value); -} - -void QQmlListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value) -{ - m_copy->setProperty(index, property, value); -} - -void QQmlListModelWorkerAgent::move(int from, int to, int count) -{ - m_copy->move(from, to, count); -} - -void QQmlListModelWorkerAgent::sync() -{ - Sync *s = new Sync(m_copy); - - mutex.lock(); - QCoreApplication::postEvent(this, s); - syncDone.wait(&mutex); - mutex.unlock(); -} - -bool QQmlListModelWorkerAgent::event(QEvent *e) -{ - if (e->type() == QEvent::User) { - bool cc = false; - QMutexLocker locker(&mutex); - if (m_orig) { - Sync *s = static_cast<Sync *>(e); - - cc = (m_orig->count() != s->list->count()); - - Q_ASSERT(m_orig->m_dynamicRoles == s->list->m_dynamicRoles); - if (m_orig->m_dynamicRoles) - QQmlListModel::sync(s->list, m_orig); - else - ListModel::sync(s->list->m_listModel, m_orig->m_listModel); - } - - syncDone.wakeAll(); - locker.unlock(); - - if (cc) - emit m_orig->countChanged(); - return true; - } - - return QObject::event(e); -} - -QT_END_NAMESPACE - -#include "moc_qqmllistmodelworkeragent_p.cpp" diff --git a/src/qml/types/qqmllistmodelworkeragent_p.h b/src/qml/types/qqmllistmodelworkeragent_p.h deleted file mode 100644 index ae2d4b11e0..0000000000 --- a/src/qml/types/qqmllistmodelworkeragent_p.h +++ /dev/null @@ -1,140 +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$ -** -****************************************************************************/ - -#ifndef QQUICKLISTMODELWORKERAGENT_P_H -#define QQUICKLISTMODELWORKERAGENT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qqml.h> - -#include <QEvent> -#include <QMutex> -#include <QWaitCondition> - -#include <private/qv8engine_p.h> - -QT_REQUIRE_CONFIG(qml_list_model); - -QT_BEGIN_NAMESPACE - - -class QQmlListModel; - -class QQmlListModelWorkerAgent : public QObject -{ - Q_OBJECT - Q_PROPERTY(int count READ count) - -public: - QQmlListModelWorkerAgent(QQmlListModel *); - ~QQmlListModelWorkerAgent(); - void setEngine(QV4::ExecutionEngine *eng); - - void addref(); - void release(); - - int count() const; - - Q_INVOKABLE void clear(); - Q_INVOKABLE void remove(QQmlV4Function *args); - Q_INVOKABLE void append(QQmlV4Function *args); - Q_INVOKABLE void insert(QQmlV4Function *args); - Q_INVOKABLE QQmlV4Handle get(int index) const; - Q_INVOKABLE void set(int index, const QQmlV4Handle &); - Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); - Q_INVOKABLE void move(int from, int to, int count); - Q_INVOKABLE void sync(); - - struct VariantRef - { - VariantRef() : a(nullptr) {} - VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); } - VariantRef(QQmlListModelWorkerAgent *_a) : a(_a) { if (a) a->addref(); } - ~VariantRef() { if (a) a->release(); } - - VariantRef &operator=(const VariantRef &o) { - if (o.a) o.a->addref(); - if (a) a->release(); - a = o.a; - return *this; - } - - QQmlListModelWorkerAgent *a; - }; - - void modelDestroyed(); -protected: - bool event(QEvent *) override; - -private: - friend class QQuickWorkerScriptEnginePrivate; - friend class QQmlListModel; - - struct Sync : public QEvent { - Sync(QQmlListModel *l) - : QEvent(QEvent::User) - , list(l) - {} - ~Sync(); - QQmlListModel *list; - }; - - QAtomicInt m_ref; - QQmlListModel *m_orig; - QQmlListModel *m_copy; - QMutex mutex; - QWaitCondition syncDone; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QQmlListModelWorkerAgent::VariantRef) - -#endif // QQUICKLISTMODELWORKERAGENT_P_H - diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp deleted file mode 100644 index bb72771cd9..0000000000 --- a/src/qml/types/qqmlmodelsmodule.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion. -** 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 "qqmlmodelsmodule_p.h" -#include <QtCore/qitemselectionmodel.h> -#if QT_CONFIG(qml_list_model) -#include <private/qqmllistmodel_p.h> -#endif -#if QT_CONFIG(qml_delegate_model) -#include <private/qqmldelegatemodel_p.h> -#include <private/qqmldelegatecomponent_p.h> -#endif -#include <private/qqmlobjectmodel_p.h> - -QT_BEGIN_NAMESPACE - -void QQmlModelsModule::defineModule() -{ - const char uri[] = "QtQml.Models"; - -#if QT_CONFIG(qml_list_model) - qmlRegisterType<QQmlListElement>(uri, 2, 1, "ListElement"); - qmlRegisterCustomType<QQmlListModel>(uri, 2, 1, "ListModel", new QQmlListModelParser); -#endif -#if QT_CONFIG(qml_delegate_model) - qmlRegisterType<QQmlDelegateModel>(uri, 2, 1, "DelegateModel"); - qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 1, "DelegateModelGroup"); -#endif - qmlRegisterType<QQmlObjectModel>(uri, 2, 1, "ObjectModel"); - qmlRegisterType<QQmlObjectModel,3>(uri, 2, 3, "ObjectModel"); - - qmlRegisterType<QItemSelectionModel>(uri, 2, 2, "ItemSelectionModel"); -} - -void QQmlModelsModule::defineLabsModule() -{ - const char uri[] = "Qt.labs.qmlmodels"; - -#if QT_CONFIG(qml_delegate_model) - qmlRegisterUncreatableType<QQmlAbstractDelegateComponent>(uri, 1, 0, "AbstractDelegateComponent", QQmlAbstractDelegateComponent::tr("Cannot create instance of abstract class AbstractDelegateComponent.")); - qmlRegisterType<QQmlDelegateChooser>(uri, 1, 0, "DelegateChooser"); - qmlRegisterType<QQmlDelegateChoice>(uri, 1, 0, "DelegateChoice"); -#endif -} - -QT_END_NAMESPACE diff --git a/src/qml/types/qqmlmodelsmodule_p.h b/src/qml/types/qqmlmodelsmodule_p.h deleted file mode 100644 index 939ecc1500..0000000000 --- a/src/qml/types/qqmlmodelsmodule_p.h +++ /dev/null @@ -1,67 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion. -** 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$ -** -****************************************************************************/ - -#ifndef QQMLMODELSMODULE_H -#define QQMLMODELSMODULE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtqmlglobal_p.h> - -QT_BEGIN_NAMESPACE - -class Q_QML_PRIVATE_EXPORT QQmlModelsModule -{ -public: - static void defineModule(); - static void defineLabsModule(); -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/types/qqmlobjectmodel.cpp b/src/qml/types/qqmlobjectmodel.cpp deleted file mode 100644 index 2f4d427430..0000000000 --- a/src/qml/types/qqmlobjectmodel.cpp +++ /dev/null @@ -1,431 +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 "qqmlobjectmodel_p.h" - -#include <QtCore/qcoreapplication.h> -#include <QtQml/qqmlcontext.h> -#include <QtQml/qqmlengine.h> -#include <QtQml/qqmlinfo.h> - -#include <private/qqmlchangeset_p.h> -#include <private/qqmlglobal_p.h> -#include <private/qobject_p.h> -#include <private/qpodvector_p.h> - -#include <QtCore/qhash.h> -#include <QtCore/qlist.h> - -QT_BEGIN_NAMESPACE - -QHash<QObject*, QQmlObjectModelAttached*> QQmlObjectModelAttached::attachedProperties; - - -class QQmlObjectModelPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QQmlObjectModel) -public: - class Item { - public: - Item(QObject *i) : item(i), ref(0) {} - - void addRef() { ++ref; } - bool deref() { return --ref == 0; } - - QObject *item; - int ref; - }; - - QQmlObjectModelPrivate() : QObjectPrivate(), moveId(0) {} - - static void children_append(QQmlListProperty<QObject> *prop, QObject *item) { - int index = static_cast<QQmlObjectModelPrivate *>(prop->data)->children.count(); - static_cast<QQmlObjectModelPrivate *>(prop->data)->insert(index, item); - } - - static int children_count(QQmlListProperty<QObject> *prop) { - return static_cast<QQmlObjectModelPrivate *>(prop->data)->children.count(); - } - - static QObject *children_at(QQmlListProperty<QObject> *prop, int index) { - return static_cast<QQmlObjectModelPrivate *>(prop->data)->children.at(index).item; - } - - static void children_clear(QQmlListProperty<QObject> *prop) { - static_cast<QQmlObjectModelPrivate *>(prop->data)->clear(); - } - - void insert(int index, QObject *item) { - Q_Q(QQmlObjectModel); - children.insert(index, Item(item)); - for (int i = index; i < children.count(); ++i) { - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); - attached->setIndex(i); - } - QQmlChangeSet changeSet; - changeSet.insert(index, 1); - emit q->modelUpdated(changeSet, false); - emit q->countChanged(); - emit q->childrenChanged(); - } - - void move(int from, int to, int n) { - Q_Q(QQmlObjectModel); - if (from > to) { - // Only move forwards - flip if backwards moving - int tfrom = from; - int tto = to; - from = tto; - to = tto+n; - n = tfrom-tto; - } - - QPODVector<QQmlObjectModelPrivate::Item, 4> store; - for (int i = 0; i < to - from; ++i) - store.append(children[from + n + i]); - for (int i = 0; i < n; ++i) - store.append(children[from + i]); - - for (int i = 0; i < store.count(); ++i) { - children[from + i] = store[i]; - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(from + i).item); - attached->setIndex(from + i); - } - - QQmlChangeSet changeSet; - changeSet.move(from, to, n, ++moveId); - emit q->modelUpdated(changeSet, false); - emit q->childrenChanged(); - } - - void remove(int index, int n) { - Q_Q(QQmlObjectModel); - for (int i = index; i < index + n; ++i) { - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); - attached->setIndex(-1); - } - children.erase(children.begin() + index, children.begin() + index + n); - for (int i = index; i < children.count(); ++i) { - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); - attached->setIndex(i); - } - QQmlChangeSet changeSet; - changeSet.remove(index, n); - emit q->modelUpdated(changeSet, false); - emit q->countChanged(); - emit q->childrenChanged(); - } - - void clear() { - Q_Q(QQmlObjectModel); - for (const Item &child : qAsConst(children)) - emit q->destroyingItem(child.item); - remove(0, children.count()); - } - - int indexOf(QObject *item) const { - for (int i = 0; i < children.count(); ++i) - if (children.at(i).item == item) - return i; - return -1; - } - - uint moveId; - QList<Item> children; -}; - - -/*! - \qmltype ObjectModel - \instantiates QQmlObjectModel - \inqmlmodule QtQml.Models - \ingroup qtquick-models - \brief Defines a set of items to be used as a model. - - An ObjectModel contains the visual items to be used in a view. - When an ObjectModel is used in a view, the view does not require - a delegate since the ObjectModel already contains the visual - delegate (items). - - An item can determine its index within the - model via the \l{ObjectModel::index}{index} attached property. - - The example below places three colored rectangles in a ListView. - \code - import QtQuick 2.0 - import QtQml.Models 2.1 - - Rectangle { - ObjectModel { - id: itemModel - Rectangle { height: 30; width: 80; color: "red" } - Rectangle { height: 30; width: 80; color: "green" } - Rectangle { height: 30; width: 80; color: "blue" } - } - - ListView { - anchors.fill: parent - model: itemModel - } - } - \endcode - - \image objectmodel.png - - \sa {Qt Quick Examples - Views} -*/ - -QQmlObjectModel::QQmlObjectModel(QObject *parent) - : QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent) -{ -} - -/*! - \qmlattachedproperty int QtQml.Models::ObjectModel::index - This attached property holds the index of this delegate's item within the model. - - It is attached to each instance of the delegate. -*/ - -QQmlListProperty<QObject> QQmlObjectModel::children() -{ - Q_D(QQmlObjectModel); - return QQmlListProperty<QObject>(this, - d, - d->children_append, - d->children_count, - d->children_at, - d->children_clear); -} - -/*! - \qmlproperty int QtQml.Models::ObjectModel::count - - The number of items in the model. This property is readonly. -*/ -int QQmlObjectModel::count() const -{ - Q_D(const QQmlObjectModel); - return d->children.count(); -} - -bool QQmlObjectModel::isValid() const -{ - return true; -} - -QObject *QQmlObjectModel::object(int index, QQmlIncubator::IncubationMode) -{ - Q_D(QQmlObjectModel); - QQmlObjectModelPrivate::Item &item = d->children[index]; - item.addRef(); - if (item.ref == 1) { - emit initItem(index, item.item); - emit createdItem(index, item.item); - } - return item.item; -} - -QQmlInstanceModel::ReleaseFlags QQmlObjectModel::release(QObject *item) -{ - Q_D(QQmlObjectModel); - int idx = d->indexOf(item); - if (idx >= 0) { - if (!d->children[idx].deref()) - return QQmlInstanceModel::Referenced; - } - return nullptr; -} - -QString QQmlObjectModel::stringValue(int index, const QString &name) -{ - Q_D(QQmlObjectModel); - if (index < 0 || index >= d->children.count()) - return QString(); - return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString(); -} - -QQmlIncubator::Status QQmlObjectModel::incubationStatus(int) -{ - return QQmlIncubator::Ready; -} - -int QQmlObjectModel::indexOf(QObject *item, QObject *) const -{ - Q_D(const QQmlObjectModel); - return d->indexOf(item); -} - -QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj) -{ - return QQmlObjectModelAttached::properties(obj); -} - -/*! - \qmlmethod object QtQml.Models::ObjectModel::get(int index) - \since 5.6 - - Returns the item at \a index in the model. This allows the item - to be accessed or modified from JavaScript: - - \code - Component.onCompleted: { - objectModel.append(objectComponent.createObject()) - console.log(objectModel.get(0).objectName); - objectModel.get(0).objectName = "first"; - } - \endcode - - The \a index must be an element in the list. - - \sa append() -*/ -QObject *QQmlObjectModel::get(int index) const -{ - Q_D(const QQmlObjectModel); - if (index < 0 || index >= d->children.count()) - return nullptr; - return d->children.at(index).item; -} - -/*! - \qmlmethod QtQml.Models::ObjectModel::append(object item) - \since 5.6 - - Appends a new item to the end of the model. - - \code - objectModel.append(objectComponent.createObject()) - \endcode - - \sa insert(), remove() -*/ -void QQmlObjectModel::append(QObject *object) -{ - Q_D(QQmlObjectModel); - d->insert(count(), object); -} - -/*! - \qmlmethod QtQml.Models::ObjectModel::insert(int index, object item) - \since 5.6 - - Inserts a new item to the model at position \a index. - - \code - objectModel.insert(2, objectComponent.createObject()) - \endcode - - The \a index must be to an existing item in the list, or one past - the end of the list (equivalent to append). - - \sa append(), remove() -*/ -void QQmlObjectModel::insert(int index, QObject *object) -{ - Q_D(QQmlObjectModel); - if (index < 0 || index > count()) { - qmlWarning(this) << tr("insert: index %1 out of range").arg(index); - return; - } - d->insert(index, object); -} - -/*! - \qmlmethod QtQml.Models::ObjectModel::move(int from, int to, int n = 1) - \since 5.6 - - Moves \a n items \a from one position \a to another. - - The from and to ranges must exist; for example, to move the first 3 items - to the end of the model: - - \code - objectModel.move(0, objectModel.count - 3, 3) - \endcode - - \sa append() -*/ -void QQmlObjectModel::move(int from, int to, int n) -{ - Q_D(QQmlObjectModel); - if (n <= 0 || from == to) - return; - if (from < 0 || to < 0 || from + n > count() || to + n > count()) { - qmlWarning(this) << tr("move: out of range"); - return; - } - d->move(from, to, n); -} - -/*! - \qmlmethod QtQml.Models::ObjectModel::remove(int index, int n = 1) - \since 5.6 - - Removes the items at \a index from the model. - - \sa clear() -*/ -void QQmlObjectModel::remove(int index, int n) -{ - Q_D(QQmlObjectModel); - if (index < 0 || n <= 0 || index + n > count()) { - qmlWarning(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+n).arg(count()); - return; - } - d->remove(index, n); -} - -/*! - \qmlmethod QtQml.Models::ObjectModel::clear() - \since 5.6 - - Clears all items from the model. - - \sa append(), remove() -*/ -void QQmlObjectModel::clear() -{ - Q_D(QQmlObjectModel); - d->clear(); -} - -QT_END_NAMESPACE - -#include "moc_qqmlobjectmodel_p.cpp" diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qml/types/qqmlobjectmodel_p.h deleted file mode 100644 index 4ac4f1c65b..0000000000 --- a/src/qml/types/qqmlobjectmodel_p.h +++ /dev/null @@ -1,193 +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$ -** -****************************************************************************/ - -#ifndef QQMLINSTANCEMODEL_P_H -#define QQMLINSTANCEMODEL_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtqmlglobal_p.h> -#include <private/qqmlincubator_p.h> -#include <QtQml/qqml.h> -#include <QtCore/qobject.h> - -QT_BEGIN_NAMESPACE - -class QObject; -class QQmlChangeSet; -class QAbstractItemModel; - -class Q_QML_PRIVATE_EXPORT QQmlInstanceModel : public QObject -{ - Q_OBJECT - - Q_PROPERTY(int count READ count NOTIFY countChanged) - -public: - virtual ~QQmlInstanceModel() {} - - enum ReleaseFlag { Referenced = 0x01, Destroyed = 0x02 }; - Q_DECLARE_FLAGS(ReleaseFlags, ReleaseFlag) - - virtual int count() const = 0; - virtual bool isValid() const = 0; - virtual QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) = 0; - virtual ReleaseFlags release(QObject *object) = 0; - virtual void cancel(int) {} - virtual QString stringValue(int, const QString &) = 0; - virtual void setWatchedRoles(const QList<QByteArray> &roles) = 0; - virtual QQmlIncubator::Status incubationStatus(int index) = 0; - - virtual int indexOf(QObject *object, QObject *objectContext) const = 0; - virtual const QAbstractItemModel *abstractItemModel() const { return nullptr; } - -Q_SIGNALS: - void countChanged(); - void modelUpdated(const QQmlChangeSet &changeSet, bool reset); - void createdItem(int index, QObject *object); - void initItem(int index, QObject *object); - void destroyingItem(QObject *object); - -protected: - QQmlInstanceModel(QObjectPrivate &dd, QObject *parent = nullptr) - : QObject(dd, parent) {} - -private: - Q_DISABLE_COPY(QQmlInstanceModel) -}; - -class QQmlObjectModelAttached; -class QQmlObjectModelPrivate; -class Q_QML_PRIVATE_EXPORT QQmlObjectModel : public QQmlInstanceModel -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlObjectModel) - - Q_PROPERTY(QQmlListProperty<QObject> children READ children NOTIFY childrenChanged DESIGNABLE false) - Q_CLASSINFO("DefaultProperty", "children") - -public: - QQmlObjectModel(QObject *parent=nullptr); - ~QQmlObjectModel() {} - - int count() const override; - bool isValid() const override; - QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; - ReleaseFlags release(QObject *object) override; - QString stringValue(int index, const QString &role) override; - void setWatchedRoles(const QList<QByteArray> &) override {} - QQmlIncubator::Status incubationStatus(int index) override; - - int indexOf(QObject *object, QObject *objectContext) const override; - - QQmlListProperty<QObject> children(); - - static QQmlObjectModelAttached *qmlAttachedProperties(QObject *obj); - - Q_REVISION(3) Q_INVOKABLE QObject *get(int index) const; - Q_REVISION(3) Q_INVOKABLE void append(QObject *object); - Q_REVISION(3) Q_INVOKABLE void insert(int index, QObject *object); - Q_REVISION(3) Q_INVOKABLE void move(int from, int to, int n = 1); - Q_REVISION(3) Q_INVOKABLE void remove(int index, int n = 1); - -public Q_SLOTS: - Q_REVISION(3) void clear(); - -Q_SIGNALS: - void childrenChanged(); - -private: - Q_DISABLE_COPY(QQmlObjectModel) -}; - -class QQmlObjectModelAttached : public QObject -{ - Q_OBJECT - -public: - QQmlObjectModelAttached(QObject *parent) - : QObject(parent), m_index(-1) {} - ~QQmlObjectModelAttached() { - attachedProperties.remove(parent()); - } - - Q_PROPERTY(int index READ index NOTIFY indexChanged) - int index() const { return m_index; } - void setIndex(int idx) { - if (m_index != idx) { - m_index = idx; - Q_EMIT indexChanged(); - } - } - - static QQmlObjectModelAttached *properties(QObject *obj) { - QQmlObjectModelAttached *rv = attachedProperties.value(obj); - if (!rv) { - rv = new QQmlObjectModelAttached(obj); - attachedProperties.insert(obj, rv); - } - return rv; - } - -Q_SIGNALS: - void indexChanged(); - -public: - int m_index; - - static QHash<QObject*, QQmlObjectModelAttached*> attachedProperties; -}; - - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlInstanceModel) -QML_DECLARE_TYPE(QQmlObjectModel) -QML_DECLARE_TYPEINFO(QQmlObjectModel, QML_HAS_ATTACHED_PROPERTIES) - -#endif // QQMLINSTANCEMODEL_P_H diff --git a/src/qml/types/qqmltableinstancemodel.cpp b/src/qml/types/qqmltableinstancemodel.cpp deleted file mode 100644 index 2170e2daec..0000000000 --- a/src/qml/types/qqmltableinstancemodel.cpp +++ /dev/null @@ -1,547 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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 "qqmltableinstancemodel_p.h" -#include "qqmldelegatecomponent_p.h" - -#include <QtCore/QTimer> - -#include <QtQml/private/qqmlincubator_p.h> -#include <QtQml/private/qqmlchangeset_p.h> -#include <QtQml/private/qqmlcomponent_p.h> - -QT_BEGIN_NAMESPACE - -const char* kModelItemTag = "_tableinstancemodel_modelItem"; - -bool QQmlTableInstanceModel::isDoneIncubating(QQmlDelegateModelItem *modelItem) -{ - if (!modelItem->incubationTask) - return true; - - const auto status = modelItem->incubationTask->status(); - return (status == QQmlIncubator::Ready) || (status == QQmlIncubator::Error); -} - -void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelItem) -{ - Q_ASSERT(modelItem); - - delete modelItem->object; - modelItem->object = nullptr; - - if (modelItem->contextData) { - modelItem->contextData->invalidate(); - Q_ASSERT(modelItem->contextData->refCount == 1); - modelItem->contextData = nullptr; - } - - modelItem->deleteLater(); -} - -QQmlTableInstanceModel::QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent) - : QQmlInstanceModel(*(new QObjectPrivate()), parent) - , m_qmlContext(qmlContext) - , m_metaType(new QQmlDelegateModelItemMetaType(m_qmlContext->engine()->handle(), nullptr, QStringList())) -{ -} - -void QQmlTableInstanceModel::useImportVersion(int minorVersion) -{ - m_adaptorModel.useImportVersion(minorVersion); -} - -QQmlTableInstanceModel::~QQmlTableInstanceModel() -{ - for (const auto modelItem : m_modelItems) { - // No item in m_modelItems should be referenced at this point. The view - // should release all its items before it deletes this model. Only model items - // that are still being incubated should be left for us to delete. - Q_ASSERT(modelItem->objectRef == 0); - Q_ASSERT(modelItem->incubationTask); - // Check that we are not being deleted while we're - // in the process of e.g emitting a created signal. - Q_ASSERT(modelItem->scriptRef == 0); - - if (modelItem->object) { - delete modelItem->object; - modelItem->object = nullptr; - modelItem->contextData->invalidate(); - modelItem->contextData = nullptr; - } - } - - deleteAllFinishedIncubationTasks(); - qDeleteAll(m_modelItems); - drainReusableItemsPool(0); -} - -QQmlComponent *QQmlTableInstanceModel::resolveDelegate(int index) -{ - if (m_delegateChooser) { - const int row = m_adaptorModel.rowAt(index); - const int column = m_adaptorModel.columnAt(index); - QQmlComponent *delegate = nullptr; - QQmlAbstractDelegateComponent *chooser = m_delegateChooser; - do { - delegate = chooser->delegate(&m_adaptorModel, row, column); - chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate); - } while (chooser); - return delegate; - } - - return m_delegate; -} - -QQmlDelegateModelItem *QQmlTableInstanceModel::resolveModelItem(int index) -{ - // Check if an item for the given index is already loaded and ready - QQmlDelegateModelItem *modelItem = m_modelItems.value(index, nullptr); - if (modelItem) - return modelItem; - - QQmlComponent *delegate = resolveDelegate(index); - if (!delegate) - return nullptr; - - // Check if the pool contains an item that can be reused - modelItem = takeFromReusableItemsPool(delegate); - if (modelItem) { - reuseItem(modelItem, index); - m_modelItems.insert(index, modelItem); - return modelItem; - } - - // Create a new item from scratch - modelItem = m_adaptorModel.createItem(m_metaType, index); - if (modelItem) { - modelItem->delegate = delegate; - m_modelItems.insert(index, modelItem); - return modelItem; - } - - qWarning() << Q_FUNC_INFO << "failed creating a model item for index: " << index; - return nullptr; -} - -QObject *QQmlTableInstanceModel::object(int index, QQmlIncubator::IncubationMode incubationMode) -{ - Q_ASSERT(m_delegate); - Q_ASSERT(index >= 0 && index < m_adaptorModel.count()); - Q_ASSERT(m_qmlContext && m_qmlContext->isValid()); - - QQmlDelegateModelItem *modelItem = resolveModelItem(index); - if (!modelItem) - return nullptr; - - if (modelItem->object) { - // The model item has already been incubated. So - // just bump the ref-count and return it. - modelItem->referenceObject(); - return modelItem->object; - } - - // The object is not ready, and needs to be incubated - incubateModelItem(modelItem, incubationMode); - if (!isDoneIncubating(modelItem)) - return nullptr; - - // Incubation is done, so the task should be removed - Q_ASSERT(!modelItem->incubationTask); - - if (!modelItem->object) { - // The object was incubated synchronously (otherwise we would return above). But since - // we have no object, the incubation must have failed. And when we have no object, there - // should be no object references either. And there should also not be any internal script - // refs at this point. So we delete the model item. - Q_ASSERT(!modelItem->isObjectReferenced()); - Q_ASSERT(!modelItem->isReferenced()); - m_modelItems.remove(modelItem->index); - delete modelItem; - return nullptr; - } - - // Incubation was completed sync and successful - modelItem->referenceObject(); - return modelItem->object; -} - -QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object, ReusableFlag reusable) -{ - Q_ASSERT(object); - auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag)); - Q_ASSERT(modelItem); - - if (!modelItem->releaseObject()) - return QQmlDelegateModel::Referenced; - - if (modelItem->isReferenced()) { - // We still have an internal reference to this object, which means that we are told to release an - // object while the createdItem signal for it is still on the stack. This can happen when objects - // are e.g delivered async, and the user flicks back and forth quicker than the loading can catch - // up with. The view might then find that the object is no longer visible and should be released. - // We detect this case in incubatorStatusChanged(), and delete it there instead. But from the callers - // point of view, it should consider it destroyed. - return QQmlDelegateModel::Destroyed; - } - - // The item is not referenced by anyone - m_modelItems.remove(modelItem->index); - - if (reusable == Reusable) { - insertIntoReusableItemsPool(modelItem); - return QQmlInstanceModel::Referenced; - } - - // The item is not reused or referenced by anyone, so just delete it - modelItem->destroyObject(); - emit destroyingItem(object); - - delete modelItem; - return QQmlInstanceModel::Destroyed; -} - -void QQmlTableInstanceModel::cancel(int index) -{ - auto modelItem = m_modelItems.value(index); - Q_ASSERT(modelItem); - - // Since the view expects the item to be incubating, there should be - // an incubation task. And since the incubation is not done, no-one - // should yet have received, and therfore hold a reference to, the object. - Q_ASSERT(modelItem->incubationTask); - Q_ASSERT(!modelItem->isObjectReferenced()); - - m_modelItems.remove(index); - - if (modelItem->object) - delete modelItem->object; - - // modelItem->incubationTask will be deleted from the modelItems destructor - delete modelItem; -} - -void QQmlTableInstanceModel::insertIntoReusableItemsPool(QQmlDelegateModelItem *modelItem) -{ - // Currently, the only way for a view to reuse items is to call QQmlTableInstanceModel::release() - // with the second argument explicitly set to QQmlTableInstanceModel::Reusable. If the released - // item is no longer referenced, it will be added to the pool. Reusing of items can be specified - // per item, in case certain items cannot be recycled. - // A QQmlDelegateModelItem knows which delegate its object was created from. So when we are - // about to create a new item, we first check if the pool contains an item based on the same - // delegate from before. If so, we take it out of the pool (instead of creating a new item), and - // update all its context-, and attached properties. - // When a view is recycling items, it should call QQmlTableInstanceModel::drainReusableItemsPool() - // regularly. As there is currently no logic to 'hibernate' items in the pool, they are only - // meant to rest there for a short while, ideally only from the time e.g a row is unloaded - // on one side of the view, and until a new row is loaded on the opposite side. In-between - // this time, the application will see the item as fully functional and 'alive' (just not - // visible on screen). Since this time is supposed to be short, we don't take any action to - // notify the application about it, since we don't want to trigger any bindings that can - // disturb performance. - // A recommended time for calling drainReusableItemsPool() is each time a view has finished - // loading e.g a new row or column. If there are more items in the pool after that, it means - // that the view most likely doesn't need them anytime soon. Those items should be destroyed to - // not consume resources. - // Depending on if a view is a list or a table, it can sometimes be performant to keep - // items in the pool for a bit longer than one "row out/row in" cycle. E.g for a table, if the - // number of visible rows in a view is much larger than the number of visible columns. - // In that case, if you flick out a row, and then flick in a column, you would throw away a lot - // of items in the pool if completely draining it. The reason is that unloading a row places more - // items in the pool than what ends up being recycled when loading a new column. And then, when you - // next flick in a new row, you would need to load all those drained items again from scratch. For - // that reason, you can specify a maxPoolTime to the drainReusableItemsPool() that allows you to keep - // items in the pool for a bit longer, effectively keeping more items in circulation. - // A recommended maxPoolTime would be equal to the number of dimenstions in the view, which - // means 1 for a list view and 2 for a table view. If you specify 0, all items will be drained. - Q_ASSERT(!modelItem->incubationTask); - Q_ASSERT(!modelItem->isObjectReferenced()); - Q_ASSERT(!modelItem->isReferenced()); - Q_ASSERT(modelItem->object); - - modelItem->poolTime = 0; - m_reusableItemsPool.append(modelItem); - emit itemPooled(modelItem->index, modelItem->object); -} - -QQmlDelegateModelItem *QQmlTableInstanceModel::takeFromReusableItemsPool(const QQmlComponent *delegate) -{ - // Find the oldest item in the pool that was made from the same delegate as - // the given argument, remove it from the pool, and return it. - if (m_reusableItemsPool.isEmpty()) - return nullptr; - - for (auto it = m_reusableItemsPool.begin(); it != m_reusableItemsPool.end(); ++it) { - if ((*it)->delegate != delegate) - continue; - auto modelItem = *it; - m_reusableItemsPool.erase(it); - return modelItem; - } - - return nullptr; -} - -void QQmlTableInstanceModel::drainReusableItemsPool(int maxPoolTime) -{ - // Rather than releasing all pooled items upon a call to this function, each - // item has a poolTime. The poolTime specifies for how many loading cycles an item - // has been resting in the pool. And for each invocation of this function, poolTime - // will increase. If poolTime is equal to, or exceeds, maxPoolTime, it will be removed - // from the pool and released. This way, the view can tweak a bit for how long - // items should stay in "circulation", even if they are not recycled right away. - for (auto it = m_reusableItemsPool.begin(); it != m_reusableItemsPool.end();) { - auto modelItem = *it; - modelItem->poolTime++; - if (modelItem->poolTime <= maxPoolTime) { - ++it; - } else { - it = m_reusableItemsPool.erase(it); - release(modelItem->object, NotReusable); - } - } -} - -void QQmlTableInstanceModel::reuseItem(QQmlDelegateModelItem *item, int newModelIndex) -{ - // Update the context properties index, row and column on - // the delegate item, and inform the application about it. - const int newRow = m_adaptorModel.rowAt(newModelIndex); - const int newColumn = m_adaptorModel.columnAt(newModelIndex); - item->setModelIndex(newModelIndex, newRow, newColumn); - - // Notify the application that all 'dynamic'/role-based context data has - // changed as well (their getter function will use the updated index). - auto const itemAsList = QList<QQmlDelegateModelItem *>() << item; - auto const updateAllRoles = QVector<int>(); - m_adaptorModel.notify(itemAsList, newModelIndex, 1, updateAllRoles); - - // Inform the view that the item is recycled. This will typically result - // in the view updating its own attached delegate item properties. - emit itemReused(newModelIndex, item->object); -} - -void QQmlTableInstanceModel::incubateModelItem(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode) -{ - // Guard the model item temporarily so that it's not deleted from - // incubatorStatusChanged(), in case the incubation is done synchronously. - modelItem->scriptRef++; - - if (modelItem->incubationTask) { - // We're already incubating the model item from a previous request. If the previous call requested - // the item async, but the current request needs it sync, we need to force-complete the incubation. - const bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested); - if (sync && modelItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) - modelItem->incubationTask->forceCompletion(); - } else { - modelItem->incubationTask = new QQmlTableInstanceModelIncubationTask(this, modelItem, incubationMode); - - QQmlContextData *ctxt = new QQmlContextData; - QQmlContext *creationContext = modelItem->delegate->creationContext(); - ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data())); - ctxt->contextObject = modelItem; - modelItem->contextData = ctxt; - - QQmlComponentPrivate::get(modelItem->delegate)->incubateObject( - modelItem->incubationTask, - modelItem->delegate, - m_qmlContext->engine(), - ctxt, - QQmlContextData::get(m_qmlContext)); - } - - // Remove the temporary guard - modelItem->scriptRef--; -} - -void QQmlTableInstanceModel::incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *incubationTask, QQmlIncubator::Status status) -{ - QQmlDelegateModelItem *modelItem = incubationTask->modelItemToIncubate; - Q_ASSERT(modelItem->incubationTask); - - modelItem->incubationTask = nullptr; - incubationTask->modelItemToIncubate = nullptr; - - if (status == QQmlIncubator::Ready) { - // Tag the incubated object with the model item for easy retrieval upon release etc. - modelItem->object->setProperty(kModelItemTag, QVariant::fromValue(modelItem)); - - // Emit that the item has been created. What normally happens next is that the view - // upon receiving the signal asks for the model item once more. And since the item is - // now in the map, it will be returned directly. - Q_ASSERT(modelItem->object); - modelItem->scriptRef++; - emit createdItem(modelItem->index, modelItem->object); - modelItem->scriptRef--; - } else if (status == QQmlIncubator::Error) { - qWarning() << "Error incubating delegate:" << incubationTask->errors(); - } - - if (!modelItem->isReferenced() && !modelItem->isObjectReferenced()) { - // We have no internal reference to the model item, and the view has no - // reference to the incubated object. So just delete the model item. - // Note that being here means that the object was incubated _async_ - // (otherwise modelItem->isReferenced() would be true). - m_modelItems.remove(modelItem->index); - - if (modelItem->object) { - modelItem->scriptRef++; - emit destroyingItem(modelItem->object); - modelItem->scriptRef--; - Q_ASSERT(!modelItem->isReferenced()); - } - - deleteModelItemLater(modelItem); - } - - deleteIncubationTaskLater(incubationTask); -} - -QQmlIncubator::Status QQmlTableInstanceModel::incubationStatus(int index) { - const auto modelItem = m_modelItems.value(index, nullptr); - if (!modelItem) - return QQmlIncubator::Null; - - if (modelItem->incubationTask) - return modelItem->incubationTask->status(); - - // Since we clear the incubation task when we're done - // incubating, it means that the status is Ready. - return QQmlIncubator::Ready; -} - -void QQmlTableInstanceModel::deleteIncubationTaskLater(QQmlIncubator *incubationTask) -{ - // We often need to post-delete incubation tasks, since we cannot - // delete them while we're in the middle of an incubation change callback. - Q_ASSERT(!m_finishedIncubationTasks.contains(incubationTask)); - m_finishedIncubationTasks.append(incubationTask); - if (m_finishedIncubationTasks.count() == 1) - QTimer::singleShot(1, this, &QQmlTableInstanceModel::deleteAllFinishedIncubationTasks); -} - -void QQmlTableInstanceModel::deleteAllFinishedIncubationTasks() -{ - qDeleteAll(m_finishedIncubationTasks); - m_finishedIncubationTasks.clear(); -} - -QVariant QQmlTableInstanceModel::model() const -{ - return m_adaptorModel.model(); -} - -void QQmlTableInstanceModel::setModel(const QVariant &model) -{ - // Pooled items are still accessible/alive for the application, and - // needs to stay in sync with the model. So we need to drain the pool - // completely when the model changes. - drainReusableItemsPool(0); - if (auto const aim = abstractItemModel()) - disconnect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback); - m_adaptorModel.setModel(model, this, m_qmlContext->engine()); - if (auto const aim = abstractItemModel()) - connect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback); -} - -void QQmlTableInstanceModel::dataChangedCallback(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles) -{ - // This function is called when model data has changed. In that case, we tell the adaptor model - // to go through all the items we have created, find the ones that are affected, and notify that - // their model data has changed. This will in turn update QML bindings inside the delegate items. - int numberOfRowsChanged = end.row() - begin.row() + 1; - int numberOfColumnsChanged = end.column() - begin.column() + 1; - - for (int column = 0; column < numberOfColumnsChanged; ++column) { - const int columnIndex = begin.column() + column; - const int rowIndex = begin.row() + (columnIndex * rows()); - m_adaptorModel.notify(m_modelItems.values(), rowIndex, numberOfRowsChanged, roles); - } -} - -QQmlComponent *QQmlTableInstanceModel::delegate() const -{ - return m_delegate; -} - -void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate) -{ - if (m_delegate == delegate) - return; - - m_delegateChooser = nullptr; - if (delegate) { - QQmlAbstractDelegateComponent *adc = - qobject_cast<QQmlAbstractDelegateComponent *>(delegate); - if (adc) - m_delegateChooser = adc; - } - - m_delegate = delegate; -} - -const QAbstractItemModel *QQmlTableInstanceModel::abstractItemModel() const -{ - return m_adaptorModel.adaptsAim() ? m_adaptorModel.aim() : nullptr; -} - -// -------------------------------------------------------- - -void QQmlTableInstanceModelIncubationTask::setInitialState(QObject *object) -{ - modelItemToIncubate->object = object; - emit tableInstanceModel->initItem(modelItemToIncubate->index, object); -} - -void QQmlTableInstanceModelIncubationTask::statusChanged(QQmlIncubator::Status status) -{ - if (!QQmlTableInstanceModel::isDoneIncubating(modelItemToIncubate)) - return; - - // We require the view to cancel any ongoing load - // requests before the tableInstanceModel is destructed. - Q_ASSERT(tableInstanceModel); - - tableInstanceModel->incubatorStatusChanged(this, status); -} - -#include "moc_qqmltableinstancemodel_p.cpp" - -QT_END_NAMESPACE - diff --git a/src/qml/types/qqmltableinstancemodel_p.h b/src/qml/types/qqmltableinstancemodel_p.h deleted file mode 100644 index 3dd5c4e4ce..0000000000 --- a/src/qml/types/qqmltableinstancemodel_p.h +++ /dev/null @@ -1,162 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 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$ -** -****************************************************************************/ - -#ifndef QQMLTABLEINSTANCEMODEL_P_H -#define QQMLTABLEINSTANCEMODEL_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtQml/private/qqmldelegatemodel_p.h> -#include <QtQml/private/qqmldelegatemodel_p_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlTableInstanceModel; -class QQmlAbstractDelegateComponent; - -class QQmlTableInstanceModelIncubationTask : public QQDMIncubationTask -{ -public: - QQmlTableInstanceModelIncubationTask( - QQmlTableInstanceModel *tableInstanceModel - , QQmlDelegateModelItem* modelItemToIncubate - , IncubationMode mode) - : QQDMIncubationTask(nullptr, mode) - , modelItemToIncubate(modelItemToIncubate) - , tableInstanceModel(tableInstanceModel) { - clear(); - } - - void statusChanged(Status status) override; - void setInitialState(QObject *object) override; - - QQmlDelegateModelItem *modelItemToIncubate = nullptr; - QQmlTableInstanceModel *tableInstanceModel = nullptr; -}; - -class Q_QML_PRIVATE_EXPORT QQmlTableInstanceModel : public QQmlInstanceModel -{ - Q_OBJECT - -public: - - enum ReusableFlag { - NotReusable, - Reusable - }; - - QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent = nullptr); - ~QQmlTableInstanceModel() override; - - void useImportVersion(int minorVersion); - - int count() const override { return m_adaptorModel.count(); } - int rows() const { return m_adaptorModel.rowCount(); } - int columns() const { return m_adaptorModel.columnCount(); } - - bool isValid() const override { return true; } - - QVariant model() const; - void setModel(const QVariant &model); - - QQmlComponent *delegate() const; - void setDelegate(QQmlComponent *); - - const QAbstractItemModel *abstractItemModel() const override; - - QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; - ReleaseFlags release(QObject *object) override { return release(object, NotReusable); } - ReleaseFlags release(QObject *object, ReusableFlag reusable); - void cancel(int) override; - - void insertIntoReusableItemsPool(QQmlDelegateModelItem *modelItem); - QQmlDelegateModelItem *takeFromReusableItemsPool(const QQmlComponent *delegate); - void drainReusableItemsPool(int maxPoolTime); - int poolSize() { return m_reusableItemsPool.size(); } - void reuseItem(QQmlDelegateModelItem *item, int newModelIndex); - - QQmlIncubator::Status incubationStatus(int index) override; - - QString stringValue(int, const QString &) override { Q_UNREACHABLE(); return QString(); } - void setWatchedRoles(const QList<QByteArray> &) override { Q_UNREACHABLE(); } - int indexOf(QObject *, QObject *) const override { Q_UNREACHABLE(); return 0; } - -Q_SIGNALS: - void itemPooled(int index, QObject *object); - void itemReused(int index, QObject *object); - -private: - QQmlComponent *resolveDelegate(int index); - - QQmlAdaptorModel m_adaptorModel; - QQmlAbstractDelegateComponent *m_delegateChooser = nullptr; - QQmlComponent *m_delegate = nullptr; - QPointer<QQmlContext> m_qmlContext; - QQmlDelegateModelItemMetaType *m_metaType; - - QHash<int, QQmlDelegateModelItem *> m_modelItems; - QList<QQmlDelegateModelItem *> m_reusableItemsPool; - QList<QQmlIncubator *> m_finishedIncubationTasks; - - void incubateModelItem(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode); - void incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *dmIncubationTask, QQmlIncubator::Status status); - void deleteIncubationTaskLater(QQmlIncubator *incubationTask); - void deleteAllFinishedIncubationTasks(); - QQmlDelegateModelItem *resolveModelItem(int index); - - void dataChangedCallback(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles); - - static bool isDoneIncubating(QQmlDelegateModelItem *modelItem); - static void deleteModelItemLater(QQmlDelegateModelItem *modelItem); - - friend class QQmlTableInstanceModelIncubationTask; -}; - -QT_END_NAMESPACE - -#endif // QQMLTABLEINSTANCEMODEL_P_H diff --git a/src/qml/types/qquickpackage.cpp b/src/qml/types/qquickpackage.cpp deleted file mode 100644 index 03539d8737..0000000000 --- a/src/qml/types/qquickpackage.cpp +++ /dev/null @@ -1,198 +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 "qquickpackage_p.h" - -#include <private/qobject_p.h> -#include <private/qqmlguard_p.h> - -QT_BEGIN_NAMESPACE - -/*! - \qmltype Package - \instantiates QQuickPackage - \inqmlmodule QtQuick - \ingroup qtquick-views - \brief Specifies a collection of named items. - - The Package type is used in conjunction with - DelegateModel to enable delegates with a shared context - to be provided to multiple views. - - Any item within a Package may be assigned a name via the - \l{Package::name}{Package.name} attached property. - - The example below creates a Package containing two named items; - \e list and \e grid. The third item in the package (the \l Rectangle) is parented to whichever - delegate it should appear in. This allows an item to move - between views. - - \snippet package/Delegate.qml 0 - - These named items are used as the delegates by the two views who - reference the special \l{DelegateModel::parts} property to select - a model which provides the chosen delegate. - - \snippet package/view.qml 0 - - \sa {Qt Quick Examples - Views}, {Qt Quick Demo - Photo Viewer}, {Qt QML} -*/ - -/*! - \qmlattachedproperty string QtQuick::Package::name - This attached property holds the name of an item within a Package. -*/ - - -class QQuickPackagePrivate : public QObjectPrivate -{ -public: - QQuickPackagePrivate() {} - - struct DataGuard : public QQmlGuard<QObject> - { - DataGuard(QObject *obj, QList<DataGuard> *l) : list(l) { (QQmlGuard<QObject>&)*this = obj; } - QList<DataGuard> *list; - void objectDestroyed(QObject *) override { - // we assume priv will always be destroyed after objectDestroyed calls - list->removeOne(*this); - } - }; - - QList<DataGuard> dataList; - static void data_append(QQmlListProperty<QObject> *prop, QObject *o) { - QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); - list->append(DataGuard(o, list)); - } - static void data_clear(QQmlListProperty<QObject> *prop) { - QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); - list->clear(); - } - static QObject *data_at(QQmlListProperty<QObject> *prop, int index) { - QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); - return list->at(index); - } - static int data_count(QQmlListProperty<QObject> *prop) { - QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); - return list->count(); - } -}; - -QHash<QObject *, QQuickPackageAttached *> QQuickPackageAttached::attached; - -QQuickPackageAttached::QQuickPackageAttached(QObject *parent) -: QObject(parent) -{ - attached.insert(parent, this); -} - -QQuickPackageAttached::~QQuickPackageAttached() -{ - attached.remove(parent()); -} - -QString QQuickPackageAttached::name() const -{ - return _name; -} - -void QQuickPackageAttached::setName(const QString &n) -{ - _name = n; -} - -QQuickPackage::QQuickPackage(QObject *parent) - : QObject(*(new QQuickPackagePrivate), parent) -{ -} - -QQuickPackage::~QQuickPackage() -{ -} - -QQmlListProperty<QObject> QQuickPackage::data() -{ - Q_D(QQuickPackage); - return QQmlListProperty<QObject>(this, &d->dataList, QQuickPackagePrivate::data_append, - QQuickPackagePrivate::data_count, - QQuickPackagePrivate::data_at, - QQuickPackagePrivate::data_clear); -} - -bool QQuickPackage::hasPart(const QString &name) -{ - Q_D(QQuickPackage); - for (int ii = 0; ii < d->dataList.count(); ++ii) { - QObject *obj = d->dataList.at(ii); - QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); - if (a && a->name() == name) - return true; - } - return false; -} - -QObject *QQuickPackage::part(const QString &name) -{ - Q_D(QQuickPackage); - if (name.isEmpty() && !d->dataList.isEmpty()) - return d->dataList.at(0); - - for (int ii = 0; ii < d->dataList.count(); ++ii) { - QObject *obj = d->dataList.at(ii); - QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); - if (a && a->name() == name) - return obj; - } - - if (name == QLatin1String("default") && !d->dataList.isEmpty()) - return d->dataList.at(0); - - return nullptr; -} - -QQuickPackageAttached *QQuickPackage::qmlAttachedProperties(QObject *o) -{ - return new QQuickPackageAttached(o); -} - - - -QT_END_NAMESPACE - -#include "moc_qquickpackage_p.cpp" diff --git a/src/qml/types/qquickpackage_p.h b/src/qml/types/qquickpackage_p.h deleted file mode 100644 index 122c7fcb30..0000000000 --- a/src/qml/types/qquickpackage_p.h +++ /dev/null @@ -1,101 +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$ -** -****************************************************************************/ - -#ifndef QQUICKPACKAGE_H -#define QQUICKPACKAGE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qqml.h> - -QT_BEGIN_NAMESPACE - -class QQuickPackagePrivate; -class QQuickPackageAttached; -class Q_AUTOTEST_EXPORT QQuickPackage : public QObject -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQuickPackage) - - Q_CLASSINFO("DefaultProperty", "data") - Q_PROPERTY(QQmlListProperty<QObject> data READ data) - -public: - QQuickPackage(QObject *parent=nullptr); - virtual ~QQuickPackage(); - - QQmlListProperty<QObject> data(); - - QObject *part(const QString & = QString()); - bool hasPart(const QString &); - - static QQuickPackageAttached *qmlAttachedProperties(QObject *); -}; - -class QQuickPackageAttached : public QObject -{ -Q_OBJECT -Q_PROPERTY(QString name READ name WRITE setName) -public: - QQuickPackageAttached(QObject *parent); - virtual ~QQuickPackageAttached(); - - QString name() const; - void setName(const QString &n); - - static QHash<QObject *, QQuickPackageAttached *> attached; -private: - QString _name; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickPackage) -QML_DECLARE_TYPEINFO(QQuickPackage, QML_HAS_ATTACHED_PROPERTIES) - -#endif // QQUICKPACKAGE_H diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp deleted file mode 100644 index edb112276c..0000000000 --- a/src/qml/types/qquickworkerscript.cpp +++ /dev/null @@ -1,675 +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 "qtqmlglobal_p.h" -#include "qquickworkerscript_p.h" -#if QT_CONFIG(qml_list_model) -#include "qqmllistmodel_p.h" -#include "qqmllistmodelworkeragent_p.h" -#endif -#include <private/qqmlengine_p.h> -#include <private/qqmlexpression_p.h> - -#include <QtCore/qcoreevent.h> -#include <QtCore/qcoreapplication.h> -#include <QtCore/qdebug.h> -#include <QtQml/qjsengine.h> -#include <QtCore/qmutex.h> -#include <QtCore/qwaitcondition.h> -#include <QtCore/qfile.h> -#include <QtCore/qdatetime.h> -#include <QtQml/qqmlinfo.h> -#include <QtQml/qqmlfile.h> -#if QT_CONFIG(qml_network) -#include <QtNetwork/qnetworkaccessmanager.h> -#include "qqmlnetworkaccessmanagerfactory.h" -#endif - -#include <private/qv8engine_p.h> -#include <private/qv4serialize_p.h> - -#include <private/qv4value_p.h> -#include <private/qv4functionobject_p.h> -#include <private/qv4script_p.h> -#include <private/qv4scopedvalue_p.h> -#include <private/qv4jscall_p.h> - -QT_BEGIN_NAMESPACE - -class WorkerDataEvent : public QEvent -{ -public: - enum Type { WorkerData = QEvent::User }; - - WorkerDataEvent(int workerId, const QByteArray &data); - virtual ~WorkerDataEvent(); - - int workerId() const; - QByteArray data() const; - -private: - int m_id; - QByteArray m_data; -}; - -class WorkerLoadEvent : public QEvent -{ -public: - enum Type { WorkerLoad = WorkerDataEvent::WorkerData + 1 }; - - WorkerLoadEvent(int workerId, const QUrl &url); - - int workerId() const; - QUrl url() const; - -private: - int m_id; - QUrl m_url; -}; - -class WorkerRemoveEvent : public QEvent -{ -public: - enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 }; - - WorkerRemoveEvent(int workerId); - - int workerId() const; - -private: - int m_id; -}; - -class WorkerErrorEvent : public QEvent -{ -public: - enum Type { WorkerError = WorkerRemoveEvent::WorkerRemove + 1 }; - - WorkerErrorEvent(const QQmlError &error); - - QQmlError error() const; - -private: - QQmlError m_error; -}; - -class QQuickWorkerScriptEnginePrivate : public QObject -{ - Q_OBJECT -public: - enum WorkerEventTypes { - WorkerDestroyEvent = QEvent::User + 100 - }; - - QQuickWorkerScriptEnginePrivate(QQmlEngine *eng); - - QQmlEngine *qmlengine; - - QMutex m_lock; - QWaitCondition m_wait; - - struct WorkerScript : public QV8Engine { - WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent); - ~WorkerScript() override; - -#if QT_CONFIG(qml_network) - QNetworkAccessManager *networkAccessManager() override; -#endif - - QQuickWorkerScriptEnginePrivate *p = nullptr; - QUrl source; - QQuickWorkerScript *owner = nullptr; -#if QT_CONFIG(qml_network) - QScopedPointer<QNetworkAccessManager> accessManager; -#endif - int id = -1; - }; - - QHash<int, WorkerScript *> workers; - QV4::ReturnedValue getWorker(WorkerScript *); - - int m_nextId; - - static QV4::ReturnedValue method_sendMessage(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - -signals: - void stopThread(); - -protected: - bool event(QEvent *) override; - -private: - void processMessage(int, const QByteArray &); - void processLoad(int, const QUrl &); - void reportScriptException(WorkerScript *, const QQmlError &error); -}; - -QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine) -: qmlengine(engine), m_nextId(0) -{ -} - -QV4::ReturnedValue QQuickWorkerScriptEnginePrivate::method_sendMessage(const QV4::FunctionObject *b, - const QV4::Value *, const QV4::Value *argv, int argc) -{ - QV4::Scope scope(b); - WorkerScript *script = static_cast<WorkerScript *>(scope.engine->v8Engine); - - QV4::ScopedValue v(scope, argc > 0 ? argv[0] : QV4::Value::undefinedValue()); - QByteArray data = QV4::Serialize::serialize(v, scope.engine); - - QMutexLocker locker(&script->p->m_lock); - if (script && script->owner) - QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data)); - - return QV4::Encode::undefined(); -} - -bool QQuickWorkerScriptEnginePrivate::event(QEvent *event) -{ - if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { - WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event); - processMessage(workerEvent->workerId(), workerEvent->data()); - return true; - } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) { - WorkerLoadEvent *workerEvent = static_cast<WorkerLoadEvent *>(event); - processLoad(workerEvent->workerId(), workerEvent->url()); - return true; - } else if (event->type() == (QEvent::Type)WorkerDestroyEvent) { - emit stopThread(); - return true; - } else if (event->type() == (QEvent::Type)WorkerRemoveEvent::WorkerRemove) { - QMutexLocker locker(&m_lock); - WorkerRemoveEvent *workerEvent = static_cast<WorkerRemoveEvent *>(event); - QHash<int, WorkerScript *>::iterator itr = workers.find(workerEvent->workerId()); - if (itr != workers.end()) { - delete itr.value(); - workers.erase(itr); - } - return true; - } else { - return QObject::event(event); - } -} - -void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &data) -{ - WorkerScript *script = workers.value(id); - if (!script) - return; - - QV4::ExecutionEngine *v4 = QV8Engine::getV4(script); - QV4::Scope scope(v4); - QV4::ScopedString v(scope); - QV4::ScopedObject worker(scope, v4->globalObject->get((v = v4->newString(QStringLiteral("WorkerScript"))))); - QV4::ScopedFunctionObject onmessage(scope); - if (worker) - onmessage = worker->get((v = v4->newString(QStringLiteral("onMessage")))); - - if (!onmessage) - return; - - QV4::ScopedValue value(scope, QV4::Serialize::deserialize(data, v4)); - - QV4::JSCallData jsCallData(scope, 1); - *jsCallData->thisObject = v4->global(); - jsCallData->args[0] = value; - onmessage->call(jsCallData); - if (scope.hasException()) { - QQmlError error = scope.engine->catchExceptionAsQmlError(); - reportScriptException(script, error); - } -} - -void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url) -{ - if (url.isRelative()) - return; - - QString fileName = QQmlFile::urlToLocalFileOrQrc(url); - - WorkerScript *script = workers.value(id); - if (!script) - return; - - QV4::ExecutionEngine *v4 = QV8Engine::getV4(script); - - script->source = url; - - if (fileName.endsWith(QLatin1String(".mjs"))) { - auto moduleUnit = v4->loadModule(url); - if (moduleUnit) { - if (moduleUnit->instantiate(v4)) - moduleUnit->evaluate(); - } else { - v4->throwError(QStringLiteral("Could not load module file")); - } - } else { - QString error; - QV4::Scope scope(v4); - QScopedPointer<QV4::Script> program; - program.reset(QV4::Script::createFromFileOrCache(v4, /*qmlContext*/nullptr, fileName, url, &error)); - if (program.isNull()) { - if (!error.isEmpty()) - qWarning().nospace() << error; - return; - } - - if (!v4->hasException) - program->run(); - } - - if (v4->hasException) { - QQmlError error = v4->catchExceptionAsQmlError(); - reportScriptException(script, error); - } -} - -void QQuickWorkerScriptEnginePrivate::reportScriptException(WorkerScript *script, - const QQmlError &error) -{ - QMutexLocker locker(&script->p->m_lock); - if (script->owner) - QCoreApplication::postEvent(script->owner, new WorkerErrorEvent(error)); -} - -WorkerDataEvent::WorkerDataEvent(int workerId, const QByteArray &data) -: QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data) -{ -} - -WorkerDataEvent::~WorkerDataEvent() -{ -} - -int WorkerDataEvent::workerId() const -{ - return m_id; -} - -QByteArray WorkerDataEvent::data() const -{ - return m_data; -} - -WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url) -: QEvent((QEvent::Type)WorkerLoad), m_id(workerId), m_url(url) -{ -} - -int WorkerLoadEvent::workerId() const -{ - return m_id; -} - -QUrl WorkerLoadEvent::url() const -{ - return m_url; -} - -WorkerRemoveEvent::WorkerRemoveEvent(int workerId) -: QEvent((QEvent::Type)WorkerRemove), m_id(workerId) -{ -} - -int WorkerRemoveEvent::workerId() const -{ - return m_id; -} - -WorkerErrorEvent::WorkerErrorEvent(const QQmlError &error) -: QEvent((QEvent::Type)WorkerError), m_error(error) -{ -} - -QQmlError WorkerErrorEvent::error() const -{ - return m_error; -} - -QQuickWorkerScriptEngine::QQuickWorkerScriptEngine(QQmlEngine *parent) -: QThread(parent), d(new QQuickWorkerScriptEnginePrivate(parent)) -{ - d->m_lock.lock(); - connect(d, SIGNAL(stopThread()), this, SLOT(quit()), Qt::DirectConnection); - start(QThread::LowestPriority); - d->m_wait.wait(&d->m_lock); - d->moveToThread(this); - d->m_lock.unlock(); -} - -QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine() -{ - d->m_lock.lock(); - QCoreApplication::postEvent(d, new QEvent((QEvent::Type)QQuickWorkerScriptEnginePrivate::WorkerDestroyEvent)); - d->m_lock.unlock(); - - //We have to force to cleanup the main thread's event queue here - //to make sure the main GUI release all pending locks/wait conditions which - //some worker script/agent are waiting for (QQmlListModelWorkerAgent::sync() for example). - while (!isFinished()) { - // We can't simply wait here, because the worker thread will not terminate - // until the main thread processes the last data event it generates - QCoreApplication::processEvents(); - yieldCurrentThread(); - } - - d->deleteLater(); -} - -QQuickWorkerScriptEnginePrivate::WorkerScript::WorkerScript(int id, QQuickWorkerScriptEnginePrivate *parent) - : QV8Engine(new QV4::ExecutionEngine) - , p(parent) - , id(id) -{ - m_v4Engine->v8Engine = this; - - initQmlGlobalObject(); - - QV4::Scope scope(m_v4Engine); - QV4::ScopedObject api(scope, scope.engine->newObject()); - QV4::ScopedString name(scope, m_v4Engine->newString(QStringLiteral("sendMessage"))); - QV4::ScopedValue sendMessage(scope, QV4::FunctionObject::createBuiltinFunction(m_v4Engine, name, method_sendMessage, 1)); - api->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("sendMessage"))), sendMessage); - m_v4Engine->globalObject->put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("WorkerScript"))), api); -} - -QQuickWorkerScriptEnginePrivate::WorkerScript::~WorkerScript() -{ - delete m_v4Engine; -} - -#if QT_CONFIG(qml_network) -QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerScript::networkAccessManager() -{ - if (!accessManager) { - if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) { - accessManager.reset(p->qmlengine->networkAccessManagerFactory()->create(p)); - } else { - accessManager.reset(new QNetworkAccessManager(p)); - } - } - return accessManager.data(); -} -#endif - -int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner) -{ - typedef QQuickWorkerScriptEnginePrivate::WorkerScript WorkerScript; - WorkerScript *script = new WorkerScript(d->m_nextId++, d); - - script->owner = owner; - - d->m_lock.lock(); - d->workers.insert(script->id, script); - d->m_lock.unlock(); - - return script->id; -} - -void QQuickWorkerScriptEngine::removeWorkerScript(int id) -{ - QQuickWorkerScriptEnginePrivate::WorkerScript* script = d->workers.value(id); - if (script) { - script->owner = nullptr; - QCoreApplication::postEvent(d, new WorkerRemoveEvent(id)); - } -} - -void QQuickWorkerScriptEngine::executeUrl(int id, const QUrl &url) -{ - QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url)); -} - -void QQuickWorkerScriptEngine::sendMessage(int id, const QByteArray &data) -{ - QCoreApplication::postEvent(d, new WorkerDataEvent(id, data)); -} - -void QQuickWorkerScriptEngine::run() -{ - d->m_lock.lock(); - - d->m_wait.wakeAll(); - - d->m_lock.unlock(); - - exec(); - - qDeleteAll(d->workers); - d->workers.clear(); -} - - -/*! - \qmltype WorkerScript - \instantiates QQuickWorkerScript - \ingroup qtquick-threading - \inqmlmodule QtQml - \brief Enables the use of threads in a Qt Quick application. - - Use WorkerScript to run operations in a new thread. - This is useful for running operations in the background so - that the main GUI thread is not blocked. - - Messages can be passed between the new thread and the parent thread - using \l sendMessage() and the \c onMessage() handler. - - An example: - - \snippet qml/workerscript/workerscript.qml 0 - - The above worker script specifies a JavaScript file, "script.mjs", that handles - the operations to be performed in the new thread. Here is \c script.mjs: - - \quotefile qml/workerscript/script.mjs - - When the user clicks anywhere within the rectangle, \c sendMessage() is - called, triggering the \tt WorkerScript.onMessage() handler in - \tt script.mjs. This in turn sends a reply message that is then received - by the \tt onMessage() handler of \tt myWorker. - - The example uses a script that is an ECMAScript module, because it has the ".mjs" extension. - It can use import statements to access functionality from other modules and it is run in JavaScript - strict mode. - - If a worker script has the extension ".js" instead, then it is considered to contain plain JavaScript - statements and it is run in non-strict mode. - - \note Each WorkerScript element will instantiate a separate JavaScript engine to ensure perfect - isolation and thread-safety. If the impact of that results in a memory consumption that is too - high for your environment, then consider sharing a WorkerScript element. - - \section3 Restrictions - - Since the \c WorkerScript.onMessage() function is run in a separate thread, the - JavaScript file is evaluated in a context separate from the main QML engine. This means - that unlike an ordinary JavaScript file that is imported into QML, the \c script.mjs - in the above example cannot access the properties, methods or other attributes - of the QML item, nor can it access any context properties set on the QML object - through QQmlContext. - - Additionally, there are restrictions on the types of values that can be passed to and - from the worker script. See the sendMessage() documentation for details. - - Worker scripts that are plain JavaScript sources can not use \l {qtqml-javascript-imports.html}{.import} syntax. - Scripts that are ECMAScript modules can freely use import and export statements. - - \sa {Qt Quick Examples - Threading}, - {Threaded ListModel Example} -*/ -QQuickWorkerScript::QQuickWorkerScript(QObject *parent) -: QObject(parent), m_engine(nullptr), m_scriptId(-1), m_componentComplete(true) -{ -} - -QQuickWorkerScript::~QQuickWorkerScript() -{ - if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId); -} - -/*! - \qmlproperty url WorkerScript::source - - This holds the url of the JavaScript file that implements the - \tt WorkerScript.onMessage() handler for threaded operations. - - If the file name component of the url ends with ".mjs", then the script - is parsed as an ECMAScript module and run in strict mode. Otherwise it is considered to be - plain script. -*/ -QUrl QQuickWorkerScript::source() const -{ - return m_source; -} - -void QQuickWorkerScript::setSource(const QUrl &source) -{ - if (m_source == source) - return; - - m_source = source; - - if (engine()) - m_engine->executeUrl(m_scriptId, m_source); - - emit sourceChanged(); -} - -/*! - \qmlmethod WorkerScript::sendMessage(jsobject message) - - Sends the given \a message to a worker script handler in another - thread. The other worker script handler can receive this message - through the onMessage() handler. - - The \c message object may only contain values of the following - types: - - \list - \li boolean, number, string - \li JavaScript objects and arrays - \li ListModel objects (any other type of QObject* is not allowed) - \endlist - - All objects and arrays are copied to the \c message. With the exception - of ListModel objects, any modifications by the other thread to an object - passed in \c message will not be reflected in the original object. -*/ -void QQuickWorkerScript::sendMessage(QQmlV4Function *args) -{ - if (!engine()) { - qWarning("QQuickWorkerScript: Attempt to send message before WorkerScript establishment"); - return; - } - - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue argument(scope, QV4::Value::undefinedValue()); - if (args->length() != 0) - argument = (*args)[0]; - - m_engine->sendMessage(m_scriptId, QV4::Serialize::serialize(argument, scope.engine)); -} - -void QQuickWorkerScript::classBegin() -{ - m_componentComplete = false; -} - -QQuickWorkerScriptEngine *QQuickWorkerScript::engine() -{ - if (m_engine) return m_engine; - if (m_componentComplete) { - QQmlEngine *engine = qmlEngine(this); - if (!engine) { - qWarning("QQuickWorkerScript: engine() called without qmlEngine() set"); - return nullptr; - } - - m_engine = QQmlEnginePrivate::get(engine)->getWorkerScriptEngine(); - m_scriptId = m_engine->registerWorkerScript(this); - - if (m_source.isValid()) - m_engine->executeUrl(m_scriptId, m_source); - - return m_engine; - } - return nullptr; -} - -void QQuickWorkerScript::componentComplete() -{ - m_componentComplete = true; - engine(); // Get it started now. -} - -/*! - \qmlsignal WorkerScript::message(jsobject msg) - - This signal is emitted when a message \a msg is received from a worker - script in another thread through a call to sendMessage(). - - The corresponding handler is \c onMessage. -*/ - -bool QQuickWorkerScript::event(QEvent *event) -{ - if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) { - QQmlEngine *engine = qmlEngine(this); - if (engine) { - WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event); - QV4::Scope scope(engine->handle()); - QV4::ScopedValue value(scope, QV4::Serialize::deserialize(workerEvent->data(), scope.engine)); - emit message(QQmlV4Handle(value)); - } - return true; - } else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) { - WorkerErrorEvent *workerEvent = static_cast<WorkerErrorEvent *>(event); - QQmlEnginePrivate::warning(qmlEngine(this), workerEvent->error()); - return true; - } else { - return QObject::event(event); - } -} - -QT_END_NAMESPACE - -#include <qquickworkerscript.moc> - -#include "moc_qquickworkerscript_p.cpp" diff --git a/src/qml/types/qquickworkerscript_p.h b/src/qml/types/qquickworkerscript_p.h deleted file mode 100644 index 1a8d2ab076..0000000000 --- a/src/qml/types/qquickworkerscript_p.h +++ /dev/null @@ -1,124 +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$ -** -****************************************************************************/ - -#ifndef QQUICKWORKERSCRIPT_P_H -#define QQUICKWORKERSCRIPT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qqml.h> - -#include <QtQml/qqmlparserstatus.h> -#include <QtCore/qthread.h> -#include <QtQml/qjsvalue.h> -#include <QtCore/qurl.h> - -QT_BEGIN_NAMESPACE - - -class QQuickWorkerScript; -class QQuickWorkerScriptEnginePrivate; -class QQuickWorkerScriptEngine : public QThread -{ -Q_OBJECT -public: - QQuickWorkerScriptEngine(QQmlEngine *parent = nullptr); - ~QQuickWorkerScriptEngine(); - - int registerWorkerScript(QQuickWorkerScript *); - void removeWorkerScript(int); - void executeUrl(int, const QUrl &); - void sendMessage(int, const QByteArray &); - -protected: - void run() override; - -private: - QQuickWorkerScriptEnginePrivate *d; -}; - -class QQmlV4Function; -class QQmlV4Handle; -class Q_AUTOTEST_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus -{ - Q_OBJECT - Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged) - - Q_INTERFACES(QQmlParserStatus) -public: - QQuickWorkerScript(QObject *parent = nullptr); - ~QQuickWorkerScript(); - - QUrl source() const; - void setSource(const QUrl &); - -public Q_SLOTS: - void sendMessage(QQmlV4Function*); - -Q_SIGNALS: - void sourceChanged(); - void message(const QQmlV4Handle &messageObject); - -protected: - void classBegin() override; - void componentComplete() override; - bool event(QEvent *) override; - -private: - QQuickWorkerScriptEngine *engine(); - QQuickWorkerScriptEngine *m_engine; - int m_scriptId; - QUrl m_source; - bool m_componentComplete; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickWorkerScript) - -#endif // QQUICKWORKERSCRIPT_P_H diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri index e74c89b1f1..c50273071b 100644 --- a/src/qml/types/types.pri +++ b/src/qml/types/types.pri @@ -1,52 +1,12 @@ SOURCES += \ $$PWD/qqmlbind.cpp \ $$PWD/qqmlconnections.cpp \ - $$PWD/qqmlmodelsmodule.cpp \ - $$PWD/qqmlmodelindexvaluetype.cpp \ - $$PWD/qqmlobjectmodel.cpp \ - $$PWD/qquickpackage.cpp \ - $$PWD/qqmlinstantiator.cpp \ - $$PWD/qqmltableinstancemodel.cpp + $$PWD/qqmlmodelindexvaluetype.cpp HEADERS += \ $$PWD/qqmlbind_p.h \ $$PWD/qqmlconnections_p.h \ - $$PWD/qqmlmodelsmodule_p.h \ - $$PWD/qqmlmodelindexvaluetype_p.h \ - $$PWD/qqmlobjectmodel_p.h \ - $$PWD/qquickpackage_p.h \ - $$PWD/qqmlinstantiator_p.h \ - $$PWD/qqmlinstantiator_p_p.h \ - $$PWD/qqmltableinstancemodel_p.h - -qtConfig(qml-worker-script) { - SOURCES += \ - $$PWD/qquickworkerscript.cpp - HEADERS += \ - $$PWD/qquickworkerscript_p.h -} - -qtConfig(qml-list-model) { - SOURCES += \ - $$PWD/qqmllistmodel.cpp \ - $$PWD/qqmllistmodelworkeragent.cpp - - HEADERS += \ - $$PWD/qqmllistmodel_p.h \ - $$PWD/qqmllistmodel_p_p.h \ - $$PWD/qqmllistmodelworkeragent_p.h -} - -qtConfig(qml-delegate-model) { - SOURCES += \ - $$PWD/qqmldelegatemodel.cpp \ - $$PWD/qqmldelegatecomponent.cpp - - HEADERS += \ - $$PWD/qqmldelegatemodel_p.h \ - $$PWD/qqmldelegatemodel_p_p.h \ - $$PWD/qqmldelegatecomponent_p.h -} + $$PWD/qqmlmodelindexvaluetype_p.h qtConfig(qml-animation) { SOURCES += \ |