aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
authorAlan Alpert <aalpert@rim.com>2013-01-15 14:26:59 -0800
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-01-24 17:14:23 +0100
commit7cad0e52c5a020bd29635e9912fd8946a6b48124 (patch)
tree2f1530f14b8c595d2ad85a229588a988f7f5aba9 /src/qml
parentbbb3d5b403511f6e0bc1966835983b2574596e25 (diff)
Move the model classes from QtQuick to QtQml
This is needed for proper support of non-GUI instantiators in QtQml. Only private C++ classes are affected. Aside from name changes, model classes now operate on QObjects instead of QQuickItems, leading to minor changes in the implementation of QtQuick classes using them. The old QML type names will still be registered in the QtQuick import for the forseeable future, but pointing to the new classes. The new QML types will be added in a second commit. Classes Affected: QQuickVisualDataGroup -> QQmlDataGroup QQuickVisualDataModel -> QQmlDelegateModel QQuickVisualItemModel -> QQmlObjectModel QQuickVisualModel -> QQmlInstanceModel QQuickChangeSet -> QQmlChangeSet QQuickListAccessor -> QQmlListAccessor QQuickListCompositor -> QQmlListCompositor QQuickPackage -> QQuickPackage (just moved for now) Change-Id: Ia19e630e53bfa9e5d459e289596cd11df1ea3930 Reviewed-by: Andrew den Exter <andrew.den.exter@qinetic.com.au>
Diffstat (limited to 'src/qml')
-rw-r--r--src/qml/items/items.pri10
-rw-r--r--src/qml/items/qqmldelegatemodel.cpp3154
-rw-r--r--src/qml/items/qqmldelegatemodel_p.h238
-rw-r--r--src/qml/items/qqmldelegatemodel_p_p.h411
-rw-r--r--src/qml/items/qqmlobjectmodel.cpp251
-rw-r--r--src/qml/items/qqmlobjectmodel_p.h174
-rw-r--r--src/qml/items/qquickpackage.cpp198
-rw-r--r--src/qml/items/qquickpackage_p.h96
-rw-r--r--src/qml/qml.pro1
-rw-r--r--src/qml/qml/qqmlengine.cpp8
-rw-r--r--src/qml/util/qqmladaptormodel.cpp977
-rw-r--r--src/qml/util/qqmladaptormodel_p.h156
-rw-r--r--src/qml/util/qqmlchangeset.cpp621
-rw-r--r--src/qml/util/qqmlchangeset_p.h167
-rw-r--r--src/qml/util/qqmllistaccessor.cpp138
-rw-r--r--src/qml/util/qqmllistaccessor_p.h78
-rw-r--r--src/qml/util/qqmllistcompositor.cpp1484
-rw-r--r--src/qml/util/qqmllistcompositor_p.h375
-rw-r--r--src/qml/util/util.pri8
19 files changed, 8545 insertions, 0 deletions
diff --git a/src/qml/items/items.pri b/src/qml/items/items.pri
new file mode 100644
index 0000000000..0fa489f4e1
--- /dev/null
+++ b/src/qml/items/items.pri
@@ -0,0 +1,10 @@
+SOURCES += \
+ $$PWD/qquickpackage.cpp \
+ $$PWD/qqmldelegatemodel.cpp \
+ $$PWD/qqmlobjectmodel.cpp
+
+HEADERS += \
+ $$PWD/qquickpackage_p.h \
+ $$PWD/qqmldelegatemodel_p.h \
+ $$PWD/qqmldelegatemodel_p_p.h \
+ $$PWD/qqmlobjectmodel_p.h
diff --git a/src/qml/items/qqmldelegatemodel.cpp b/src/qml/items/qqmldelegatemodel.cpp
new file mode 100644
index 0000000000..131b539d92
--- /dev/null
+++ b/src/qml/items/qqmldelegatemodel.cpp
@@ -0,0 +1,3154 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmldelegatemodel_p_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/qqmlcompiler_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlDelegateModelEngineData : public QV8Engine::Deletable
+{
+public:
+ enum
+ {
+ Model,
+ Groups,
+ IsUnresolved,
+ ItemsIndex,
+ PersistedItemsIndex,
+ InItems,
+ InPersistedItems,
+ StringCount
+ };
+
+ QQmlDelegateModelEngineData(QV8Engine *engine);
+ ~QQmlDelegateModelEngineData();
+
+ v8::Local<v8::Object> array(
+ QV8Engine *engine, const QVector<QQmlChangeSet::Remove> &changes);
+ v8::Local<v8::Object> array(
+ QV8Engine *engine, const QVector<QQmlChangeSet::Insert> &changes);
+ v8::Local<v8::Object> array(
+ QV8Engine *engine, const QVector<QQmlChangeSet::Change> &changes);
+
+
+ inline v8::Local<v8::String> model() { return strings->Get(Model)->ToString(); }
+ inline v8::Local<v8::String> groups() { return strings->Get(Groups)->ToString(); }
+ inline v8::Local<v8::String> isUnresolved() { return strings->Get(IsUnresolved)->ToString(); }
+ inline v8::Local<v8::String> itemsIndex() { return strings->Get(ItemsIndex)->ToString(); }
+ inline v8::Local<v8::String> persistedItemsIndex() { return strings->Get(PersistedItemsIndex)->ToString(); }
+ inline v8::Local<v8::String> inItems() { return strings->Get(InItems)->ToString(); }
+ inline v8::Local<v8::String> inPersistedItems() { return strings->Get(InPersistedItems)->ToString(); }
+
+ v8::Persistent<v8::Array> strings;
+ v8::Persistent<v8::Function> constructorChange;
+ v8::Persistent<v8::Function> constructorChangeArray;
+};
+
+V8_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 VisualDataModel
+ \instantiates QQmlDelegateModel
+ \inqmlmodule QtQuick 2
+ \ingroup qtquick-models
+ \brief Encapsulates a model and delegate
+
+ The VisualDataModel type encapsulates a model and the delegate that will
+ be instantiated for items in the model.
+
+ It is usually not necessary to create a VisualDataModel.
+ However, it can be useful for manipulating and accessing the \l modelIndex
+ when a QAbstractItemModel subclass is used as the
+ model. Also, VisualDataModel is used together with \l Package to
+ provide delegates to multiple views, and with VisualDataGroup to sort and filter
+ delegate items.
+
+ The example below illustrates using a VisualDataModel with a ListView.
+
+ \snippet qml/visualdatamodel.qml 0
+*/
+
+QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt)
+ : m_delegate(0)
+ , m_cacheMetaType(0)
+ , m_context(ctxt)
+ , m_parts(0)
+ , 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_cacheItems(0)
+ , m_items(0)
+ , m_persistedItems(0)
+{
+}
+
+QQmlDelegateModelPrivate::~QQmlDelegateModelPrivate()
+{
+ qDeleteAll(m_finishedIncubating);
+
+ if (m_cacheMetaType)
+ m_cacheMetaType->release();
+}
+
+void QQmlDelegateModelPrivate::init()
+{
+ Q_Q(QQmlDelegateModel);
+ m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag);
+
+ m_items = new QQmlDataGroup(QStringLiteral("items"), q, Compositor::Default, q);
+ m_items->setDefaultInclude(true);
+ m_persistedItems = new QQmlDataGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q);
+ QQmlDataGroupPrivate::get(m_items)->emitters.insert(this);
+}
+
+QQmlDelegateModel::QQmlDelegateModel()
+: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(0)))
+{
+ Q_D(QQmlDelegateModel);
+ d->init();
+}
+
+QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent)
+: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(ctxt)), parent)
+{
+ Q_D(QQmlDelegateModel);
+ d->init();
+}
+
+QQmlDelegateModel::~QQmlDelegateModel()
+{
+ Q_D(QQmlDelegateModel);
+
+ foreach (QQmlDelegateModelItem *cacheItem, d->m_cache) {
+ if (cacheItem->object) {
+ delete cacheItem->object;
+
+ cacheItem->object = 0;
+ cacheItem->contextData->destroy();
+ cacheItem->contextData = 0;
+ cacheItem->scriptRef -= 1;
+ }
+ cacheItem->groups &= ~Compositor::UnresolvedFlag;
+ cacheItem->objectRef = 0;
+ if (!cacheItem->isReferenced())
+ delete cacheItem;
+ }
+}
+
+
+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 (QQmlDataGroupPrivate::get(d->m_items)->defaultInclude)
+ defaultGroups |= Compositor::DefaultFlag;
+ if (QQmlDataGroupPrivate::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()) {
+ qmlInfo(d->m_groups[i]) << QQmlDataGroup::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);
+
+ QQmlDataGroupPrivate *group = QQmlDataGroupPrivate::get(d->m_groups[i]);
+ group->setModel(this, Compositor::Group(i));
+ if (group->defaultInclude)
+ defaultGroups |= (1 << i);
+ }
+ }
+
+ d->m_cacheMetaType = new QQmlDelegateModelItemMetaType(
+ QQmlEnginePrivate::getV8Engine(d->m_context->engine()), 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->m_adaptorModel.count();
+ d->m_compositor.append(
+ &d->m_adaptorModel,
+ 0,
+ d->m_count,
+ defaultGroups | Compositor::AppendFlag | Compositor::PrependFlag,
+ &inserts);
+ d->itemsInserted(inserts);
+ d->emitChanges();
+
+ if (d->m_adaptorModel.canFetchMore())
+ QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
+}
+
+/*!
+ \qmlproperty model QtQuick2::VisualDataModel::model
+ This property holds the model providing data for the VisualDataModel.
+
+ 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{XmlListModel}.
+
+ \sa {qml-data-models}{Data Models}
+*/
+QVariant QQmlDelegateModel::model() const
+{
+ Q_D(const QQmlDelegateModel);
+ return d->m_adaptorModel.model();
+}
+
+void QQmlDelegateModel::setModel(const QVariant &model)
+{
+ Q_D(QQmlDelegateModel);
+
+ if (d->m_complete)
+ _q_itemsRemoved(0, d->m_count);
+
+ d->m_adaptorModel.setModel(model, this, d->m_context->engine());
+ 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->m_adaptorModel.count());
+ if (d->m_adaptorModel.canFetchMore())
+ QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
+ }
+}
+
+/*!
+ \qmlproperty Component QtQuick2::VisualDataModel::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) {
+ qmlInfo(this) << tr("The delegate of a VisualDataModel cannot be changed within onUpdated.");
+ return;
+ }
+ bool wasValid = d->m_delegate != 0;
+ d->m_delegate = delegate;
+ d->m_delegateValidated = false;
+ if (wasValid && d->m_complete) {
+ for (int i = 1; i < d->m_groupCount; ++i) {
+ QQmlDataGroupPrivate::get(d->m_groups[i])->changeSet.remove(
+ 0, d->m_compositor.count(Compositor::Group(i)));
+ }
+ }
+ if (d->m_complete && d->m_delegate) {
+ for (int i = 1; i < d->m_groupCount; ++i) {
+ QQmlDataGroupPrivate::get(d->m_groups[i])->changeSet.insert(
+ 0, d->m_compositor.count(Compositor::Group(i)));
+ }
+ }
+ d->emitChanges();
+}
+
+/*!
+ \qmlproperty QModelIndex QtQuick2::VisualDataModel::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 qml/visualdatamodel_rootindex/main.cpp 0
+
+ \c view.qml:
+ \snippet qml/visualdatamodel_rootindex/view.qml 0
+
+ If the \l 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->m_adaptorModel.setModel(d->m_adaptorModel.list.list(), this, d->m_context->engine());
+ if (d->m_adaptorModel.canFetchMore())
+ d->m_adaptorModel.fetchMore();
+ if (d->m_complete) {
+ const int newCount = d->m_adaptorModel.count();
+ if (oldCount)
+ _q_itemsRemoved(0, oldCount);
+ if (newCount)
+ _q_itemsInserted(0, newCount);
+ }
+ if (changed)
+ emit rootIndexChanged();
+ }
+}
+
+/*!
+ \qmlmethod QModelIndex QtQuick2::VisualDataModel::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 QtQuick2::VisualDataModel::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 QtQuick2::VisualDataModel::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)
+{
+ QQmlDelegateModel::ReleaseFlags stat = 0;
+ if (!object)
+ return stat;
+
+ if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object)) {
+ if (cacheItem->releaseObject()) {
+ cacheItem->destroyObject();
+ emitDestroyingItem(object);
+ if (cacheItem->incubationTask) {
+ releaseIncubator(cacheItem->incubationTask);
+ cacheItem->incubationTask = 0;
+ }
+ cacheItem->Dispose();
+ stat |= QQmlInstanceModel::Destroyed;
+ } else {
+ stat |= QQmlDelegateModel::Referenced;
+ }
+ }
+ return stat;
+}
+
+/*
+ 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() << "VisualDataModel::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 = 0;
+
+ 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<QQmlDataGroup> *property, QQmlDataGroup *group)
+{
+ QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data);
+ if (d->m_complete)
+ return;
+ if (d->m_groupCount == Compositor::MaximumGroupCount) {
+ qmlInfo(d->q_func()) << QQmlDelegateModel::tr("The maximum number of supported VisualDataGroups is 8");
+ return;
+ }
+ d->m_groups[d->m_groupCount] = group;
+ d->m_groupCount += 1;
+}
+
+int QQmlDelegateModelPrivate::group_count(
+ QQmlListProperty<QQmlDataGroup> *property)
+{
+ QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data);
+ return d->m_groupCount - 1;
+}
+
+QQmlDataGroup *QQmlDelegateModelPrivate::group_at(
+ QQmlListProperty<QQmlDataGroup> *property, int index)
+{
+ QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data);
+ return index >= 0 && index < d->m_groupCount - 1
+ ? d->m_groups[index + 1]
+ : 0;
+}
+
+/*!
+ \qmlproperty list<VisualDataGroup> QtQuick2::VisualDataModel::groups
+
+ This property holds a visual data model's group definitions.
+
+ Groups define a sub-set of the items in a visual data model and can be used to filter
+ a model.
+
+ For every group defined in a VisualDataModel two attached properties are added to each
+ delegate item. The first of the form VisualDataModel.in\e{GroupName} holds whether the
+ item belongs to the group and the second VisualDataModel.\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 qml/visualdatagroup.qml 0
+*/
+
+QQmlListProperty<QQmlDataGroup> QQmlDelegateModel::groups()
+{
+ Q_D(QQmlDelegateModel);
+ return QQmlListProperty<QQmlDataGroup>(
+ this,
+ d,
+ QQmlDelegateModelPrivate::group_append,
+ QQmlDelegateModelPrivate::group_count,
+ QQmlDelegateModelPrivate::group_at,
+ 0);
+}
+
+/*!
+ \qmlproperty VisualDataGroup QtQuick2::VisualDataModel::items
+
+ This property holds visual data model's default group to which all new items are added.
+*/
+
+QQmlDataGroup *QQmlDelegateModel::items()
+{
+ Q_D(QQmlDelegateModel);
+ return d->m_items;
+}
+
+/*!
+ \qmlproperty VisualDataGroup QtQuick2::VisualDataModel::persistedItems
+
+ This property holds visual data 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
+ VisualDataModel.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 QtQuick2::VisualDataGroup::create() function are automatically added
+ to this group.
+*/
+
+QQmlDataGroup *QQmlDelegateModel::persistedItems()
+{
+ Q_D(QQmlDelegateModel);
+ return d->m_persistedItems;
+}
+
+/*!
+ \qmlproperty string QtQuick2::VisualDataModel::filterOnGroup
+
+ This property holds the name of the group used to filter the visual data model.
+
+ Only items which 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) {
+ qmlInfo(this) << tr("The group of a VisualDataModel 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;
+ }
+ }
+
+ QQmlDataGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this);
+ if (m_compositorGroup != previousGroup) {
+ QVector<QQmlChangeSet::Remove> removes;
+ QVector<QQmlChangeSet::Insert> 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) {
+ foreach (QQmlPartsModel *model, m_parts->models)
+ model->updateFilterGroup(m_compositorGroup, changeSet);
+ }
+ }
+}
+
+/*!
+ \qmlproperty object QtQuick2::VisualDataModel::parts
+
+ The \a parts property selects a VisualDataModel 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
+ VisualDataModel {
+ 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;
+}
+
+void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package)
+{
+ for (int i = 1; i < m_groupCount; ++i)
+ QQmlDataGroupPrivate::get(m_groups[i])->createdPackage(incubationTask->index[i], package);
+}
+
+void QQmlDelegateModelPrivate::emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package)
+{
+ for (int i = 1; i < m_groupCount; ++i)
+ QQmlDataGroupPrivate::get(m_groups[i])->initPackage(incubationTask->index[i], package);
+}
+
+void QQmlDelegateModelPrivate::emitDestroyingPackage(QQuickPackage *package)
+{
+ for (int i = 1; i < m_groupCount; ++i)
+ QQmlDataGroupPrivate::get(m_groups[i])->destroyingPackage(package);
+}
+
+void QQDMIncubationTask::statusChanged(Status status)
+{
+ vdm->incubatorStatusChanged(this, status);
+}
+
+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::removeCacheItem(QQmlDelegateModelItem *cacheItem)
+{
+ int cidx = m_cache.indexOf(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)
+{
+ Q_Q(QQmlDelegateModel);
+ if (status != QQmlIncubator::Ready && status != QQmlIncubator::Error)
+ return;
+
+ QQmlDelegateModelItem *cacheItem = incubationTask->incubating;
+ cacheItem->incubationTask = 0;
+ incubationTask->incubating = 0;
+ releaseIncubator(incubationTask);
+
+ if (status == QQmlIncubator::Ready) {
+ if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object))
+ emitCreatedPackage(incubationTask, package);
+ else
+ emitCreatedItem(incubationTask, cacheItem->object);
+ } else if (status == QQmlIncubator::Error) {
+ qmlInfo(q, m_delegate->errors()) << "Error creating delegate";
+ }
+
+ if (!cacheItem->isObjectReferenced()) {
+ if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object))
+ emitDestroyingPackage(package);
+ else
+ emitDestroyingItem(cacheItem->object);
+ delete cacheItem->object;
+ cacheItem->object = 0;
+ cacheItem->scriptRef -= 1;
+ cacheItem->contextData->destroy();
+ cacheItem->contextData = 0;
+ 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, bool asynchronous)
+{
+ Q_Q(QQmlDelegateModel);
+ if (!m_delegate || index < 0 || index >= m_compositor.count(group)) {
+ qWarning() << "VisualDataModel::item: index out range" << index << m_compositor.count(group);
+ return 0;
+ } else if (!m_context->isValid()) {
+ return 0;
+ }
+
+ 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, m_context->engine(), it.modelIndex());
+ if (!cacheItem)
+ return 0;
+
+ cacheItem->groups = it->flags;
+
+ m_cache.insert(it.cacheIndex, cacheItem);
+ m_compositor.setFlags(it, 1, Compositor::CacheFlag);
+ Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache));
+ }
+
+ // 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) {
+ if (!asynchronous && cacheItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) {
+ // previously requested async - now needed immediately
+ cacheItem->incubationTask->forceCompletion();
+ }
+ } else if (!cacheItem->object) {
+ QQmlContext *creationContext = m_delegate->creationContext();
+
+ cacheItem->scriptRef += 1;
+
+ cacheItem->incubationTask = new QQDMIncubationTask(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
+ 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));
+ ctxt->contextObject = cacheItem;
+ cacheItem->contextData = ctxt;
+
+ if (m_adaptorModel.hasProxyObject()) {
+ if (QQmlAdaptorModelProxyInterface *proxy
+ = qobject_cast<QQmlAdaptorModelProxyInterface *>(cacheItem)) {
+ ctxt = new QQmlContextData;
+ ctxt->setParent(cacheItem->contextData, true);
+ ctxt->contextObject = proxy->proxiedObject();
+ }
+ }
+
+ cacheItem->incubateObject(
+ m_delegate,
+ m_context->engine(),
+ ctxt,
+ QQmlContextData::get(m_context));
+ }
+
+ if (index == m_compositor.count(group) - 1 && m_adaptorModel.canFetchMore())
+ QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest));
+
+ // Remove the temporary reference count.
+ cacheItem->scriptRef -= 1;
+ if (cacheItem->object)
+ return cacheItem->object;
+
+ cacheItem->releaseObject();
+ if (!cacheItem->isReferenced()) {
+ removeCacheItem(cacheItem);
+ delete cacheItem;
+ }
+
+ return 0;
+}
+
+/*
+ If asynchronous is true or the component is being loaded asynchronously due
+ to an ancestor being loaded asynchronously, item() may return 0. In this
+ case itemCreated() will be emitted when the item is available. The item
+ at this stage does not have any references, so item() must be called again
+ to ensure a reference is held. Any call to item() which returns a valid item
+ must be matched by a call to release() in order to destroy the item.
+*/
+QObject *QQmlDelegateModel::object(int index, bool asynchronous)
+{
+ Q_D(QQmlDelegateModel);
+ if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) {
+ qWarning() << "VisualDataModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup);
+ return 0;
+ }
+
+ QObject *object = d->object(d->m_compositorGroup, index, asynchronous);
+ if (!object)
+ return 0;
+
+ return object;
+}
+
+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.mid(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(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_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);
+
+ foreach (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)
+ QQmlDataGroupPrivate::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 < count; ++i)
+ attached->m_currentIndex[i] += deltas[i];
+ }
+}
+
+void QQmlDelegateModelPrivate::itemsInserted(
+ const QVector<Compositor::Insert> &inserts,
+ QVarLengthArray<QVector<QQmlChangeSet::Insert>, 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;
+
+ foreach (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::Insert(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 (; cacheIndex < m_cache.count(); ++cacheIndex)
+ incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted);
+}
+
+void QQmlDelegateModelPrivate::itemsInserted(const QVector<Compositor::Insert> &inserts)
+{
+ QVarLengthArray<QVector<QQmlChangeSet::Insert>, 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)
+ QQmlDataGroupPrivate::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;
+
+ for (int i = 0, c = d->m_cache.count(); i < c; ++i) {
+ QQmlDelegateModelItem *item = d->m_cache.at(i);
+ if (item->modelIndex() >= index)
+ item->setModelIndex(item->modelIndex() + count);
+ }
+
+ QVector<Compositor::Insert> inserts;
+ d->m_compositor.listItemsInserted(&d->m_adaptorModel, index, count, &inserts);
+ d->itemsInserted(inserts);
+ d->emitChanges();
+}
+
+void QQmlDelegateModelPrivate::itemsRemoved(
+ const QVector<Compositor::Remove> &removes,
+ QVarLengthArray<QVector<QQmlChangeSet::Remove>, 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;
+
+ foreach (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::Remove(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) {
+ 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 (; cacheIndex < m_cache.count(); ++cacheIndex)
+ incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed);
+}
+
+void QQmlDelegateModelPrivate::itemsRemoved(const QVector<Compositor::Remove> &removes)
+{
+ QVarLengthArray<QVector<QQmlChangeSet::Remove>, 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)
+ QQmlDataGroupPrivate::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;
+
+ for (int i = 0, c = d->m_cache.count(); i < c; ++i) {
+ QQmlDelegateModelItem *item = d->m_cache.at(i);
+ if (item->modelIndex() >= index + count)
+ item->setModelIndex(item->modelIndex() - count);
+ else if (item->modelIndex() >= index)
+ item->setModelIndex(-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::Remove>, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount);
+ itemsRemoved(removes, &translatedRemoves, &movedItems);
+
+ QVarLengthArray<QVector<QQmlChangeSet::Insert>, 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) {
+ QQmlDataGroupPrivate::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;
+
+ for (int i = 0, c = d->m_cache.count(); i < c; ++i) {
+ QQmlDelegateModelItem *item = d->m_cache.at(i);
+ if (item->modelIndex() >= from && item->modelIndex() < from + count)
+ item->setModelIndex(item->modelIndex() - from + to);
+ else if (item->modelIndex() >= minimum && item->modelIndex() < maximum)
+ item->setModelIndex(item->modelIndex() + difference);
+ }
+
+ 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();
+}
+
+template <typename T> v8::Local<v8::Array>
+QQmlDelegateModelPrivate::buildChangeList(const QVector<T> &changes)
+{
+ v8::Local<v8::Array> indexes = v8::Array::New(changes.count());
+ v8::Local<v8::String> indexKey = v8::String::New("index");
+ v8::Local<v8::String> countKey = v8::String::New("count");
+ v8::Local<v8::String> moveIdKey = v8::String::New("moveId");
+
+ for (int i = 0; i < changes.count(); ++i) {
+ v8::Local<v8::Object> object = v8::Object::New();
+ object->Set(indexKey, v8::Integer::New(changes.at(i).index));
+ object->Set(countKey, v8::Integer::New(changes.at(i).count));
+ object->Set(moveIdKey, changes.at(i).moveId != -1 ? v8::Integer::New(changes.at(i).count) : v8::Undefined());
+ indexes->Set(i, object);
+ }
+ return indexes;
+}
+
+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::emitChanges()
+{
+ if (m_transaction || !m_complete || !m_context->isValid())
+ return;
+
+ m_transaction = true;
+ QV8Engine *engine = QQmlEnginePrivate::getV8Engine(m_context->engine());
+ for (int i = 1; i < m_groupCount; ++i)
+ QQmlDataGroupPrivate::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)
+ QQmlDataGroupPrivate::get(m_groups[i])->emitModelUpdated(reset);
+
+ foreach (QQmlDelegateModelItem *cacheItem, m_cache) {
+ 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->m_adaptorModel.count();
+
+ for (int i = 0, c = d->m_cache.count(); i < c; ++i) {
+ QQmlDelegateModelItem *item = d->m_cache.at(i);
+ if (item->modelIndex() != -1)
+ item->setModelIndex(-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->m_adaptorModel.invalidateModel(this);
+
+ 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);
+}
+
+void QQmlDelegateModel::_q_layoutChanged()
+{
+ Q_D(QQmlDelegateModel);
+ _q_itemsChanged(0, d->m_count, QVector<int>());
+}
+
+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 v8::Local<v8::Object> &object, int groups)
+{
+ if (!m_context->isValid())
+ return false;
+
+ QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), -1);
+ if (!cacheItem)
+ return false;
+
+ v8::Local<v8::Array> propertyNames = object->GetPropertyNames();
+ for (uint i = 0; i < propertyNames->Length(); ++i) {
+ v8::Local<v8::String> propertyName = propertyNames->Get(i)->ToString();
+ cacheItem->setValue(
+ m_cacheMetaType->v8Engine->toString(propertyName),
+ m_cacheMetaType->v8Engine->toVariant(object->Get(propertyName), 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>() << Compositor::Insert(before, 1, cacheItem->groups & ~Compositor::CacheFlag));
+
+ before = m_compositor.insert(before, 0, 0, 1, cacheItem->groups);
+ m_cache.insert(before.cacheIndex, cacheItem);
+
+ return true;
+}
+
+//============================================================================
+
+QQmlDelegateModelItemMetaType::QQmlDelegateModelItemMetaType(
+ QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames)
+ : model(model)
+ , groupCount(groupNames.count() + 1)
+ , v8Engine(engine)
+ , metaObject(0)
+ , groupNames(groupNames)
+{
+}
+
+QQmlDelegateModelItemMetaType::~QQmlDelegateModelItemMetaType()
+{
+ if (metaObject)
+ metaObject->release();
+ qPersistentDispose(constructor);
+}
+
+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 = QStringLiteral("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) + QStringLiteral("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::initializeConstructor()
+{
+ v8::HandleScope handleScope;
+ v8::Context::Scope contextScope(v8Engine->context());
+
+ QQmlDelegateModelEngineData *data = engineData(v8Engine);
+
+ constructor = qPersistentNew(v8::ObjectTemplate::New());
+
+ constructor->SetHasExternalResource(true);
+ constructor->SetAccessor(data->model(), get_model);
+ constructor->SetAccessor(data->groups(), get_groups, set_groups);
+ constructor->SetAccessor(data->isUnresolved(), get_member, 0, v8::Int32::New(30));
+ constructor->SetAccessor(data->inItems(), get_member, set_member, v8::Int32::New(1));
+ constructor->SetAccessor(data->inPersistedItems(), get_member, set_member, v8::Int32::New(2));
+ constructor->SetAccessor(data->itemsIndex(), get_index, 0, v8::Int32::New(1));
+ constructor->SetAccessor(data->persistedItemsIndex(), get_index, 0, v8::Int32::New(2));
+
+ for (int i = 2; i < groupNames.count(); ++i) {
+ QString propertyName = QStringLiteral("in") + groupNames.at(i);
+ propertyName.replace(2, 1, propertyName.at(2).toUpper());
+ constructor->SetAccessor(
+ v8Engine->toString(propertyName), get_member, set_member, v8::Int32::New(i + 1));
+ }
+ for (int i = 2; i < groupNames.count(); ++i) {
+ const QString propertyName = groupNames.at(i) + QStringLiteral("Index");
+ constructor->SetAccessor(
+ v8Engine->toString(propertyName), get_index, 0, v8::Int32::New(i + 1));
+ }
+}
+
+int QQmlDelegateModelItemMetaType::parseGroups(const QStringList &groups) const
+{
+ int groupFlags = 0;
+ foreach (const QString &groupName, groups) {
+ int index = groupNames.indexOf(groupName);
+ if (index != -1)
+ groupFlags |= 2 << index;
+ }
+ return groupFlags;
+}
+
+int QQmlDelegateModelItemMetaType::parseGroups(const v8::Local<v8::Value> &groups) const
+{
+ int groupFlags = 0;
+ if (groups->IsString()) {
+ const QString groupName = v8Engine->toString(groups);
+ int index = groupNames.indexOf(groupName);
+ if (index != -1)
+ groupFlags |= 2 << index;
+ } else if (groups->IsArray()) {
+ v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(groups);
+ for (uint i = 0; i < array->Length(); ++i) {
+ const QString groupName = v8Engine->toString(array->Get(i));
+ int index = groupNames.indexOf(groupName);
+ if (index != -1)
+ groupFlags |= 2 << index;
+ }
+ }
+ return groupFlags;
+}
+
+v8::Handle<v8::Value> QQmlDelegateModelItemMetaType::get_model(
+ v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QQmlDelegateModelItem *cacheItem = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object");
+ if (!cacheItem->metaType->model)
+ return v8::Undefined();
+
+ return cacheItem->get();
+}
+
+v8::Handle<v8::Value> QQmlDelegateModelItemMetaType::get_groups(
+ v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QQmlDelegateModelItem *cacheItem = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object");
+
+ QStringList groups;
+ for (int i = 1; i < cacheItem->metaType->groupCount; ++i) {
+ if (cacheItem->groups & (1 << i))
+ groups.append(cacheItem->metaType->groupNames.at(i - 1));
+ }
+
+ return cacheItem->engine->fromVariant(groups);
+}
+
+void QQmlDelegateModelItemMetaType::set_groups(
+ v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QQmlDelegateModelItem *cacheItem = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object");
+
+ if (!cacheItem->metaType->model)
+ return;
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model);
+
+ const int groupFlags = model->m_cacheMetaType->parseGroups(value);
+ const int cacheIndex = model->m_cache.indexOf(cacheItem);
+ Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex);
+ model->setGroups(it, 1, Compositor::Cache, groupFlags);
+}
+
+v8::Handle<v8::Value> QQmlDelegateModelItemMetaType::get_member(
+ v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QQmlDelegateModelItem *cacheItem = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object");
+
+ return v8::Boolean::New(cacheItem->groups & (1 << info.Data()->Int32Value()));
+}
+
+void QQmlDelegateModelItemMetaType::set_member(
+ v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QQmlDelegateModelItem *cacheItem = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object");
+
+ if (!cacheItem->metaType->model)
+ return;
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model);
+
+ Compositor::Group group = Compositor::Group(info.Data()->Int32Value());
+ const bool member = value->BooleanValue();
+ const int groupFlag = (1 << group);
+ if (member == ((cacheItem->groups & groupFlag) != 0))
+ return;
+
+ 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);
+}
+
+v8::Handle<v8::Value> QQmlDelegateModelItemMetaType::get_index(
+ v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QQmlDelegateModelItem *cacheItem = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object");
+
+ return v8::Integer::New(cacheItem->groupIndex(Compositor::Group(info.Data()->Int32Value())));
+}
+
+
+//---------------------------------------------------------------------------
+
+QQmlDelegateModelItem::QQmlDelegateModelItem(
+ QQmlDelegateModelItemMetaType *metaType, int modelIndex)
+ : QV8ObjectResource(metaType->v8Engine)
+ , metaType(metaType)
+ , contextData(0)
+ , object(0)
+ , attached(0)
+ , incubationTask(0)
+ , objectRef(0)
+ , scriptRef(0)
+ , groups(0)
+ , index(modelIndex)
+{
+ metaType->addref();
+}
+
+QQmlDelegateModelItem::~QQmlDelegateModelItem()
+{
+ Q_ASSERT(scriptRef == 0);
+ Q_ASSERT(objectRef == 0);
+ Q_ASSERT(!object);
+
+ if (incubationTask && metaType->model)
+ QQmlDelegateModelPrivate::get(metaType->model)->releaseIncubator(incubationTask);
+
+ metaType->release();
+
+}
+
+void QQmlDelegateModelItem::Dispose()
+{
+ --scriptRef;
+ if (isReferenced())
+ return;
+
+ if (metaType->model) {
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model);
+ model->removeCacheItem(this);
+ }
+ delete this;
+}
+
+/*
+ This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData
+ arguments instead of QQmlContext which means we don't have to construct the rather weighty
+ wrapper class for every delegate item.
+*/
+void QQmlDelegateModelItem::incubateObject(
+ QQmlComponent *component,
+ QQmlEngine *engine,
+ QQmlContextData *context,
+ QQmlContextData *forContext)
+{
+ QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubationTask);
+ QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine);
+ QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(component);
+
+ incubatorPriv->compiledData = componentPriv->cc;
+ incubatorPriv->compiledData->addref();
+ incubatorPriv->vme.init(
+ context,
+ componentPriv->cc,
+ componentPriv->start,
+ componentPriv->creationContext);
+
+ enginePriv->incubate(*incubationTask, forContext);
+}
+
+void QQmlDelegateModelItem::destroyObject()
+{
+ Q_ASSERT(object);
+ Q_ASSERT(contextData);
+
+ QObjectPrivate *p = QObjectPrivate::get(object);
+ Q_ASSERT(p->declarativeData);
+ QQmlData *data = static_cast<QQmlData*>(p->declarativeData);
+ if (data->ownContext && data->context)
+ data->context->clearContext();
+ object->deleteLater();
+
+ if (attached) {
+ attached->m_cacheItem = 0;
+ attached = 0;
+ }
+
+ contextData->destroy();
+ contextData = 0;
+ object = 0;
+}
+
+QQmlDelegateModelItem *QQmlDelegateModelItem::dataForObject(QObject *object)
+{
+ QObjectPrivate *p = QObjectPrivate::get(object);
+ QQmlContextData *context = p->declarativeData
+ ? static_cast<QQmlData *>(p->declarativeData)->context
+ : 0;
+ for (context = context ? context->parent : 0; context; context = context->parent) {
+ if (QQmlDelegateModelItem *cacheItem = qobject_cast<QQmlDelegateModelItem *>(
+ context->contextObject)) {
+ return cacheItem;
+ }
+ }
+ return 0;
+}
+
+int QQmlDelegateModelItem::groupIndex(Compositor::Group group)
+{
+ if (QQmlDelegateModelPrivate * const model = metaType->model
+ ? QQmlDelegateModelPrivate::get(metaType->model)
+ : 0) {
+ 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(0)
+ , 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);
+ if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) {
+ for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i)
+ m_currentIndex[i] = m_previousIndex[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] = m_previousIndex[i] = it.index[i];
+ }
+
+ if (!cacheItem->metaType->metaObject)
+ cacheItem->metaType->initializeMetaObject();
+
+ QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject;
+ cacheItem->metaType->metaObject->addref();
+}
+
+/*!
+ \qmlattachedproperty int QtQuick2::VisualDataModel::model
+
+ This attached property holds the visual 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 : 0;
+}
+
+/*!
+ \qmlattachedproperty stringlist QtQuick2::VisualDataModel::groups
+
+ This attached property holds the name of VisualDataGroups 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 QtQuick2::VisualDataModel::isUnresolved
+
+ This attached property holds 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 VisualDataGroup::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 QtQuick2::VisualDataModel::inItems
+
+ This attached property holds whether the item belongs to the default \l items VisualDataGroup.
+
+ Changing this property will add or remove the item from the items group.
+
+ It is attached to each instance of the delegate.
+*/
+
+/*!
+ \qmlattachedproperty int QtQuick2::VisualDataModel::itemsIndex
+
+ This attached property holds the index of the item in the default \l items VisualDataGroup.
+
+ It is attached to each instance of the delegate.
+*/
+
+/*!
+ \qmlattachedproperty int QtQuick2::VisualDataModel::inPersistedItems
+
+ This attached property holds whether the item belongs to the \l persistedItems VisualDataGroup.
+
+ 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 QtQuick2::VisualDataModel::persistedItemsIndex
+
+ This attached property holds the index of the item in the \l persistedItems VisualDataGroup.
+
+ 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, 0);
+ }
+ for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) {
+ if (indexChanges & (1 << i))
+ QMetaObject::activate(this, meta, notifierId, 0);
+ }
+
+ if (groupChanges)
+ emit groupsChanged();
+}
+
+//============================================================================
+
+void QQmlDataGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::Group g)
+{
+ Q_ASSERT(!model);
+ model = m;
+ group = g;
+}
+
+bool QQmlDataGroupPrivate::isChangedConnected()
+{
+ Q_Q(QQmlDataGroup);
+ IS_SIGNAL_CONNECTED(q, QQmlDataGroup, changed, (const QQmlV8Handle &,const QQmlV8Handle &));
+}
+
+void QQmlDataGroupPrivate::emitChanges(QV8Engine *engine)
+{
+ Q_Q(QQmlDataGroup);
+ if (isChangedConnected() && !changeSet.isEmpty()) {
+ v8::HandleScope handleScope;
+ v8::Context::Scope contextScope(engine->context());
+ v8::Local<v8::Object> removed = engineData(engine)->array(engine, changeSet.removes());
+ v8::Local<v8::Object> inserted = engineData(engine)->array(engine, changeSet.inserts());
+ emit q->changed(QQmlV8Handle::fromHandle(removed), QQmlV8Handle::fromHandle(inserted));
+ }
+ if (changeSet.difference() != 0)
+ emit q->countChanged();
+}
+
+void QQmlDataGroupPrivate::emitModelUpdated(bool reset)
+{
+ for (QQmlDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it)
+ it->emitModelUpdated(changeSet, reset);
+ changeSet.clear();
+}
+
+void QQmlDataGroupPrivate::createdPackage(int index, QQuickPackage *package)
+{
+ for (QQmlDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it)
+ it->createdPackage(index, package);
+}
+
+void QQmlDataGroupPrivate::initPackage(int index, QQuickPackage *package)
+{
+ for (QQmlDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it)
+ it->initPackage(index, package);
+}
+
+void QQmlDataGroupPrivate::destroyingPackage(QQuickPackage *package)
+{
+ for (QQmlDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it)
+ it->destroyingPackage(package);
+}
+
+/*!
+ \qmltype VisualDataGroup
+ \instantiates QQmlDataGroup
+ \inqmlmodule QtQuick 2
+ \ingroup qtquick-models
+ \brief Encapsulates a filtered set of visual data items
+
+ The VisualDataGroup type provides a means to address the model data of a VisualDataModel's
+ delegate items, as well as sort and filter these delegate items.
+
+ The initial set of instantiable delegate items in a VisualDataModel is represented
+ by its \l {QtQuick2::VisualDataModel::items}{items} group, which normally directly reflects
+ the contents of the model assigned to VisualDataModel::model. This set can be changed to
+ the contents of any other member of VisualDataModel::groups by assigning the \l name of that
+ VisualDataGroup to the VisualDataModel::filterOnGroup property.
+
+ The data of an item in a VisualDataGroup 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.
+
+ Data from models can be supplemented by inserting data directly into a VisualDataGroup
+ 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 be instantiated directly from a VisualDataGroup using the
+ create() function, making it possible to use VisualDataModel 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}
+*/
+
+QQmlDataGroup::QQmlDataGroup(QObject *parent)
+ : QObject(*new QQmlDataGroupPrivate, parent)
+{
+}
+
+QQmlDataGroup::QQmlDataGroup(
+ const QString &name, QQmlDelegateModel *model, int index, QObject *parent)
+ : QObject(*new QQmlDataGroupPrivate, parent)
+{
+ Q_D(QQmlDataGroup);
+ d->name = name;
+ d->setModel(model, Compositor::Group(index));
+}
+
+QQmlDataGroup::~QQmlDataGroup()
+{
+}
+
+/*!
+ \qmlproperty string QtQuick2::VisualDataGroup::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 QQmlDataGroup::name() const
+{
+ Q_D(const QQmlDataGroup);
+ return d->name;
+}
+
+void QQmlDataGroup::setName(const QString &name)
+{
+ Q_D(QQmlDataGroup);
+ if (d->model)
+ return;
+ if (d->name != name) {
+ d->name = name;
+ emit nameChanged();
+ }
+}
+
+/*!
+ \qmlproperty int QtQuick2::VisualDataGroup::count
+
+ This property holds the number of items in the group.
+*/
+
+int QQmlDataGroup::count() const
+{
+ Q_D(const QQmlDataGroup);
+ if (!d->model)
+ return 0;
+ return QQmlDelegateModelPrivate::get(d->model)->m_compositor.count(d->group);
+}
+
+/*!
+ \qmlproperty bool QtQuick2::VisualDataGroup::includeByDefault
+
+ This property holds whether new items are assigned to this group by default.
+*/
+
+bool QQmlDataGroup::defaultInclude() const
+{
+ Q_D(const QQmlDataGroup);
+ return d->defaultInclude;
+}
+
+void QQmlDataGroup::setDefaultInclude(bool include)
+{
+ Q_D(QQmlDataGroup);
+ 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 QtQuick2::VisualDataGroup::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
+ VisualDataModel 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 {QtQuick2::VisualDataModel::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 {QtQuick2::VisualDataModel::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
+ VisualDataModel::model. Returns true if the item is not bound to the model, and false if it is.
+ \endlist
+*/
+
+QQmlV8Handle QQmlDataGroup::get(int index)
+{
+ Q_D(QQmlDataGroup);
+ if (!d->model)
+ return QQmlV8Handle::fromHandle(v8::Undefined());;
+
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
+ if (!model->m_context->isValid()) {
+ return QQmlV8Handle::fromHandle(v8::Undefined());
+ } else if (index < 0 || index >= model->m_compositor.count(d->group)) {
+ qmlInfo(this) << tr("get: index out of range");
+ return QQmlV8Handle::fromHandle(v8::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, model->m_context->engine(), it.modelIndex());
+ if (!cacheItem)
+ return QQmlV8Handle::fromHandle(v8::Undefined());
+ cacheItem->groups = it->flags;
+
+ model->m_cache.insert(it.cacheIndex, cacheItem);
+ model->m_compositor.setFlags(it, 1, Compositor::CacheFlag);
+ }
+
+ if (model->m_cacheMetaType->constructor.IsEmpty())
+ model->m_cacheMetaType->initializeConstructor();
+ v8::Local<v8::Object> handle = model->m_cacheMetaType->constructor->NewInstance();
+ handle->SetExternalResource(cacheItem);
+ ++cacheItem->scriptRef;
+
+ return QQmlV8Handle::fromHandle(handle);
+}
+
+bool QQmlDataGroupPrivate::parseIndex(
+ const v8::Local<v8::Value> &value, int *index, Compositor::Group *group) const
+{
+ if (value->IsInt32()) {
+ *index = value->Int32Value();
+ return true;
+ } else if (value->IsObject()) {
+ v8::Local<v8::Object> object = value->ToObject();
+ QQmlDelegateModelItem * const cacheItem = v8_resource_cast<QQmlDelegateModelItem>(object);
+ if (QQmlDelegateModelPrivate *model = cacheItem && cacheItem->metaType->model
+ ? QQmlDelegateModelPrivate::get(cacheItem->metaType->model)
+ : 0) {
+ *index = model->m_cache.indexOf(cacheItem);
+ *group = Compositor::Cache;
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ \qmlmethod QtQuick2::VisualDataGroup::insert(int index, jsdict data, array groups = undefined)
+ \qmlmethod QtQuick2::VisualDataGroup::insert(jsdict data, var groups = undefined)
+
+ Creates a new entry at \a index in a VisualDataModel with the values from \a data that
+ correspond to roles in the model assigned to VisualDataModel::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 VisualDataModel can later be merged with an existing entry in
+ VisualDataModel::model using the \l resolve() function. This can be used to create placeholder
+ items that are later replaced by actual data.
+*/
+
+void QQmlDataGroup::insert(QQmlV8Function *args)
+{
+ Q_D(QQmlDataGroup);
+ 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;
+ v8::Local<v8::Value> v = (*args)[i];
+ if (d->parseIndex(v, &index, &group)) {
+ if (index < 0 || index > model->m_compositor.count(group)) {
+ qmlInfo(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())
+ groups |= model->m_cacheMetaType->parseGroups((*args)[i]);
+
+ if (v->IsArray()) {
+ return;
+ } else if (v->IsObject()) {
+ model->insert(before, v->ToObject(), groups);
+ model->emitChanges();
+ }
+}
+
+/*!
+ \qmlmethod QtQuick2::VisualDataGroup::create(int index)
+ \qmlmethod QtQuick2::VisualDataGroup::create(int index, jsdict data, array groups = undefined)
+ \qmlmethod QtQuick2::VisualDataGroup::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 {QtQuick2::VisualDataModel::persistedItems}{persistedItems} group. Items in this
+ group remain instantiated when not referenced by any view.
+*/
+
+void QQmlDataGroup::create(QQmlV8Function *args)
+{
+ Q_D(QQmlDataGroup);
+ 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;
+ v8::Local<v8::Value> v = (*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->IsObject()) {
+ int groups = 1 << d->group;
+ if (++i < args->Length())
+ groups |= model->m_cacheMetaType->parseGroups((*args)[i]);
+
+ 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->ToObject(), groups)) {
+ return;
+ }
+ }
+ }
+ if (index < 0 || index >= model->m_compositor.count(group)) {
+ qmlInfo(this) << tr("create: index out of range");
+ return;
+ }
+
+ QObject *object = model->object(group, index, false);
+ 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->returnValue(args->engine()->newQObject(object));
+ model->emitChanges();
+}
+
+/*!
+ \qmlmethod QtQuick2::VisualDataGroup::resolve(int from, int to)
+
+ Binds an unresolved item at \a from to an item in VisualDataModel::model at index \a to.
+
+ Unresolved items are entries whose data has been \l {insert()}{inserted} into a VisualDataGroup
+ instead of being derived from a VisualDataModel::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 VisualDataGroup 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 QQmlDataGroup::resolve(QQmlV8Function *args)
+{
+ Q_D(QQmlDataGroup);
+ 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;
+
+ v8::Local<v8::Value> v = (*args)[0];
+ if (d->parseIndex(v, &from, &fromGroup)) {
+ if (from < 0 || from >= model->m_compositor.count(fromGroup)) {
+ qmlInfo(this) << tr("resolve: from index out of range");
+ return;
+ }
+ } else {
+ qmlInfo(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)) {
+ qmlInfo(this) << tr("resolve: to index out of range");
+ return;
+ }
+ } else {
+ qmlInfo(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()) {
+ qmlInfo(this) << tr("resolve: from is not an unresolved item");
+ return;
+ }
+ if (!toIt->list) {
+ qmlInfo(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>() << Compositor::Remove(fromIt, 1, unresolvedFlags, 0),
+ QVector<Compositor::Insert>() << Compositor::Insert(toIt, 1, unresolvedFlags, 0));
+ model->itemsInserted(
+ QVector<Compositor::Insert>() << Compositor::Insert(toIt, 1, (resolvedFlags & ~unresolvedFlags) | Compositor::CacheFlag));
+ toIt.incrementIndexes(1, resolvedFlags | unresolvedFlags);
+ model->itemsRemoved(QVector<Compositor::Remove>() << 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 QtQuick2::VisualDataGroup::remove(int index, int count)
+
+ Removes \a count items starting at \a index from the group.
+*/
+
+void QQmlDataGroup::remove(QQmlV8Function *args)
+{
+ Q_D(QQmlDataGroup);
+ if (!d->model)
+ return;
+ Compositor::Group group = d->group;
+ int index = -1;
+ int count = 1;
+
+ if (args->Length() == 0)
+ return;
+
+ int i = 0;
+ v8::Local<v8::Value> v = (*args)[i];
+ if (!d->parseIndex(v, &index, &group)) {
+ qmlInfo(this) << tr("remove: invalid index");
+ return;
+ }
+
+ if (++i < args->Length()) {
+ v = (*args)[i];
+ if (v->IsInt32())
+ count = v->Int32Value();
+ }
+
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
+ if (index < 0 || index >= model->m_compositor.count(group)) {
+ qmlInfo(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]) {
+ qmlInfo(this) << tr("remove: invalid count");
+ } else {
+ model->removeGroups(it, count, d->group, 1 << d->group);
+ }
+ }
+}
+
+bool QQmlDataGroupPrivate::parseGroupArgs(
+ QQmlV8Function *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;
+ v8::Local<v8::Value> v = (*args)[i];
+ if (!parseIndex(v, index, group))
+ return false;
+
+ v = (*args)[++i];
+ if (v->IsInt32()) {
+ *count = v->Int32Value();
+
+ if (++i == args->Length())
+ return false;
+ v = (*args)[i];
+ }
+
+ *groups = QQmlDelegateModelPrivate::get(model)->m_cacheMetaType->parseGroups(v);
+
+ return true;
+}
+
+/*!
+ \qmlmethod QtQuick2::VisualDataGroup::addGroups(int index, int count, stringlist groups)
+
+ Adds \a count items starting at \a index to \a groups.
+*/
+
+void QQmlDataGroup::addGroups(QQmlV8Function *args)
+{
+ Q_D(QQmlDataGroup);
+ 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)) {
+ qmlInfo(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]) {
+ qmlInfo(this) << tr("addGroups: invalid count");
+ } else {
+ model->addGroups(it, count, d->group, groups);
+ }
+ }
+}
+
+/*!
+ \qmlmethod QtQuick2::VisualDataGroup::removeGroups(int index, int count, stringlist groups)
+
+ Removes \a count items starting at \a index from \a groups.
+*/
+
+void QQmlDataGroup::removeGroups(QQmlV8Function *args)
+{
+ Q_D(QQmlDataGroup);
+ 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)) {
+ qmlInfo(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]) {
+ qmlInfo(this) << tr("removeGroups: invalid count");
+ } else {
+ model->removeGroups(it, count, d->group, groups);
+ }
+ }
+}
+
+/*!
+ \qmlmethod QtQuick2::VisualDataGroup::setGroups(int index, int count, stringlist groups)
+
+ Sets the \a groups \a count items starting at \a index belong to.
+*/
+
+void QQmlDataGroup::setGroups(QQmlV8Function *args)
+{
+ Q_D(QQmlDataGroup);
+ 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)) {
+ qmlInfo(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]) {
+ qmlInfo(this) << tr("setGroups: invalid count");
+ } else {
+ model->setGroups(it, count, d->group, groups);
+ }
+ }
+}
+
+/*!
+ \qmlmethod QtQuick2::VisualDataGroup::setGroups(int index, int count, stringlist groups)
+
+ Sets the \a groups \a count items starting at \a index belong to.
+*/
+
+/*!
+ \qmlmethod QtQuick2::VisualDataGroup::move(var from, var to, int count)
+
+ Moves \a count at \a from in a group \a to a new position.
+*/
+
+void QQmlDataGroup::move(QQmlV8Function *args)
+{
+ Q_D(QQmlDataGroup);
+
+ if (args->Length() < 2)
+ return;
+
+ Compositor::Group fromGroup = d->group;
+ Compositor::Group toGroup = d->group;
+ int from = -1;
+ int to = -1;
+ int count = 1;
+
+ if (!d->parseIndex((*args)[0], &from, &fromGroup)) {
+ qmlInfo(this) << tr("move: invalid from index");
+ return;
+ }
+
+ if (!d->parseIndex((*args)[1], &to, &toGroup)) {
+ qmlInfo(this) << tr("move: invalid to index");
+ return;
+ }
+
+ if (args->Length() > 2) {
+ v8::Local<v8::Value> v = (*args)[2];
+ if (v->IsInt32())
+ count = v->Int32Value();
+ }
+
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
+
+ if (count < 0) {
+ qmlInfo(this) << tr("move: invalid count");
+ } else if (from < 0 || from + count > model->m_compositor.count(fromGroup)) {
+ qmlInfo(this) << tr("move: from index out of range");
+ } else if (!model->m_compositor.verifyMoveTo(fromGroup, from, toGroup, to, count, d->group)) {
+ qmlInfo(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 QtQuick2::VisualDataGroup::onChanged(array removed, array inserted)
+
+ This handler is called 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.
+*/
+
+//============================================================================
+
+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) {
+ QQmlDataGroupPrivate::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) {
+ qmlInfo(this) << tr("The group of a VisualDataModel 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;
+ QQmlDataGroupPrivate::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;
+ }
+ }
+
+ QQmlDataGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this);
+ if (m_compositorGroup != previousGroup) {
+ QVector<QQmlChangeSet::Remove> removes;
+ QVector<QQmlChangeSet::Insert> 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;
+ QQmlDataGroupPrivate::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, bool asynchronous)
+{
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
+
+ if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) {
+ qWarning() << "VisualDataModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup);
+ return 0;
+ }
+
+ QObject *object = model->object(m_compositorGroup, index, asynchronous);
+
+ if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) {
+ QObject *part = package->part(m_part);
+ if (!part)
+ return 0;
+ m_packaged.insertMulti(part, package);
+ return part;
+ }
+
+ model->release(object);
+ if (!model->m_delegateValidated) {
+ if (object)
+ qmlInfo(model->m_delegate) << tr("Delegate component must be Package type.");
+ model->m_delegateValidated = true;
+ }
+
+ return 0;
+}
+
+QQmlInstanceModel::ReleaseFlags QQmlPartsModel::release(QObject *item)
+{
+ QQmlInstanceModel::ReleaseFlags flags = 0;
+
+ 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(QList<QByteArray> roles)
+{
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
+ model->m_adaptorModel.replaceWatchedRoles(m_watchedRoles, roles);
+ m_watchedRoles = roles;
+}
+
+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)
+{
+ 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)
+{
+ emit modelUpdated(changeSet, reset);
+ if (changeSet.difference() != 0)
+ emit countChanged();
+}
+
+//============================================================================
+
+v8::Handle<v8::Value> get_change_index(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ return info.This()->GetInternalField(0);
+}
+
+v8::Handle<v8::Value> get_change_count(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ return info.This()->GetInternalField(1);
+}
+
+v8::Handle<v8::Value> get_change_moveId(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ return info.This()->GetInternalField(2);
+}
+
+class QQmlDataGroupChangeArray : public QV8ObjectResource
+{
+ V8_RESOURCE_TYPE(ChangeSetArrayType)
+public:
+ QQmlDataGroupChangeArray(QV8Engine *engine)
+ : QV8ObjectResource(engine)
+ {
+ }
+
+ virtual quint32 count() const = 0;
+ virtual const QQmlChangeSet::Change &at(int index) const = 0;
+
+ static v8::Handle<v8::Value> get_change(quint32 index, const v8::AccessorInfo &info)
+ {
+ QQmlDataGroupChangeArray *array = v8_resource_cast<QQmlDataGroupChangeArray>(info.This());
+ V8ASSERT_TYPE(array, "Not a valid change array");
+
+ if (index >= array->count())
+ return v8::Undefined();
+
+ const QQmlChangeSet::Change &change = array->at(index);
+
+ v8::Local<v8::Object> object = engineData(array->engine)->constructorChange->NewInstance();
+ object->SetInternalField(0, v8::Int32::New(change.index));
+ object->SetInternalField(1, v8::Int32::New(change.count));
+ if (change.isMove())
+ object->SetInternalField(2, v8::Int32::New(change.moveId));
+
+ return object;
+ }
+
+ static v8::Handle<v8::Value> get_length(v8::Local<v8::String>, const v8::AccessorInfo &info)
+ {
+ QQmlDataGroupChangeArray *array = v8_resource_cast<QQmlDataGroupChangeArray>(info.This());
+ V8ASSERT_TYPE(array, "Not a valid change array");
+
+ return v8::Integer::New(array->count());
+ }
+
+ static v8::Local<v8::Function> constructor()
+ {
+ v8::Local<v8::FunctionTemplate> changeArray = v8::FunctionTemplate::New();
+ changeArray->InstanceTemplate()->SetHasExternalResource(true);
+ changeArray->InstanceTemplate()->SetIndexedPropertyHandler(get_change);
+ changeArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), get_length);
+ return changeArray->GetFunction();
+ }
+};
+
+class QQmlDataGroupRemoveArray : public QQmlDataGroupChangeArray
+{
+public:
+ QQmlDataGroupRemoveArray(QV8Engine *engine, const QVector<QQmlChangeSet::Remove> &changes)
+ : QQmlDataGroupChangeArray(engine)
+ , changes(changes)
+ {
+ }
+
+ quint32 count() const { return changes.count(); }
+ const QQmlChangeSet::Change &at(int index) const { return changes.at(index); }
+
+private:
+ QVector<QQmlChangeSet::Remove> changes;
+};
+
+class QQmlDataGroupInsertArray : public QQmlDataGroupChangeArray
+{
+public:
+ QQmlDataGroupInsertArray(QV8Engine *engine, const QVector<QQmlChangeSet::Insert> &changes)
+ : QQmlDataGroupChangeArray(engine)
+ , changes(changes)
+ {
+ }
+
+ quint32 count() const { return changes.count(); }
+ const QQmlChangeSet::Change &at(int index) const { return changes.at(index); }
+
+private:
+ QVector<QQmlChangeSet::Insert> changes;
+};
+
+QQmlDelegateModelEngineData::QQmlDelegateModelEngineData(QV8Engine *)
+{
+ strings = qPersistentNew(v8::Array::New(StringCount));
+ strings->Set(Model, v8::String::New("model"));
+ strings->Set(Groups, v8::String::New("groups"));
+ strings->Set(IsUnresolved, v8::String::New("isUnresolved"));
+ strings->Set(ItemsIndex, v8::String::New("itemsIndex"));
+ strings->Set(PersistedItemsIndex, v8::String::New("persistedItemsIndex"));
+ strings->Set(InItems, v8::String::New("inItems"));
+ strings->Set(InPersistedItems, v8::String::New("inPersistedItems"));
+
+ v8::Local<v8::FunctionTemplate> change = v8::FunctionTemplate::New();
+ change->InstanceTemplate()->SetAccessor(v8::String::New("index"), get_change_index);
+ change->InstanceTemplate()->SetAccessor(v8::String::New("count"), get_change_count);
+ change->InstanceTemplate()->SetAccessor(v8::String::New("moveId"), get_change_moveId);
+ change->InstanceTemplate()->SetInternalFieldCount(3);
+ constructorChange = qPersistentNew(change->GetFunction());
+ constructorChangeArray = qPersistentNew(QQmlDataGroupChangeArray::constructor());
+}
+
+QQmlDelegateModelEngineData::~QQmlDelegateModelEngineData()
+{
+ qPersistentDispose(strings);
+ qPersistentDispose(constructorChange);
+ qPersistentDispose(constructorChangeArray);
+}
+
+v8::Local<v8::Object> QQmlDelegateModelEngineData::array(
+ QV8Engine *engine, const QVector<QQmlChangeSet::Remove> &changes)
+{
+ v8::Local<v8::Object> array = constructorChangeArray->NewInstance();
+ array->SetExternalResource(new QQmlDataGroupRemoveArray(engine, changes));
+ return array;
+}
+
+v8::Local<v8::Object> QQmlDelegateModelEngineData::array(
+ QV8Engine *engine, const QVector<QQmlChangeSet::Insert> &changes)
+{
+ v8::Local<v8::Object> array = constructorChangeArray->NewInstance();
+ array->SetExternalResource(new QQmlDataGroupInsertArray(engine, changes));
+ return array;
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/qml/items/qqmldelegatemodel_p.h b/src/qml/items/qqmldelegatemodel_p.h
new file mode 100644
index 0000000000..20cc228043
--- /dev/null
+++ b/src/qml/items/qqmldelegatemodel_p.h
@@ -0,0 +1,238 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLDATAMODEL_P_H
+#define QQMLDATAMODEL_P_H
+
+#include <private/qtqmlglobal_p.h>
+#include <private/qqmllistcompositor_p.h>
+#include <private/qqmlobjectmodel_p.h>
+
+#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qstringlist.h>
+
+#include <private/qv8engine_p.h>
+#include <private/qqmlglobal_p.h>
+
+QT_BEGIN_HEADER
+
+Q_DECLARE_METATYPE(QModelIndex)
+
+QT_BEGIN_NAMESPACE
+
+class QQmlChangeSet;
+class QQmlComponent;
+class QQuickPackage;
+class QQmlV8Function;
+class QQmlDataGroup;
+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(QQmlDataGroup *items READ items CONSTANT) //TODO : worth renaming?
+ Q_PROPERTY(QQmlDataGroup *persistedItems READ persistedItems CONSTANT)
+ Q_PROPERTY(QQmlListProperty<QQmlDataGroup> 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=0);
+ virtual ~QQmlDelegateModel();
+
+ void classBegin();
+ void componentComplete();
+
+ 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;
+ bool isValid() const { return delegate() != 0; }
+ QObject *object(int index, bool asynchronous=false);
+ ReleaseFlags release(QObject *object);
+ void cancel(int index);
+ virtual QString stringValue(int index, const QString &role);
+ virtual void setWatchedRoles(QList<QByteArray> roles);
+
+ int indexOf(QObject *object, QObject *objectContext) const;
+
+ QString filterGroup() const;
+ void setFilterGroup(const QString &group);
+ void resetFilterGroup();
+
+ QQmlDataGroup *items();
+ QQmlDataGroup *persistedItems();
+ QQmlListProperty<QQmlDataGroup> groups();
+ QObject *parts();
+
+ bool event(QEvent *);
+
+ 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();
+
+private:
+ Q_DISABLE_COPY(QQmlDelegateModel)
+};
+
+class QQmlDataGroupPrivate;
+class Q_QML_PRIVATE_EXPORT QQmlDataGroup : 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:
+ QQmlDataGroup(QObject *parent = 0);
+ QQmlDataGroup(const QString &name, QQmlDelegateModel *model, int compositorType, QObject *parent = 0);
+ ~QQmlDataGroup();
+
+ QString name() const;
+ void setName(const QString &name);
+
+ int count() const;
+
+ bool defaultInclude() const;
+ void setDefaultInclude(bool include);
+
+ Q_INVOKABLE QQmlV8Handle get(int index);
+
+public Q_SLOTS:
+ void insert(QQmlV8Function *);
+ void create(QQmlV8Function *);
+ void resolve(QQmlV8Function *);
+ void remove(QQmlV8Function *);
+ void addGroups(QQmlV8Function *);
+ void removeGroups(QQmlV8Function *);
+ void setGroups(QQmlV8Function *);
+ void move(QQmlV8Function *);
+
+Q_SIGNALS:
+ void countChanged();
+ void nameChanged();
+ void defaultIncludeChanged();
+ void changed(const QQmlV8Handle &removed, const QQmlV8Handle &inserted);
+private:
+ Q_DECLARE_PRIVATE(QQmlDataGroup)
+};
+
+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 setCacheItem(QQmlDelegateModelItem *item);
+
+ QQmlDelegateModel *model() const;
+
+ QStringList groups() const;
+ void setGroups(const QStringList &groups);
+
+ bool isUnresolved() const;
+
+ void emitChanges();
+
+ void emitUnresolvedChanged() { 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(QQmlDataGroup)
+
+QT_END_HEADER
+
+#endif // QQMLDATAMODEL_P_H
diff --git a/src/qml/items/qqmldelegatemodel_p_p.h b/src/qml/items/qqmldelegatemodel_p_p.h
new file mode 100644
index 0000000000..0ac7285cab
--- /dev/null
+++ b/src/qml/items/qqmldelegatemodel_p_p.h
@@ -0,0 +1,411 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLDATAMODEL_P_P_H
+#define QQMLDATAMODEL_P_P_H
+
+#include "qqmldelegatemodel_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_BEGIN_NAMESPACE
+
+typedef QQmlListCompositor Compositor;
+
+class QQmlDelegateModelAttachedMetaObject;
+
+class QQmlDelegateModelItemMetaType : public QQmlRefCount
+{
+public:
+ QQmlDelegateModelItemMetaType(QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames);
+ ~QQmlDelegateModelItemMetaType();
+
+ void initializeMetaObject();
+ void initializeConstructor();
+
+ int parseGroups(const QStringList &groupNames) const;
+ int parseGroups(const v8::Local<v8::Value> &groupNames) const;
+
+ static void release_index(v8::Persistent<v8::Value> object, void *parameter);
+ static void release_model(v8::Persistent<v8::Value> object, void *parameter);
+
+ static v8::Handle<v8::Value> get_model(v8::Local<v8::String>, const v8::AccessorInfo &info);
+ static v8::Handle<v8::Value> get_groups(v8::Local<v8::String>, const v8::AccessorInfo &info);
+ static void set_groups(
+ v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info);
+ static v8::Handle<v8::Value> get_member(v8::Local<v8::String>, const v8::AccessorInfo &info);
+ static void set_member(
+ v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info);
+ static v8::Handle<v8::Value> get_index(v8::Local<v8::String>, const v8::AccessorInfo &info);
+
+ QQmlGuard<QQmlDelegateModel> model;
+ const int groupCount;
+ QV8Engine * const v8Engine;
+ QQmlDelegateModelAttachedMetaObject *metaObject;
+ const QStringList groupNames;
+ v8::Persistent<v8::ObjectTemplate> constructor;
+};
+
+class QQmlAdaptorModel;
+class QQDMIncubationTask;
+
+class QQmlDelegateModelItem : public QObject, public QV8ObjectResource
+{
+ Q_OBJECT
+ Q_PROPERTY(int index READ modelIndex NOTIFY modelIndexChanged)
+ Q_PROPERTY(QObject *model READ modelObject CONSTANT)
+ V8_RESOURCE_TYPE(VisualDataItemType)
+public:
+ QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex);
+ ~QQmlDelegateModelItem();
+
+ void referenceObject() { ++objectRef; }
+ bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); }
+ bool isObjectReferenced() const { return objectRef != 0 || (groups & Compositor::PersistedFlag); }
+
+ bool isReferenced() const {
+ return scriptRef
+ || incubationTask
+ || ((groups & Compositor::UnresolvedFlag) && (groups & Compositor::GroupMask));
+ }
+
+ void Dispose();
+
+ QObject *modelObject() { return this; }
+
+ void incubateObject(
+ QQmlComponent *component,
+ QQmlEngine *engine,
+ QQmlContextData *context,
+ QQmlContextData *forContext);
+ void destroyObject();
+
+ static QQmlDelegateModelItem *dataForObject(QObject *object);
+
+ int groupIndex(Compositor::Group group);
+
+ int modelIndex() const { return index; }
+ void setModelIndex(int idx) { index = idx; emit modelIndexChanged(); }
+
+ virtual v8::Handle<v8::Value> get() { return engine->newQObject(this); }
+
+ virtual void setValue(const QString &role, const QVariant &value) { Q_UNUSED(role); Q_UNUSED(value); }
+ virtual bool resolveIndex(const QQmlAdaptorModel &, int) { return false; }
+
+ QQmlDelegateModelItemMetaType * const metaType;
+ QQmlContextData *contextData;
+ QObject *object;
+ QQmlDelegateModelAttached *attached;
+ QQDMIncubationTask *incubationTask;
+ int objectRef;
+ int scriptRef;
+ int groups;
+ int index;
+
+
+Q_SIGNALS:
+ void modelIndexChanged();
+
+protected:
+ void objectDestroyed(QObject *);
+};
+
+
+class QQmlDelegateModelPrivate;
+class QQDMIncubationTask : public QQmlIncubator
+{
+public:
+ QQDMIncubationTask(QQmlDelegateModelPrivate *l, IncubationMode mode)
+ : QQmlIncubator(mode)
+ , incubating(0)
+ , vdm(l) {}
+
+ virtual void statusChanged(Status);
+ virtual void setInitialState(QObject *);
+
+ QQmlDelegateModelItem *incubating;
+ QQmlDelegateModelPrivate *vdm;
+ int index[QQmlListCompositor::MaximumGroupCount];
+};
+
+
+class QQmlDataGroupEmitter
+{
+public:
+ virtual ~QQmlDataGroupEmitter() {}
+ 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<QQmlDataGroupEmitter, &QQmlDataGroupEmitter::emitterNode> QQmlDataGroupEmitterList;
+
+class QQmlDataGroupPrivate : public QObjectPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QQmlDataGroup)
+
+ QQmlDataGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {}
+
+ static QQmlDataGroupPrivate *get(QQmlDataGroup *group) {
+ return static_cast<QQmlDataGroupPrivate *>(QObjectPrivate::get(group)); }
+
+ void setModel(QQmlDelegateModel *model, Compositor::Group group);
+ bool isChangedConnected();
+ void emitChanges(QV8Engine *engine);
+ void emitModelUpdated(bool reset);
+
+ void createdPackage(int index, QQuickPackage *package);
+ void initPackage(int index, QQuickPackage *package);
+ void destroyingPackage(QQuickPackage *package);
+
+ bool parseIndex(const v8::Local<v8::Value> &value, int *index, Compositor::Group *group) const;
+ bool parseGroupArgs(
+ QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const;
+
+ Compositor::Group group;
+ QQmlGuard<QQmlDelegateModel> model;
+ QQmlDataGroupEmitterList emitters;
+ QQmlChangeSet changeSet;
+ QString name;
+ bool defaultInclude;
+};
+
+class QQmlDelegateModelParts;
+
+class QQmlDelegateModelPrivate : public QObjectPrivate, public QQmlDataGroupEmitter
+{
+ 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);
+
+ QObject *object(Compositor::Group group, int index, bool asynchronous);
+ 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) {
+ emit q_func()->createdItem(incubationTask->index[m_compositorGroup], item); }
+ void emitInitItem(QQDMIncubationTask *incubationTask, QObject *item) {
+ emit q_func()->initItem(incubationTask->index[m_compositorGroup], item); }
+ void emitDestroyingPackage(QQuickPackage *package);
+ void emitDestroyingItem(QObject *item) { emit q_func()->destroyingItem(item); }
+ 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::Insert>, Compositor::MaximumGroupCount> *translatedInserts,
+ QHash<int, QList<QQmlDelegateModelItem *> > *movedItems = 0);
+ void itemsInserted(const QVector<Compositor::Insert> &inserts);
+ void itemsRemoved(
+ const QVector<Compositor::Remove> &removes,
+ QVarLengthArray<QVector<QQmlChangeSet::Remove>, Compositor::MaximumGroupCount> *translatedRemoves,
+ QHash<int, QList<QQmlDelegateModelItem *> > *movedItems = 0);
+ 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);
+ template <typename T> static v8::Local<v8::Array> buildChangeList(const QVector<T> &changes);
+ void emitChanges();
+ void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset);
+
+ bool insert(Compositor::insert_iterator &before, const v8::Local<v8::Object> &object, int groups);
+
+ static void group_append(QQmlListProperty<QQmlDataGroup> *property, QQmlDataGroup *group);
+ static int group_count(QQmlListProperty<QQmlDataGroup> *property);
+ static QQmlDataGroup *group_at(QQmlListProperty<QQmlDataGroup> *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;
+ QQmlComponent *m_delegate;
+ QQmlDelegateModelItemMetaType *m_cacheMetaType;
+ QQmlContext *m_context;
+ QQmlDelegateModelParts *m_parts;
+ QQmlDataGroupEmitterList 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;
+
+ union {
+ struct {
+ QQmlDataGroup *m_cacheItems;
+ QQmlDataGroup *m_items;
+ QQmlDataGroup *m_persistedItems;
+ };
+ QQmlDataGroup *m_groups[Compositor::MaximumGroupCount];
+ };
+};
+
+class QQmlPartsModel : public QQmlInstanceModel, public QQmlDataGroupEmitter
+{
+ Q_OBJECT
+ Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup)
+public:
+ QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent = 0);
+ ~QQmlPartsModel();
+
+ QString filterGroup() const;
+ void setFilterGroup(const QString &group);
+ void resetFilterGroup();
+ void updateFilterGroup();
+ void updateFilterGroup(Compositor::Group group, const QQmlChangeSet &changeSet);
+
+ int count() const;
+ bool isValid() const;
+ QObject *object(int index, bool asynchronous=false);
+ ReleaseFlags release(QObject *item);
+ QString stringValue(int index, const QString &role);
+ QList<QByteArray> watchedRoles() const { return m_watchedRoles; }
+ void setWatchedRoles(QList<QByteArray> roles);
+
+ int indexOf(QObject *item, QObject *objectContext) const;
+
+ void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset);
+
+ void createdPackage(int index, QQuickPackage *package);
+ void initPackage(int index, QQuickPackage *package);
+ void destroyingPackage(QQuickPackage *package);
+
+Q_SIGNALS:
+ void filterGroupChanged();
+
+private:
+ QQmlDelegateModel *m_model;
+ QHash<QObject *, QQuickPackage *> m_packaged;
+ QString m_part;
+ QString m_filterGroup;
+ QList<QByteArray> m_watchedRoles;
+ Compositor::Group m_compositorGroup;
+ bool m_inheritGroup;
+};
+
+class QMetaPropertyBuilder;
+
+class QQmlDelegateModelPartsMetaObject : public QQmlOpenMetaObject
+{
+public:
+ QQmlDelegateModelPartsMetaObject(QObject *parent)
+ : QQmlOpenMetaObject(parent) {}
+
+ virtual void propertyCreated(int, QMetaPropertyBuilder &);
+ virtual QVariant initialValue(int);
+};
+
+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 *);
+ int metaCall(QObject *, QMetaObject::Call, int _id, void **);
+
+private:
+ QQmlDelegateModelItemMetaType * const metaType;
+ QMetaObject * const metaObject;
+ const int memberPropertyOffset;
+ const int indexPropertyOffset;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/items/qqmlobjectmodel.cpp b/src/qml/items/qqmlobjectmodel.cpp
new file mode 100644
index 0000000000..913fa79719
--- /dev/null
+++ b/src/qml/items/qqmlobjectmodel.cpp
@@ -0,0 +1,251 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlobjectmodel_p.h"
+
+#include <QtCore/qcoreapplication.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+
+#include <private/qqmlchangeset_p.h>
+#include <private/qqmlglobal_p.h>
+#include <private/qobject_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() {}
+
+ static void children_append(QQmlListProperty<QObject> *prop, QObject *item) {
+ static_cast<QQmlObjectModelPrivate *>(prop->data)->children.append(Item(item));
+ static_cast<QQmlObjectModelPrivate *>(prop->data)->itemAppended();
+ static_cast<QQmlObjectModelPrivate *>(prop->data)->emitChildrenChanged();
+ }
+
+ 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)->itemCleared(static_cast<QQmlObjectModelPrivate *>(prop->data)->children);
+ static_cast<QQmlObjectModelPrivate *>(prop->data)->children.clear();
+ static_cast<QQmlObjectModelPrivate *>(prop->data)->emitChildrenChanged();
+ }
+
+ void itemAppended() {
+ Q_Q(QQmlObjectModel);
+ QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.last().item);
+ attached->setIndex(children.count()-1);
+ QQmlChangeSet changeSet;
+ changeSet.insert(children.count() - 1, 1);
+ emit q->modelUpdated(changeSet, false);
+ emit q->countChanged();
+ }
+
+ void itemCleared(const QList<Item> &children) {
+ Q_Q(QQmlObjectModel);
+ foreach (const Item &child, children)
+ emit q->destroyingItem(child.item);
+ emit q->countChanged();
+ }
+
+ void emitChildrenChanged() {
+ Q_Q(QQmlObjectModel);
+ emit q->childrenChanged();
+ }
+
+ int indexOf(QObject *item) const {
+ for (int i = 0; i < children.count(); ++i)
+ if (children.at(i).item == item)
+ return i;
+ return -1;
+ }
+
+
+ QList<Item> children;
+};
+
+
+/*!
+ \qmltype VisualItemModel
+ \instantiates QQmlObjectModel
+ \inqmlmodule QtQuick 2
+ \ingroup qtquick-models
+ \brief Defines items to be used added to a view
+
+ A VisualItemModel contains the visual items to be used in a view.
+ When a VisualItemModel is used in a view, the view does not require
+ a delegate since the VisualItemModel already contains the visual
+ delegate (items).
+
+ An item can determine its index within the
+ model via the \l{VisualItemModel::index}{index} attached property.
+
+ The example below places three colored rectangles in a ListView.
+ \code
+ import QtQuick 2.0
+
+ Rectangle {
+ VisualItemModel {
+ 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 visualitemmodel.png
+
+ \sa {quick/modelviews/visualitemmodel}{VisualItemModel example}
+*/
+QQmlObjectModel::QQmlObjectModel(QObject *parent)
+ : QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent)
+{
+}
+
+/*!
+ \qmlattachedproperty int QtQuick2::VisualItemModel::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 QtQuick2::VisualItemModel::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, bool)
+{
+ 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 0;
+}
+
+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();
+}
+
+int QQmlObjectModel::indexOf(QObject *item, QObject *) const
+{
+ Q_D(const QQmlObjectModel);
+ return d->indexOf(item);
+}
+
+QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj)
+{
+ return QQmlObjectModelAttached::properties(obj);
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/qml/items/qqmlobjectmodel_p.h b/src/qml/items/qqmlobjectmodel_p.h
new file mode 100644
index 0000000000..5ec57bddc5
--- /dev/null
+++ b/src/qml/items/qqmlobjectmodel_p.h
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLINSTANCEMODEL_P_H
+#define QQMLINSTANCEMODEL_P_H
+
+#include <private/qtqmlglobal_p.h>
+#include <QtQml/qqml.h>
+#include <QtCore/qobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QObject;
+class QQmlChangeSet;
+
+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, bool asynchronous=false) = 0;
+ virtual ReleaseFlags release(QObject *object) = 0;
+ virtual void cancel(int) {}
+ virtual QString stringValue(int, const QString &) = 0;
+ virtual void setWatchedRoles(QList<QByteArray> roles) = 0;
+
+ virtual int indexOf(QObject *object, QObject *objectContext) const = 0;
+
+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 = 0)
+ : 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=0);
+ virtual ~QQmlObjectModel() {}
+
+ virtual int count() const;
+ virtual bool isValid() const;
+ virtual QObject *object(int index, bool asynchronous=false);
+ virtual ReleaseFlags release(QObject *object);
+ virtual QString stringValue(int index, const QString &role);
+ virtual void setWatchedRoles(QList<QByteArray>) {}
+
+ virtual int indexOf(QObject *object, QObject *objectContext) const;
+
+ QQmlListProperty<QObject> children();
+
+ static QQmlObjectModelAttached *qmlAttachedProperties(QObject *obj);
+
+Q_SIGNALS:
+ void childrenChanged();
+
+private:
+ Q_DISABLE_COPY(QQmlObjectModel)
+};
+
+class QQmlObjectModelAttached : public QObject
+{
+ Q_OBJECT
+
+public:
+ QQmlObjectModelAttached(QObject *parent)
+ : QObject(parent), m_index(0) {}
+ ~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;
+ 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)
+
+QT_END_HEADER
+
+#endif // QQMLINSTANCEMODEL_P_H
diff --git a/src/qml/items/qquickpackage.cpp b/src/qml/items/qquickpackage.cpp
new file mode 100644
index 0000000000..e885524b27
--- /dev/null
+++ b/src/qml/items/qquickpackage.cpp
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $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 2
+ \ingroup qtquick-views
+ \brief Specifies a collection of named items
+
+ The Package class is used in conjunction with
+ VisualDataModel 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 quick/views/package/Delegate.qml 0
+
+ These named items are used as the delegates by the two views who
+ reference the special \l{VisualDataModel::parts} property to select
+ a model which provides the chosen delegate.
+
+ \snippet quick/views/package/view.qml 0
+
+ \sa {quick/views/package}{Package example}, {quick/demos/photoviewer}{Photo Viewer example}, QtQml
+*/
+
+/*!
+ \qmlattachedproperty string QtQuick2::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 *) {
+ // 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 0;
+}
+
+QQuickPackageAttached *QQuickPackage::qmlAttachedProperties(QObject *o)
+{
+ return new QQuickPackageAttached(o);
+}
+
+
+
+QT_END_NAMESPACE
diff --git a/src/qml/items/qquickpackage_p.h b/src/qml/items/qquickpackage_p.h
new file mode 100644
index 0000000000..a777ff4a7e
--- /dev/null
+++ b/src/qml/items/qquickpackage_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPACKAGE_H
+#define QQUICKPACKAGE_H
+
+#include <qqml.h>
+
+QT_BEGIN_HEADER
+
+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=0);
+ 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)
+
+QT_END_HEADER
+
+#endif // QQUICKPACKAGE_H
diff --git a/src/qml/qml.pro b/src/qml/qml.pro
index 4f55b83e14..08aa369ac3 100644
--- a/src/qml/qml.pro
+++ b/src/qml/qml.pro
@@ -25,3 +25,4 @@ include(util/util.pri)
include(qml/qml.pri)
include(debugger/debugger.pri)
include(animations/animations.pri)
+include(items/items.pri)
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 600f526c45..5657cacf7d 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -92,6 +92,9 @@
#include "qqmlbind_p.h"
#include "qqmlconnections_p.h"
#include "qqmltimer_p.h"
+#include <private/qquickpackage_p.h>
+#include <private/qqmldelegatemodel_p.h>
+#include <private/qqmlobjectmodel_p.h>
#ifdef Q_OS_WIN // for %APPDATA%
#include <qt_windows.h>
@@ -189,6 +192,11 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int
void QQmlEnginePrivate::registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor)
{
qmlRegisterType<QQuickWorkerScript>(uri, versionMajor, versionMinor, "WorkerScript");
+ qmlRegisterType<QQuickPackage>(uri, versionMajor, versionMinor, "Package");
+ qmlRegisterType<QQmlDelegateModel>(uri, versionMajor, versionMinor, "VisualDataModel");
+ qmlRegisterType<QQmlDataGroup>(uri, versionMajor, versionMinor, "VisualDataGroup");
+ qmlRegisterType<QQmlObjectModel>(uri, versionMajor, versionMinor, "VisualItemModel");
+ qmlRegisterType<QQmlInstanceModel>();
}
void QQmlEnginePrivate::defineQtQuick2Module()
diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp
new file mode 100644
index 0000000000..5b6ef79338
--- /dev/null
+++ b/src/qml/util/qqmladaptormodel.cpp
@@ -0,0 +1,977 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmladaptormodel_p.h"
+
+#include <private/qqmldelegatemodel_p_p.h>
+#include <private/qmetaobjectbuilder_p.h>
+#include <private/qqmlproperty_p.h>
+#include <private/qv8engine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlAdaptorModelEngineData : public QV8Engine::Deletable
+{
+public:
+ enum
+ {
+ Index,
+ ModelData,
+ HasModelChildren,
+ StringCount
+ };
+
+ QQmlAdaptorModelEngineData(QV8Engine *engine);
+ ~QQmlAdaptorModelEngineData();
+
+ v8::Local<v8::String> index() { return strings->Get(Index)->ToString(); }
+ v8::Local<v8::String> modelData() { return strings->Get(ModelData)->ToString(); }
+ v8::Local<v8::String> hasModelChildren() { return strings->Get(HasModelChildren)->ToString(); }
+
+ v8::Persistent<v8::Function> constructorListItem;
+ v8::Persistent<v8::Array> strings;
+};
+
+V8_DEFINE_EXTENSION(QQmlAdaptorModelEngineData, engineData)
+
+static v8::Handle<v8::Value> get_index(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QQmlDelegateModelItem *data = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE(data, "Not a valid VisualData object");
+
+ return v8::Int32::New(data->index);
+}
+
+template <typename T, typename M> static void setModelDataType(QMetaObjectBuilder *builder, M *metaType)
+{
+ builder->setFlags(QMetaObjectBuilder::DynamicMetaObject);
+ builder->setClassName(T::staticMetaObject.className());
+ builder->setSuperClass(&T::staticMetaObject);
+ metaType->propertyOffset = T::staticMetaObject.propertyCount();
+ metaType->signalOffset = T::staticMetaObject.methodCount();
+}
+
+static void addProperty(QMetaObjectBuilder *builder, int propertyId, const QByteArray &propertyName, const QByteArray &propertyType)
+{
+ builder->addSignal("__" + QByteArray::number(propertyId) + "()");
+ QMetaPropertyBuilder property = builder->addProperty(
+ propertyName, propertyType, propertyId);
+ property.setWritable(true);
+}
+
+class VDMModelDelegateDataType;
+
+class QQmlDMCachedModelData : public QQmlDelegateModelItem
+{
+public:
+ QQmlDMCachedModelData(
+ QQmlDelegateModelItemMetaType *metaType,
+ VDMModelDelegateDataType *dataType,
+ int index);
+
+ int metaCall(QMetaObject::Call call, int id, void **arguments);
+
+ virtual QVariant value(int role) const = 0;
+ virtual void setValue(int role, const QVariant &value) = 0;
+
+ void setValue(const QString &role, const QVariant &value);
+ bool resolveIndex(const QQmlAdaptorModel &model, int idx);
+
+ static v8::Handle<v8::Value> get_property(v8::Local<v8::String>, const v8::AccessorInfo &info);
+ static void set_property(
+ v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info);
+
+ VDMModelDelegateDataType *type;
+ QVector<QVariant> cachedData;
+};
+
+class VDMModelDelegateDataType
+ : public QQmlRefCount
+ , public QQmlAdaptorModel::Accessors
+ , public QAbstractDynamicMetaObject
+{
+public:
+ VDMModelDelegateDataType(QQmlAdaptorModel *model)
+ : model(model)
+ , metaObject(0)
+ , propertyCache(0)
+ , propertyOffset(0)
+ , signalOffset(0)
+ , hasModelData(false)
+ {
+ }
+
+ ~VDMModelDelegateDataType()
+ {
+ if (propertyCache)
+ propertyCache->release();
+ free(metaObject);
+
+ qPersistentDispose(constructor);
+ }
+
+ bool notify(
+ const QQmlAdaptorModel &,
+ const QList<QQmlDelegateModelItem *> &items,
+ int index,
+ int count,
+ const QVector<int> &roles) const
+ {
+ bool changed = roles.isEmpty() && !watchedRoles.isEmpty();
+ if (!changed && !watchedRoles.isEmpty() && watchedRoleIds.isEmpty()) {
+ QList<int> roleIds;
+ foreach (const QByteArray &r, watchedRoles) {
+ QHash<QByteArray, int>::const_iterator it = roleNames.find(r);
+ if (it != roleNames.end())
+ roleIds << it.value();
+ }
+ const_cast<VDMModelDelegateDataType *>(this)->watchedRoleIds = roleIds;
+ }
+
+ QVector<int> signalIndexes;
+ for (int i = 0; i < roles.count(); ++i) {
+ const int role = roles.at(i);
+ if (!changed && watchedRoleIds.contains(role))
+ changed = true;
+
+ int propertyId = propertyRoles.indexOf(role);
+ if (propertyId != -1)
+ signalIndexes.append(propertyId + signalOffset);
+ }
+ if (roles.isEmpty()) {
+ for (int propertyId = 0; propertyId < propertyRoles.count(); ++propertyId)
+ signalIndexes.append(propertyId + signalOffset);
+ }
+
+ for (int i = 0, c = items.count(); i < c; ++i) {
+ QQmlDelegateModelItem *item = items.at(i);
+ const int idx = item->modelIndex();
+ if (idx >= index && idx < index + count) {
+ for (int i = 0; i < signalIndexes.count(); ++i)
+ QMetaObject::activate(item, signalIndexes.at(i), 0);
+ }
+ }
+ return changed;
+ }
+
+ void replaceWatchedRoles(
+ QQmlAdaptorModel &,
+ const QList<QByteArray> &oldRoles,
+ const QList<QByteArray> &newRoles) const
+ {
+ VDMModelDelegateDataType *dataType = const_cast<VDMModelDelegateDataType *>(this);
+
+ dataType->watchedRoleIds.clear();
+ foreach (const QByteArray &oldRole, oldRoles)
+ dataType->watchedRoles.removeOne(oldRole);
+ dataType->watchedRoles += newRoles;
+ }
+
+ void initializeConstructor(QQmlAdaptorModelEngineData *const data)
+ {
+ constructor = qPersistentNew(v8::ObjectTemplate::New());
+ constructor->SetHasExternalResource(true);
+ constructor->SetAccessor(data->index(), get_index);
+
+ typedef QHash<QByteArray, int>::const_iterator iterator;
+ for (iterator it = roleNames.constBegin(), end = roleNames.constEnd(); it != end; ++it) {
+ const int propertyId = propertyRoles.indexOf(it.value());
+ const QByteArray &propertyName = it.key();
+
+ constructor->SetAccessor(
+ v8::String::New(propertyName.constData(), propertyName.length()),
+ QQmlDMCachedModelData::get_property,
+ QQmlDMCachedModelData::set_property,
+ v8::Int32::New(propertyId));
+ }
+ }
+
+ // QAbstractDynamicMetaObject
+
+ void objectDestroyed(QObject *)
+ {
+ release();
+ }
+
+ int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments)
+ {
+ return static_cast<QQmlDMCachedModelData *>(object)->metaCall(call, id, arguments);
+ }
+
+ v8::Persistent<v8::ObjectTemplate> constructor;
+ QList<int> propertyRoles;
+ QList<int> watchedRoleIds;
+ QList<QByteArray> watchedRoles;
+ QHash<QByteArray, int> roleNames;
+ QQmlAdaptorModel *model;
+ QMetaObject *metaObject;
+ QQmlPropertyCache *propertyCache;
+ int propertyOffset;
+ int signalOffset;
+ bool hasModelData;
+};
+
+QQmlDMCachedModelData::QQmlDMCachedModelData(
+ QQmlDelegateModelItemMetaType *metaType, VDMModelDelegateDataType *dataType, int index)
+ : QQmlDelegateModelItem(metaType, index)
+ , type(dataType)
+{
+ if (index == -1)
+ cachedData.resize(type->hasModelData ? 1 : type->propertyRoles.count());
+
+ QObjectPrivate::get(this)->metaObject = type;
+
+ type->addref();
+
+ QQmlData *qmldata = QQmlData::get(this, true);
+ qmldata->propertyCache = dataType->propertyCache;
+ qmldata->propertyCache->addref();
+}
+
+int QQmlDMCachedModelData::metaCall(QMetaObject::Call call, int id, void **arguments)
+{
+ if (call == QMetaObject::ReadProperty && id >= type->propertyOffset) {
+ const int propertyIndex = id - type->propertyOffset;
+ if (index == -1) {
+ if (!cachedData.isEmpty()) {
+ *static_cast<QVariant *>(arguments[0]) = cachedData.at(
+ type->hasModelData ? 0 : propertyIndex);
+ }
+ } else if (*type->model) {
+ *static_cast<QVariant *>(arguments[0]) = value(type->propertyRoles.at(propertyIndex));
+ }
+ return -1;
+ } else if (call == QMetaObject::WriteProperty && id >= type->propertyOffset) {
+ const int propertyIndex = id - type->propertyOffset;
+ if (index == -1) {
+ const QMetaObject *meta = metaObject();
+ if (cachedData.count() > 1) {
+ cachedData[propertyIndex] = *static_cast<QVariant *>(arguments[0]);
+ QMetaObject::activate(this, meta, propertyIndex, 0);
+ } else if (cachedData.count() == 1) {
+ cachedData[0] = *static_cast<QVariant *>(arguments[0]);
+ QMetaObject::activate(this, meta, 0, 0);
+ QMetaObject::activate(this, meta, 1, 0);
+ }
+ } else if (*type->model) {
+ setValue(type->propertyRoles.at(propertyIndex), *static_cast<QVariant *>(arguments[0]));
+ }
+ return -1;
+ } else {
+ return qt_metacall(call, id, arguments);
+ }
+}
+
+void QQmlDMCachedModelData::setValue(const QString &role, const QVariant &value)
+{
+ QHash<QByteArray, int>::iterator it = type->roleNames.find(role.toUtf8());
+ if (it != type->roleNames.end()) {
+ for (int i = 0; i < type->propertyRoles.count(); ++i) {
+ if (type->propertyRoles.at(i) == *it) {
+ cachedData[i] = value;
+ return;
+ }
+ }
+ }
+}
+
+bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &, int idx)
+{
+ if (index == -1) {
+ Q_ASSERT(idx >= 0);
+ index = idx;
+ cachedData.clear();
+ emit modelIndexChanged();
+ const QMetaObject *meta = metaObject();
+ const int propertyCount = type->propertyRoles.count();
+ for (int i = 0; i < propertyCount; ++i)
+ QMetaObject::activate(this, meta, i, 0);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+v8::Handle<v8::Value> QQmlDMCachedModelData::get_property(
+ v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QQmlDelegateModelItem *data = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE(data, "Not a valid VisualData object");
+
+ QQmlDMCachedModelData *modelData = static_cast<QQmlDMCachedModelData *>(data);
+ const int propertyId = info.Data()->Int32Value();
+ if (data->index == -1) {
+ if (!modelData->cachedData.isEmpty()) {
+ return data->engine->fromVariant(
+ modelData->cachedData.at(modelData->type->hasModelData ? 0 : propertyId));
+ }
+ } else if (*modelData->type->model) {
+ return data->engine->fromVariant(
+ modelData->value(modelData->type->propertyRoles.at(propertyId)));
+ }
+ return v8::Undefined();
+}
+
+void QQmlDMCachedModelData::set_property(
+ v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QQmlDelegateModelItem *data = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE_SETTER(data, "Not a valid VisualData object");
+
+ const int propertyId = info.Data()->Int32Value();
+ if (data->index == -1) {
+ QQmlDMCachedModelData *modelData = static_cast<QQmlDMCachedModelData *>(data);
+ if (!modelData->cachedData.isEmpty()) {
+ if (modelData->cachedData.count() > 1) {
+ modelData->cachedData[propertyId] = data->engine->toVariant(value, QVariant::Invalid);
+ QMetaObject::activate(data, data->metaObject(), propertyId, 0);
+ } else if (modelData->cachedData.count() == 1) {
+ modelData->cachedData[0] = data->engine->toVariant(value, QVariant::Invalid);
+ QMetaObject::activate(data, data->metaObject(), 0, 0);
+ QMetaObject::activate(data, data->metaObject(), 1, 0);
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------
+// QAbstractItemModel
+//-----------------------------------------------------------------
+
+class QQmlDMAbstractItemModelData : public QQmlDMCachedModelData
+{
+ Q_OBJECT
+ Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT)
+public:
+ QQmlDMAbstractItemModelData(
+ QQmlDelegateModelItemMetaType *metaType,
+ VDMModelDelegateDataType *dataType,
+ int index)
+ : QQmlDMCachedModelData(metaType, dataType, index)
+ {
+ }
+
+ bool hasModelChildren() const
+ {
+ if (index >= 0 && *type->model) {
+ const QAbstractItemModel * const model = type->model->aim();
+ return model->hasChildren(model->index(index, 0, type->model->rootIndex));
+ } else {
+ return false;
+ }
+ }
+
+ QVariant value(int role) const
+ {
+ return type->model->aim()->index(index, 0, type->model->rootIndex).data(role);
+ }
+
+ void setValue(int role, const QVariant &value)
+ {
+ type->model->aim()->setData(
+ type->model->aim()->index(index, 0, type->model->rootIndex), value, role);
+ }
+
+ v8::Handle<v8::Value> get()
+ {
+ if (type->constructor.IsEmpty()) {
+ QQmlAdaptorModelEngineData * const data = engineData(engine);
+ v8::HandleScope handleScope;
+ v8::Context::Scope contextScope(engine->context());
+ type->initializeConstructor(data);
+ type->constructor->SetAccessor(data->hasModelChildren(), get_hasModelChildren);
+ }
+ v8::Local<v8::Object> data = type->constructor->NewInstance();
+ data->SetExternalResource(this);
+ ++scriptRef;
+ return data;
+ }
+
+ static v8::Handle<v8::Value> get_hasModelChildren(v8::Local<v8::String>, const v8::AccessorInfo &info)
+ {
+ QQmlDelegateModelItem *data = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE(data, "Not a valid VisualData object");
+
+ const QQmlAdaptorModel *const model = static_cast<QQmlDMCachedModelData *>(data)->type->model;
+ if (data->index >= 0 && *model) {
+ const QAbstractItemModel * const aim = model->aim();
+ return v8::Boolean::New(aim->hasChildren(aim->index(data->index, 0, model->rootIndex)));
+ } else {
+ return v8::Boolean::New(false);
+ }
+ }
+};
+
+class VDMAbstractItemModelDataType : public VDMModelDelegateDataType
+{
+public:
+ VDMAbstractItemModelDataType(QQmlAdaptorModel *model)
+ : VDMModelDelegateDataType(model)
+ {
+ }
+
+ int count(const QQmlAdaptorModel &model) const
+ {
+ return model.aim()->rowCount(model.rootIndex);
+ }
+
+ void cleanup(QQmlAdaptorModel &model, QQmlDelegateModel *vdm) const
+ {
+ QAbstractItemModel * const aim = model.aim();
+ if (aim && vdm) {
+ QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ vdm, SLOT(_q_rowsInserted(QModelIndex,int,int)));
+ QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ vdm, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
+ QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ vdm, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
+ QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
+ vdm, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>)));
+ QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
+ vdm, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
+ QObject::disconnect(aim, SIGNAL(modelReset()),
+ vdm, SLOT(_q_modelReset()));
+ QObject::disconnect(aim, SIGNAL(layoutChanged()),
+ vdm, SLOT(_q_layoutChanged()));
+ }
+
+ const_cast<VDMAbstractItemModelDataType *>(this)->release();
+ }
+
+ QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const
+ {
+ QHash<QByteArray, int>::const_iterator it = roleNames.find(role.toUtf8());
+ if (it != roleNames.end()) {
+ return model.aim()->index(index, 0, model.rootIndex).data(*it);
+ } else if (role == QLatin1String("hasModelChildren")) {
+ return QVariant(model.aim()->hasChildren(model.aim()->index(index, 0, model.rootIndex)));
+ } else {
+ return QVariant();
+ }
+ }
+
+ QVariant parentModelIndex(const QQmlAdaptorModel &model) const
+ {
+ return model
+ ? QVariant::fromValue(model.aim()->parent(model.rootIndex))
+ : QVariant();
+ }
+
+ QVariant modelIndex(const QQmlAdaptorModel &model, int index) const
+ {
+ return model
+ ? QVariant::fromValue(model.aim()->index(index, 0, model.rootIndex))
+ : QVariant();
+ }
+
+ bool canFetchMore(const QQmlAdaptorModel &model) const
+ {
+ return model && model.aim()->canFetchMore(model.rootIndex);
+ }
+
+ void fetchMore(QQmlAdaptorModel &model) const
+ {
+ if (model)
+ model.aim()->fetchMore(model.rootIndex);
+ }
+
+ QQmlDelegateModelItem *createItem(
+ QQmlAdaptorModel &model,
+ QQmlDelegateModelItemMetaType *metaType,
+ QQmlEngine *engine,
+ int index) const
+ {
+ VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this);
+ if (!metaObject)
+ dataType->initializeMetaType(model, engine);
+ return new QQmlDMAbstractItemModelData(metaType, dataType, index);
+ }
+
+ void initializeMetaType(QQmlAdaptorModel &model, QQmlEngine *engine)
+ {
+ QMetaObjectBuilder builder;
+ setModelDataType<QQmlDMAbstractItemModelData>(&builder, this);
+
+ const QByteArray propertyType = QByteArrayLiteral("QVariant");
+ const QHash<int, QByteArray> names = model.aim()->roleNames();
+ for (QHash<int, QByteArray>::const_iterator it = names.begin(); it != names.end(); ++it) {
+ const int propertyId = propertyRoles.count();
+ propertyRoles.append(it.key());
+ roleNames.insert(it.value(), it.key());
+ addProperty(&builder, propertyId, it.value(), propertyType);
+ }
+ if (propertyRoles.count() == 1) {
+ hasModelData = true;
+ const int role = names.begin().key();
+ const QByteArray propertyName = QByteArrayLiteral("modelData");
+
+ propertyRoles.append(role);
+ roleNames.insert(propertyName, role);
+ addProperty(&builder, 1, propertyName, propertyType);
+ }
+
+ metaObject = builder.toMetaObject();
+ *static_cast<QMetaObject *>(this) = *metaObject;
+ propertyCache = new QQmlPropertyCache(engine, metaObject);
+ }
+};
+
+//-----------------------------------------------------------------
+// QQmlListAccessor
+//-----------------------------------------------------------------
+
+class QQmlDMListAccessorData : public QQmlDelegateModelItem
+{
+ Q_OBJECT
+ Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
+public:
+ QQmlDMListAccessorData(QQmlDelegateModelItemMetaType *metaType, int index, const QVariant &value)
+ : QQmlDelegateModelItem(metaType, index)
+ , cachedData(value)
+ {
+ }
+
+ QVariant modelData() const
+ {
+ return cachedData;
+ }
+
+ void setModelData(const QVariant &data)
+ {
+ if (index == -1 && data != cachedData) {
+ cachedData = data;
+ emit modelDataChanged();
+ }
+ }
+
+ static v8::Handle<v8::Value> get_modelData(v8::Local<v8::String>, const v8::AccessorInfo &info)
+ {
+ QQmlDelegateModelItem *data = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE(data, "Not a valid VisualData object");
+
+ return data->engine->fromVariant(static_cast<QQmlDMListAccessorData *>(data)->cachedData);
+ }
+
+ static void set_modelData(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+ {
+ QQmlDelegateModelItem *data = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE_SETTER(data, "Not a valid VisualData object");
+
+ static_cast<QQmlDMListAccessorData *>(data)->setModelData(
+ data->engine->toVariant(value, QVariant::Invalid));
+ }
+
+ v8::Handle<v8::Value> get()
+ {
+ v8::Local<v8::Object> data = engineData(engine)->constructorListItem->NewInstance();
+ data->SetExternalResource(this);
+ ++scriptRef;
+ return data;
+ }
+
+ void setValue(const QString &role, const QVariant &value)
+ {
+ if (role == QLatin1String("modelData"))
+ cachedData = value;
+ }
+
+ bool resolveIndex(const QQmlAdaptorModel &model, int idx)
+ {
+ if (index == -1) {
+ index = idx;
+ cachedData = model.list.at(idx);
+ emit modelIndexChanged();
+ emit modelDataChanged();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+Q_SIGNALS:
+ void modelDataChanged();
+
+private:
+ QVariant cachedData;
+};
+
+
+class VDMListDelegateDataType : public QQmlAdaptorModel::Accessors
+{
+public:
+ inline VDMListDelegateDataType() {}
+
+ int count(const QQmlAdaptorModel &model) const
+ {
+ return model.list.count();
+ }
+
+ QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const
+ {
+ return role == QLatin1String("modelData")
+ ? model.list.at(index)
+ : QVariant();
+ }
+
+ QQmlDelegateModelItem *createItem(
+ QQmlAdaptorModel &model,
+ QQmlDelegateModelItemMetaType *metaType,
+ QQmlEngine *,
+ int index) const
+ {
+ return new QQmlDMListAccessorData(
+ metaType,
+ index,
+ index >= 0 && index < model.list.count() ? model.list.at(index) : QVariant());
+ }
+};
+
+//-----------------------------------------------------------------
+// QObject
+//-----------------------------------------------------------------
+
+class VDMObjectDelegateDataType;
+class QQmlDMObjectData : public QQmlDelegateModelItem, public QQmlAdaptorModelProxyInterface
+{
+ Q_OBJECT
+ Q_PROPERTY(QObject *modelData READ modelData CONSTANT)
+ Q_INTERFACES(QQmlAdaptorModelProxyInterface)
+public:
+ QQmlDMObjectData(
+ QQmlDelegateModelItemMetaType *metaType,
+ VDMObjectDelegateDataType *dataType,
+ int index,
+ QObject *object);
+
+ QObject *modelData() const { return object; }
+ QObject *proxiedObject() { return object; }
+
+ QQmlGuard<QObject> object;
+};
+
+class VDMObjectDelegateDataType : public QQmlRefCount, public QQmlAdaptorModel::Accessors
+{
+public:
+ QMetaObject *metaObject;
+ int propertyOffset;
+ int signalOffset;
+ bool shared;
+ QMetaObjectBuilder builder;
+
+ VDMObjectDelegateDataType()
+ : metaObject(0)
+ , propertyOffset(0)
+ , signalOffset(0)
+ , shared(true)
+ {
+ }
+
+ VDMObjectDelegateDataType(const VDMObjectDelegateDataType &type)
+ : QQmlRefCount()
+ , QQmlAdaptorModel::Accessors()
+ , metaObject(0)
+ , propertyOffset(type.propertyOffset)
+ , signalOffset(type.signalOffset)
+ , shared(false)
+ , builder(type.metaObject, QMetaObjectBuilder::Properties
+ | QMetaObjectBuilder::Signals
+ | QMetaObjectBuilder::SuperClass
+ | QMetaObjectBuilder::ClassName)
+ {
+ builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
+ }
+
+ ~VDMObjectDelegateDataType()
+ {
+ free(metaObject);
+ }
+
+ int count(const QQmlAdaptorModel &model) const
+ {
+ return model.list.count();
+ }
+
+ QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const
+ {
+ if (QObject *object = model.list.at(index).value<QObject *>())
+ return object->property(role.toUtf8());
+ return QVariant();
+ }
+
+ QQmlDelegateModelItem *createItem(
+ QQmlAdaptorModel &model,
+ QQmlDelegateModelItemMetaType *metaType,
+ QQmlEngine *,
+ int index) const
+ {
+ VDMObjectDelegateDataType *dataType = const_cast<VDMObjectDelegateDataType *>(this);
+ if (!metaObject)
+ dataType->initializeMetaType(model);
+ return index >= 0 && index < model.list.count()
+ ? new QQmlDMObjectData(metaType, dataType, index, qvariant_cast<QObject *>(model.list.at(index)))
+ : 0;
+ }
+
+ void initializeMetaType(QQmlAdaptorModel &)
+ {
+ setModelDataType<QQmlDMObjectData>(&builder, this);
+
+ metaObject = builder.toMetaObject();
+ }
+
+ void cleanup(QQmlAdaptorModel &, QQmlDelegateModel *) const
+ {
+ const_cast<VDMObjectDelegateDataType *>(this)->release();
+ }
+};
+
+class QQmlDMObjectDataMetaObject : public QAbstractDynamicMetaObject
+{
+public:
+ QQmlDMObjectDataMetaObject(QQmlDMObjectData *data, VDMObjectDelegateDataType *type)
+ : m_data(data)
+ , m_type(type)
+ {
+ QObjectPrivate *op = QObjectPrivate::get(m_data);
+ *static_cast<QMetaObject *>(this) = *type->metaObject;
+ op->metaObject = this;
+ m_type->addref();
+ }
+
+ ~QQmlDMObjectDataMetaObject()
+ {
+ m_type->release();
+ }
+
+ int metaCall(QMetaObject::Call call, int id, void **arguments)
+ {
+ static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount();
+ if (id >= m_type->propertyOffset
+ && (call == QMetaObject::ReadProperty
+ || call == QMetaObject::WriteProperty
+ || call == QMetaObject::ResetProperty)) {
+ if (m_data->object)
+ QMetaObject::metacall(m_data->object, call, id - m_type->propertyOffset + objectPropertyOffset, arguments);
+ return -1;
+ } else if (id >= m_type->signalOffset && call == QMetaObject::InvokeMetaMethod) {
+ QMetaObject::activate(m_data, this, id - m_type->signalOffset, 0);
+ return -1;
+ } else {
+ return m_data->qt_metacall(call, id, arguments);
+ }
+ }
+
+ int createProperty(const char *name, const char *)
+ {
+ if (!m_data->object)
+ return -1;
+ const QMetaObject *metaObject = m_data->object->metaObject();
+ static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount();
+
+ const int previousPropertyCount = propertyCount() - propertyOffset();
+ int propertyIndex = metaObject->indexOfProperty(name);
+ if (propertyIndex == -1)
+ return -1;
+ if (previousPropertyCount + objectPropertyOffset == metaObject->propertyCount())
+ return propertyIndex + m_type->propertyOffset - objectPropertyOffset;
+
+ if (m_type->shared) {
+ VDMObjectDelegateDataType *type = m_type;
+ m_type = new VDMObjectDelegateDataType(*m_type);
+ type->release();
+ }
+
+ const int previousMethodCount = methodCount();
+ int notifierId = previousMethodCount - methodOffset();
+ for (int propertyId = previousPropertyCount; propertyId < metaObject->propertyCount() - objectPropertyOffset; ++propertyId) {
+ QMetaProperty property = metaObject->property(propertyId + objectPropertyOffset);
+ QMetaPropertyBuilder propertyBuilder;
+ if (property.hasNotifySignal()) {
+ m_type->builder.addSignal("__" + QByteArray::number(propertyId) + "()");
+ propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName(), notifierId);
+ ++notifierId;
+ } else {
+ propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName());
+ }
+ propertyBuilder.setWritable(property.isWritable());
+ propertyBuilder.setResettable(property.isResettable());
+ propertyBuilder.setConstant(property.isConstant());
+ }
+
+ if (m_type->metaObject)
+ free(m_type->metaObject);
+ m_type->metaObject = m_type->builder.toMetaObject();
+ *static_cast<QMetaObject *>(this) = *m_type->metaObject;
+
+ notifierId = previousMethodCount;
+ for (int i = previousPropertyCount; i < metaObject->propertyCount() - objectPropertyOffset; ++i) {
+ QMetaProperty property = metaObject->property(i + objectPropertyOffset);
+ if (property.hasNotifySignal()) {
+ QQmlPropertyPrivate::connect(
+ m_data->object, property.notifySignalIndex(), m_data, notifierId);
+ ++notifierId;
+ }
+ }
+ return propertyIndex + m_type->propertyOffset - objectPropertyOffset;
+ }
+
+ QQmlDMObjectData *m_data;
+ VDMObjectDelegateDataType *m_type;
+};
+
+QQmlDMObjectData::QQmlDMObjectData(
+ QQmlDelegateModelItemMetaType *metaType,
+ VDMObjectDelegateDataType *dataType,
+ int index,
+ QObject *object)
+ : QQmlDelegateModelItem(metaType, index)
+ , object(object)
+{
+ new QQmlDMObjectDataMetaObject(this, dataType);
+}
+
+//-----------------------------------------------------------------
+// QQmlAdaptorModel
+//-----------------------------------------------------------------
+
+static const QQmlAdaptorModel::Accessors qt_vdm_null_accessors;
+static const VDMListDelegateDataType qt_vdm_list_accessors;
+
+QQmlAdaptorModel::Accessors::~Accessors()
+{
+}
+
+QQmlAdaptorModel::QQmlAdaptorModel()
+ : accessors(&qt_vdm_null_accessors)
+{
+}
+
+QQmlAdaptorModel::~QQmlAdaptorModel()
+{
+ accessors->cleanup(*this);
+}
+
+void QQmlAdaptorModel::setModel(const QVariant &variant, QQmlDelegateModel *vdm, QQmlEngine *engine)
+{
+ accessors->cleanup(*this, vdm);
+
+ list.setList(variant, engine);
+
+ if (QObject *object = qvariant_cast<QObject *>(variant)) {
+ setObject(object);
+ if (QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(object)) {
+ accessors = new VDMAbstractItemModelDataType(this);
+
+ qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ vdm, QQmlDelegateModel, SLOT(_q_rowsInserted(QModelIndex,int,int)));
+ qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ vdm, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int)));
+ qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ vdm, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int)));
+ qmlobject_connect(model, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)),
+ vdm, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>)));
+ qmlobject_connect(model, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)),
+ vdm, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int)));
+ qmlobject_connect(model, QAbstractItemModel, SIGNAL(modelReset()),
+ vdm, QQmlDelegateModel, SLOT(_q_modelReset()));
+ qmlobject_connect(model, QAbstractItemModel, SIGNAL(layoutChanged()),
+ vdm, QQmlDelegateModel, SLOT(_q_layoutChanged()));
+ } else {
+ accessors = new VDMObjectDelegateDataType;
+ }
+ } else if (list.type() == QQmlListAccessor::ListProperty) {
+ setObject(static_cast<const QQmlListReference *>(variant.constData())->object());
+ accessors = new VDMObjectDelegateDataType;
+ } else if (list.type() != QQmlListAccessor::Invalid) {
+ Q_ASSERT(list.type() != QQmlListAccessor::Instance); // Should have cast to QObject.
+ setObject(0);
+ accessors = &qt_vdm_list_accessors;
+ } else {
+ setObject(0);
+ accessors = &qt_vdm_null_accessors;
+ }
+}
+
+void QQmlAdaptorModel::invalidateModel(QQmlDelegateModel *vdm)
+{
+ accessors->cleanup(*this, vdm);
+ accessors = &qt_vdm_null_accessors;
+ // Don't clear the model object as we still need the guard to clear the list variant if the
+ // object is destroyed.
+}
+
+bool QQmlAdaptorModel::isValid() const
+{
+ return accessors != &qt_vdm_null_accessors;
+}
+
+void QQmlAdaptorModel::objectDestroyed(QObject *)
+{
+ setModel(QVariant(), 0, 0);
+}
+
+QQmlAdaptorModelEngineData::QQmlAdaptorModelEngineData(QV8Engine *)
+{
+ strings = qPersistentNew(v8::Array::New(StringCount));
+ strings->Set(Index, v8::String::New("index"));
+ strings->Set(ModelData, v8::String::New("modelData"));
+ strings->Set(HasModelChildren, v8::String::New("hasModelChildren"));
+
+ v8::Local<v8::FunctionTemplate> listItem = v8::FunctionTemplate::New();
+ listItem->InstanceTemplate()->SetHasExternalResource(true);
+ listItem->InstanceTemplate()->SetAccessor(index(), get_index);
+ listItem->InstanceTemplate()->SetAccessor(
+ modelData(),
+ QQmlDMListAccessorData::get_modelData,
+ QQmlDMListAccessorData::set_modelData);
+ constructorListItem = qPersistentNew(listItem->GetFunction());
+}
+
+QQmlAdaptorModelEngineData::~QQmlAdaptorModelEngineData()
+{
+ qPersistentDispose(constructorListItem);
+ qPersistentDispose(strings);
+}
+
+QT_END_NAMESPACE
+
+#include <qqmladaptormodel.moc>
diff --git a/src/qml/util/qqmladaptormodel_p.h b/src/qml/util/qqmladaptormodel_p.h
new file mode 100644
index 0000000000..6e9af8ebda
--- /dev/null
+++ b/src/qml/util/qqmladaptormodel_p.h
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLADAPTORMODEL_P_H
+#define QQMLADAPTORMODEL_P_H
+
+#include <QtCore/qabstractitemmodel.h>
+
+#include "private/qqmllistaccessor_p.h"
+
+#include <private/qqmlguard_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQmlEngine;
+
+class QQmlDelegateModel;
+class QQmlDelegateModelItem;
+class QQmlDelegateModelItemMetaType;
+
+class QQmlAdaptorModel : public QQmlGuard<QObject>
+{
+public:
+ class Accessors
+ {
+ public:
+ inline Accessors() {}
+ virtual ~Accessors();
+ virtual int count(const QQmlAdaptorModel &) const { return 0; }
+ virtual void cleanup(QQmlAdaptorModel &, QQmlDelegateModel * = 0) const {}
+
+ virtual QVariant value(const QQmlAdaptorModel &, int, const QString &) const {
+ return QVariant(); }
+
+ virtual QQmlDelegateModelItem *createItem(
+ QQmlAdaptorModel &,
+ QQmlDelegateModelItemMetaType *,
+ QQmlEngine *,
+ int) const { return 0; }
+
+ virtual bool notify(
+ const QQmlAdaptorModel &,
+ const QList<QQmlDelegateModelItem *> &,
+ int,
+ int,
+ const QVector<int> &) const { return false; }
+ virtual void replaceWatchedRoles(
+ QQmlAdaptorModel &,
+ const QList<QByteArray> &,
+ const QList<QByteArray> &) const {}
+ virtual QVariant parentModelIndex(const QQmlAdaptorModel &) const {
+ return QVariant(); }
+ virtual QVariant modelIndex(const QQmlAdaptorModel &, int) const {
+ return QVariant(); }
+ virtual bool canFetchMore(const QQmlAdaptorModel &) const { return false; }
+ virtual void fetchMore(QQmlAdaptorModel &) const {}
+ };
+
+ const Accessors *accessors;
+ QPersistentModelIndex rootIndex;
+ QQmlListAccessor list;
+
+ QQmlAdaptorModel();
+ ~QQmlAdaptorModel();
+
+ inline QVariant model() const { return list.list(); }
+ void setModel(const QVariant &variant, QQmlDelegateModel *vdm, QQmlEngine *engine);
+ void invalidateModel(QQmlDelegateModel *vdm);
+
+ bool isValid() const;
+
+ inline QAbstractItemModel *aim() { return static_cast<QAbstractItemModel *>(object()); }
+ inline const QAbstractItemModel *aim() const { return static_cast<const QAbstractItemModel *>(object()); }
+
+ inline int count() const { return qMax(0, accessors->count(*this)); }
+ inline QVariant value(int index, const QString &role) const {
+ return accessors->value(*this, index, role); }
+ inline QQmlDelegateModelItem *createItem(QQmlDelegateModelItemMetaType *metaType, QQmlEngine *engine, int index) {
+ return accessors->createItem(*this, metaType, engine, index); }
+ inline bool hasProxyObject() const {
+ return list.type() == QQmlListAccessor::Instance || list.type() == QQmlListAccessor::ListProperty; }
+
+ inline bool notify(
+ const QList<QQmlDelegateModelItem *> &items,
+ int index,
+ int count,
+ const QVector<int> &roles) const {
+ return accessors->notify(*this, items, index, count, roles); }
+ inline void replaceWatchedRoles(
+ const QList<QByteArray> &oldRoles, const QList<QByteArray> &newRoles) {
+ accessors->replaceWatchedRoles(*this, oldRoles, newRoles); }
+
+ inline QVariant modelIndex(int index) const { return accessors->modelIndex(*this, index); }
+ inline QVariant parentModelIndex() const { return accessors->parentModelIndex(*this); }
+ inline bool canFetchMore() const { return accessors->canFetchMore(*this); }
+ inline void fetchMore() { return accessors->fetchMore(*this); }
+
+protected:
+ void objectDestroyed(QObject *);
+};
+
+class QQmlAdaptorModelProxyInterface
+{
+public:
+ virtual ~QQmlAdaptorModelProxyInterface() {}
+
+ virtual QObject *proxiedObject() = 0;
+};
+
+#define QQmlAdaptorModelProxyInterface_iid "org.qt-project.Qt.QQmlAdaptorModelProxyInterface"
+
+Q_DECLARE_INTERFACE(QQmlAdaptorModelProxyInterface, QQmlAdaptorModelProxyInterface_iid)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/util/qqmlchangeset.cpp b/src/qml/util/qqmlchangeset.cpp
new file mode 100644
index 0000000000..831cb063a5
--- /dev/null
+++ b/src/qml/util/qqmlchangeset.cpp
@@ -0,0 +1,621 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlchangeset_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+/*!
+ \class QQmlChangeSet
+ \brief The QQmlChangeSet class stores an ordered list of notifications about
+ changes to a linear data set.
+ \internal
+
+ QQmlChangeSet can be used to record a series of notifications about items in an indexed list
+ being inserted, removed, moved, and changed. Notifications in the set are re-ordered so that
+ all notifications of a single type are grouped together and sorted in order of ascending index,
+ with remove notifications preceding all others, followed by insert notification, and then
+ change notifications.
+
+ Moves in a change set are represented by a remove notification paired with an insert
+ notification by way of a shared unique moveId. Re-ordering may result in one or both of the
+ paired notifications being divided, when this happens the offset member of the notification
+ will indicate the relative offset of the divided notification from the beginning of the
+ original.
+*/
+
+/*!
+ Constructs an empty change set.
+*/
+
+QQmlChangeSet::QQmlChangeSet()
+ : m_difference(0)
+{
+}
+
+/*!
+ Constructs a copy of a \a changeSet.
+*/
+
+QQmlChangeSet::QQmlChangeSet(const QQmlChangeSet &changeSet)
+ : m_removes(changeSet.m_removes)
+ , m_inserts(changeSet.m_inserts)
+ , m_changes(changeSet.m_changes)
+ , m_difference(changeSet.m_difference)
+{
+}
+
+/*!
+ Destroys a change set.
+*/
+
+QQmlChangeSet::~QQmlChangeSet()
+{
+}
+
+/*!
+ Assigns the value of a \a changeSet to another.
+*/
+
+QQmlChangeSet &QQmlChangeSet::operator =(const QQmlChangeSet &changeSet)
+{
+ m_removes = changeSet.m_removes;
+ m_inserts = changeSet.m_inserts;
+ m_changes = changeSet.m_changes;
+ m_difference = changeSet.m_difference;
+ return *this;
+}
+
+/*!
+ Appends a notification that \a count items were inserted at \a index.
+*/
+
+void QQmlChangeSet::insert(int index, int count)
+{
+ insert(QVector<Insert>() << Insert(index, count));
+}
+
+/*!
+ Appends a notification that \a count items were removed at \a index.
+*/
+
+void QQmlChangeSet::remove(int index, int count)
+{
+ QVector<Remove> removes;
+ removes.append(Remove(index, count));
+ remove(&removes, 0);
+}
+
+/*!
+ Appends a notification that \a count items were moved \a from one index \a to another.
+
+ The \a moveId must be unique across the lifetime of the change set and any related
+ change sets.
+*/
+
+void QQmlChangeSet::move(int from, int to, int count, int moveId)
+{
+ QVector<Remove> removes;
+ removes.append(Remove(from, count, moveId));
+ QVector<Insert> inserts;
+ inserts.append(Insert(to, count, moveId));
+ remove(&removes, &inserts);
+ insert(inserts);
+}
+
+/*!
+ Appends a notification that \a count items were changed at \a index.
+*/
+
+void QQmlChangeSet::change(int index, int count)
+{
+ QVector<Change> changes;
+ changes.append(Change(index, count));
+ change(changes);
+}
+
+/*!
+ Applies the changes in a \a changeSet to another.
+*/
+
+void QQmlChangeSet::apply(const QQmlChangeSet &changeSet)
+{
+ QVector<Remove> r = changeSet.m_removes;
+ QVector<Insert> i = changeSet.m_inserts;
+ QVector<Change> c = changeSet.m_changes;
+ remove(&r, &i);
+ insert(i);
+ change(c);
+}
+
+/*!
+ Applies a list of \a removes to a change set.
+
+ If a remove contains a moveId then any intersecting insert in the set will replace the
+ corresponding intersection in the optional \a inserts list.
+*/
+
+void QQmlChangeSet::remove(const QVector<Remove> &removes, QVector<Insert> *inserts)
+{
+ QVector<Remove> r = removes;
+ remove(&r, inserts);
+}
+
+void QQmlChangeSet::remove(QVector<Remove> *removes, QVector<Insert> *inserts)
+{
+ int removeCount = 0;
+ int insertCount = 0;
+ QVector<Insert>::iterator insert = m_inserts.begin();
+ QVector<Change>::iterator change = m_changes.begin();
+ QVector<Remove>::iterator rit = removes->begin();
+ for (; rit != removes->end(); ++rit) {
+ int index = rit->index + removeCount;
+ int count = rit->count;
+
+ // Decrement the accumulated remove count from the indexes of any changes prior to the
+ // current remove.
+ for (; change != m_changes.end() && change->end() < rit->index; ++change)
+ change->index -= removeCount;
+ // Remove any portion of a change notification that intersects the current remove.
+ for (; change != m_changes.end() && change->index > rit->end(); ++change) {
+ change->count -= qMin(change->end(), rit->end()) - qMax(change->index, rit->index);
+ if (change->count == 0) {
+ change = m_changes.erase(change);
+ } else if (rit->index < change->index) {
+ change->index = rit->index;
+ }
+ }
+
+ // Decrement the accumulated remove count from the indexes of any inserts prior to the
+ // current remove.
+ for (; insert != m_inserts.end() && insert->end() <= index; ++insert) {
+ insertCount += insert->count;
+ insert->index -= removeCount;
+ }
+
+ rit->index -= insertCount;
+
+ // Remove any portion of a insert notification that intersects the current remove.
+ while (insert != m_inserts.end() && insert->index < index + count) {
+ int offset = index - insert->index;
+ const int difference = qMin(insert->end(), index + count) - qMax(insert->index, index);
+
+ // If part of the remove or insert that precedes the intersection has a moveId create
+ // a new delta for that portion and subtract the size of that delta from the current
+ // one.
+ if (offset < 0 && rit->moveId != -1) {
+ rit = removes->insert(rit, Remove(
+ rit->index, -offset, rit->moveId, rit->offset));
+ ++rit;
+ rit->count -= -offset;
+ rit->offset += -offset;
+ index += -offset;
+ count -= -offset;
+ removeCount += -offset;
+ offset = 0;
+ } else if (offset > 0 && insert->moveId != -1) {
+ insert = m_inserts.insert(insert, Insert(
+ insert->index - removeCount, offset, insert->moveId, insert->offset));
+ ++insert;
+ insert->index += offset;
+ insert->count -= offset;
+ insert->offset += offset;
+ rit->index -= offset;
+ insertCount += offset;
+ }
+
+ // If the current remove has a move id, find any inserts with the same move id and
+ // replace the corresponding sections with the insert removed from the change set.
+ if (rit->moveId != -1 && difference > 0 && inserts) {
+ for (QVector<Insert>::iterator iit = inserts->begin(); iit != inserts->end(); ++iit) {
+ if (iit->moveId != rit->moveId
+ || rit->offset > iit->offset + iit->count
+ || iit->offset > rit->offset + difference) {
+ continue;
+ }
+ // If the intersecting insert starts before the replacement one create
+ // a new insert for the portion prior to the replacement insert.
+ const int overlapOffset = rit->offset - iit->offset;
+ if (overlapOffset > 0) {
+ iit = inserts->insert(iit, Insert(
+ iit->index, overlapOffset, iit->moveId, iit->offset));
+ ++iit;
+ iit->index += overlapOffset;
+ iit->count -= overlapOffset;
+ iit->offset += overlapOffset;
+ }
+ if (iit->offset >= rit->offset
+ && iit->offset + iit->count <= rit->offset + difference) {
+ // If the replacement insert completely encapsulates the existing
+ // one just change the moveId.
+ iit->moveId = insert->moveId;
+ iit->offset = insert->offset + qMax(0, -overlapOffset);
+ } else {
+ // Create a new insertion before the intersecting one with the number of intersecting
+ // items and remove that number from that insert.
+ const int count
+ = qMin(iit->offset + iit->count, rit->offset + difference)
+ - qMax(iit->offset, rit->offset);
+ iit = inserts->insert(iit, Insert(
+ iit->index,
+ count,
+ insert->moveId,
+ insert->offset + qMax(0, -overlapOffset)));
+ ++iit;
+ iit->index += count;
+ iit->count -= count;
+ iit->offset += count;
+ }
+ }
+ }
+
+ // Subtract the number of intersecting items from the current remove and insert.
+ insert->count -= difference;
+ insert->offset += difference;
+ rit->count -= difference;
+ rit->offset += difference;
+
+ index += difference;
+ count -= difference;
+ removeCount += difference;
+
+ if (insert->count == 0) {
+ insert = m_inserts.erase(insert);
+ } else if (rit->count == -offset || rit->count == 0) {
+ insert->index += difference;
+ break;
+ } else {
+ insert->index -= removeCount - difference;
+ rit->index -= insert->count;
+ insertCount += insert->count;
+ ++insert;
+ }
+ }
+ removeCount += rit->count;
+ }
+ for (; insert != m_inserts.end(); ++insert)
+ insert->index -= removeCount;
+
+ removeCount = 0;
+ QVector<Remove>::iterator remove = m_removes.begin();
+ for (rit = removes->begin(); rit != removes->end(); ++rit) {
+ if (rit->count == 0)
+ continue;
+ // Accumulate consecutive removes into a single delta before attempting to apply.
+ for (QVector<Remove>::iterator next = rit + 1; next != removes->end()
+ && next->index == rit->index
+ && next->moveId == -1
+ && rit->moveId == -1; ++next) {
+ next->count += rit->count;
+ rit = next;
+ }
+ int index = rit->index + removeCount;
+ // Decrement the accumulated remove count from the indexes of any inserts prior to the
+ // current remove.
+ for (; remove != m_removes.end() && index > remove->index; ++remove)
+ remove->index -= removeCount;
+ while (remove != m_removes.end() && index + rit->count >= remove->index) {
+ int count = 0;
+ const int offset = remove->index - index;
+ QVector<Remove>::iterator rend = remove;
+ for (; rend != m_removes.end()
+ && rit->moveId == -1
+ && rend->moveId == -1
+ && index + rit->count >= rend->index; ++rend) {
+ count += rend->count;
+ }
+ if (remove != rend) {
+ // Accumulate all existing non-move removes that are encapsulated by or immediately
+ // follow the current remove into it.
+ int difference = 0;
+ if (rend == m_removes.end()) {
+ difference = rit->count;
+ } else if (rit->index + rit->count < rend->index - removeCount) {
+ difference = rit->count;
+ } else if (rend->moveId != -1) {
+ difference = rend->index - removeCount - rit->index;
+ index += difference;
+ }
+ count += difference;
+
+ rit->count -= difference;
+ removeCount += difference;
+ remove->index = rit->index;
+ remove->count = count;
+ remove = m_removes.erase(++remove, rend);
+ } else {
+ // Insert a remove for the portion of the unmergable current remove prior to the
+ // point of intersection.
+ if (offset > 0) {
+ remove = m_removes.insert(remove, Remove(
+ rit->index, offset, rit->moveId, rit->offset));
+ ++remove;
+ rit->count -= offset;
+ rit->offset += offset;
+ removeCount += offset;
+ index += offset;
+ }
+ remove->index = rit->index;
+
+ ++remove;
+ }
+ }
+
+ if (rit->count > 0) {
+ remove = m_removes.insert(remove, *rit);
+ ++remove;
+ }
+ removeCount += rit->count;
+ }
+ for (; remove != m_removes.end(); ++remove)
+ remove->index -= removeCount;
+ m_difference -= removeCount;
+}
+
+/*!
+ Applies a list of \a inserts to a change set.
+*/
+
+void QQmlChangeSet::insert(const QVector<Insert> &inserts)
+{
+ int insertCount = 0;
+ QVector<Insert>::iterator insert = m_inserts.begin();
+ QVector<Change>::iterator change = m_changes.begin();
+ for (QVector<Insert>::const_iterator iit = inserts.begin(); iit != inserts.end(); ++iit) {
+ if (iit->count == 0)
+ continue;
+ int index = iit->index - insertCount;
+
+ Insert current = *iit;
+ // Accumulate consecutive inserts into a single delta before attempting to insert.
+ for (QVector<Insert>::const_iterator next = iit + 1; next != inserts.end()
+ && next->index == iit->index + iit->count
+ && next->moveId == -1
+ && iit->moveId == -1; ++next) {
+ current.count += next->count;
+ iit = next;
+ }
+
+ // Increment the index of any changes before the current insert by the accumlated insert
+ // count.
+ for (; change != m_changes.end() && change->index >= index; ++change)
+ change->index += insertCount;
+ // If the current insert index is in the middle of a change split it in two at that
+ // point and increment the index of the latter half.
+ if (change != m_changes.end() && change->index < index + iit->count) {
+ int offset = index - change->index;
+ change = m_changes.insert(change, Change(change->index + insertCount, offset));
+ ++change;
+ change->index += iit->count + offset;
+ change->count -= offset;
+ }
+
+ // Increment the index of any inserts before the current insert by the accumlated insert
+ // count.
+ for (; insert != m_inserts.end() && index > insert->index + insert->count; ++insert)
+ insert->index += insertCount;
+ if (insert == m_inserts.end()) {
+ insert = m_inserts.insert(insert, current);
+ ++insert;
+ } else {
+ const int offset = index - insert->index;
+
+ if (offset < 0) {
+ // If the current insert is before an existing insert and not adjacent just insert
+ // it into the list.
+ insert = m_inserts.insert(insert, current);
+ ++insert;
+ } else if (iit->moveId == -1 && insert->moveId == -1) {
+ // If neither the current nor existing insert has a moveId add the current insert
+ // to the existing one.
+ if (offset < insert->count) {
+ insert->index -= current.count;
+ insert->count += current.count;
+ } else {
+ insert->index += insertCount;
+ insert->count += current.count;
+ ++insert;
+ }
+ } else if (offset < insert->count) {
+ // If either insert has a moveId then split the existing insert and insert the
+ // current one in the middle.
+ if (offset > 0) {
+ insert = m_inserts.insert(insert, Insert(
+ insert->index + insertCount, offset, insert->moveId, insert->offset));
+ ++insert;
+ insert->index += offset;
+ insert->count -= offset;
+ insert->offset += offset;
+ }
+ insert = m_inserts.insert(insert, current);
+ ++insert;
+ } else {
+ insert->index += insertCount;
+ ++insert;
+ insert = m_inserts.insert(insert, current);
+ ++insert;
+ }
+ }
+ insertCount += current.count;
+ }
+ for (; insert != m_inserts.end(); ++insert)
+ insert->index += insertCount;
+ m_difference += insertCount;
+}
+
+/*!
+ Applies a combined list of \a removes and \a inserts to a change set. This is equivalent
+ calling \l remove() followed by \l insert() with the same lists.
+*/
+
+void QQmlChangeSet::move(const QVector<Remove> &removes, const QVector<Insert> &inserts)
+{
+ QVector<Remove> r = removes;
+ QVector<Insert> i = inserts;
+ remove(&r, &i);
+ insert(i);
+}
+
+/*!
+ Applies a list of \a changes to a change set.
+*/
+
+void QQmlChangeSet::change(const QVector<Change> &changes)
+{
+ QVector<Change> c = changes;
+ change(&c);
+}
+
+void QQmlChangeSet::change(QVector<Change> *changes)
+{
+ QVector<Insert>::iterator insert = m_inserts.begin();
+ QVector<Change>::iterator change = m_changes.begin();
+ for (QVector<Change>::iterator cit = changes->begin(); cit != changes->end(); ++cit) {
+ for (; insert != m_inserts.end() && insert->end() < cit->index; ++insert) {}
+ for (; insert != m_inserts.end() && insert->index < cit->end(); ++insert) {
+ const int offset = insert->index - cit->index;
+ const int count = cit->count + cit->index - insert->index - insert->count;
+ if (offset == 0) {
+ cit->index = insert->index + insert->count;
+ cit->count = count;
+ } else {
+ cit = changes->insert(++cit, Change(insert->index + insert->count, count));
+ --cit;
+ cit->count = offset;
+ }
+ }
+
+ for (; change != m_changes.end() && change->index + change->count < cit->index; ++change) {}
+ if (change == m_changes.end() || change->index > cit->index + cit->count) {
+ if (cit->count > 0) {
+ change = m_changes.insert(change, *cit);
+ ++change;
+ }
+ } else {
+ if (cit->index < change->index) {
+ change->count += change->index - cit->index;
+ change->index = cit->index;
+ }
+
+ if (cit->index + cit->count > change->index + change->count) {
+ change->count = cit->index + cit->count - change->index;
+ QVector<Change>::iterator cbegin = change;
+ QVector<Change>::iterator cend = ++cbegin;
+ for (; cend != m_changes.end() && cend->index <= change->index + change->count; ++cend) {
+ if (cend->index + cend->count > change->index + change->count)
+ change->count = cend->index + cend->count - change->index;
+ }
+ if (cbegin != cend) {
+ change = m_changes.erase(cbegin, cend);
+ --change;
+ }
+ }
+ }
+ }
+}
+
+/*!
+ Prints the contents of a change \a set to the \a debug stream.
+*/
+
+QDebug operator <<(QDebug debug, const QQmlChangeSet &set)
+{
+ debug.nospace() << "QQmlChangeSet(";
+ foreach (const QQmlChangeSet::Remove &remove, set.removes()) debug << remove;
+ foreach (const QQmlChangeSet::Insert &insert, set.inserts()) debug << insert;
+ foreach (const QQmlChangeSet::Change &change, set.changes()) debug << change;
+ return debug.nospace() << ')';
+}
+
+/*!
+ Prints a \a remove to the \a debug stream.
+*/
+
+QDebug operator <<(QDebug debug, const QQmlChangeSet::Remove &remove)
+{
+ if (remove.moveId == -1) {
+ return (debug.nospace()
+ << "Remove(" << remove.index
+ << ',' << remove.count
+ << ')').space();
+ } else {
+ return (debug.nospace()
+ << "Remove(" << remove.index
+ << ',' << remove.count
+ << ',' << remove.moveId
+ << ',' << remove.offset
+ << ')').space();
+ }
+}
+
+/*!
+ Prints an \a insert to the \a debug stream.
+*/
+
+QDebug operator <<(QDebug debug, const QQmlChangeSet::Insert &insert)
+{
+ if (insert.moveId == -1) {
+ return (debug.nospace()
+ << "Insert(" << insert.index
+ << ',' << insert.count
+ << ')').space();
+ } else {
+ return (debug.nospace()
+ << "Insert(" << insert.index
+ << ',' << insert.count
+ << ',' << insert.moveId
+ << ',' << insert.offset
+ << ')').space();
+ }
+}
+
+/*!
+ Prints a \a change to the \a debug stream.
+*/
+
+QDebug operator <<(QDebug debug, const QQmlChangeSet::Change &change)
+{
+ return (debug.nospace() << "Change(" << change.index << ',' << change.count << ')').space();
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/qml/util/qqmlchangeset_p.h b/src/qml/util/qqmlchangeset_p.h
new file mode 100644
index 0000000000..acafbd4eec
--- /dev/null
+++ b/src/qml/util/qqmlchangeset_p.h
@@ -0,0 +1,167 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLCHANGESET_P_H
+#define QQMLCHANGESET_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 <QtCore/qdebug.h>
+#include <QtCore/qvector.h>
+#include <QtQml/private/qtqmlglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QML_PRIVATE_EXPORT QQmlChangeSet
+{
+public:
+ struct MoveKey
+ {
+ MoveKey() : moveId(-1), offset(0) {}
+ MoveKey(int moveId, int offset) : moveId(moveId), offset(offset) {}
+ int moveId;
+ int offset;
+ };
+
+ struct Change
+ {
+ Change() : index(0), count(0), moveId(-1) {}
+ Change(int index, int count, int moveId = -1, int offset = 0)
+ : index(index), count(count), moveId(moveId), offset(offset) {}
+
+ int index;
+ int count;
+ int moveId;
+ int offset;
+
+ bool isMove() const { return moveId >= 0; }
+
+ MoveKey moveKey(int index) const {
+ return MoveKey(moveId, index - Change::index + offset); }
+
+ int start() const { return index; }
+ int end() const { return index + count; }
+ };
+
+
+ struct Insert : public Change
+ {
+ Insert() {}
+ Insert(int index, int count, int moveId = -1, int offset = 0)
+ : Change(index, count, moveId, offset) {}
+ };
+
+ struct Remove : public Change
+ {
+ Remove() {}
+ Remove(int index, int count, int moveId = -1, int offset = 0)
+ : Change(index, count, moveId, offset) {}
+ };
+
+ QQmlChangeSet();
+ QQmlChangeSet(const QQmlChangeSet &changeSet);
+ ~QQmlChangeSet();
+
+ QQmlChangeSet &operator =(const QQmlChangeSet &changeSet);
+
+ const QVector<Remove> &removes() const { return m_removes; }
+ const QVector<Insert> &inserts() const { return m_inserts; }
+ const QVector<Change> &changes() const { return m_changes; }
+
+ void insert(int index, int count);
+ void remove(int index, int count);
+ void move(int from, int to, int count, int moveId);
+ void change(int index, int count);
+
+ void insert(const QVector<Insert> &inserts);
+ void remove(const QVector<Remove> &removes, QVector<Insert> *inserts = 0);
+ void move(const QVector<Remove> &removes, const QVector<Insert> &inserts);
+ void change(const QVector<Change> &changes);
+ void apply(const QQmlChangeSet &changeSet);
+
+ bool isEmpty() const { return m_removes.empty() && m_inserts.empty() && m_changes.isEmpty(); }
+
+ void clear()
+ {
+ m_removes.clear();
+ m_inserts.clear();
+ m_changes.clear();
+ m_difference = 0;
+ }
+
+ int difference() const { return m_difference; }
+
+private:
+ void remove(QVector<Remove> *removes, QVector<Insert> *inserts);
+ void change(QVector<Change> *changes);
+
+ QVector<Remove> m_removes;
+ QVector<Insert> m_inserts;
+ QVector<Change> m_changes;
+ int m_difference;
+};
+
+Q_DECLARE_TYPEINFO(QQmlChangeSet::Change, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(QQmlChangeSet::Remove, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(QQmlChangeSet::Insert, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(QQmlChangeSet::MoveKey, Q_PRIMITIVE_TYPE);
+
+inline uint qHash(const QQmlChangeSet::MoveKey &key) { return qHash(qMakePair(key.moveId, key.offset)); }
+inline bool operator ==(const QQmlChangeSet::MoveKey &l, const QQmlChangeSet::MoveKey &r) {
+ return l.moveId == r.moveId && l.offset == r.offset; }
+
+Q_QML_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet::Remove &remove);
+Q_QML_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet::Insert &insert);
+Q_QML_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet::Change &change);
+Q_QML_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet &change);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/util/qqmllistaccessor.cpp b/src/qml/util/qqmllistaccessor.cpp
new file mode 100644
index 0000000000..2a2bd74a54
--- /dev/null
+++ b/src/qml/util/qqmllistaccessor.cpp
@@ -0,0 +1,138 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmllistaccessor_p.h"
+
+#include <private/qqmlmetatype_p.h>
+
+#include <QtCore/qstringlist.h>
+#include <QtCore/qdebug.h>
+
+// ### Remove me
+#include <private/qqmlengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+QQmlListAccessor::QQmlListAccessor()
+: m_type(Invalid)
+{
+}
+
+QQmlListAccessor::~QQmlListAccessor()
+{
+}
+
+QVariant QQmlListAccessor::list() const
+{
+ return d;
+}
+
+void QQmlListAccessor::setList(const QVariant &v, QQmlEngine *engine)
+{
+ d = v;
+
+ QQmlEnginePrivate *enginePrivate = engine?QQmlEnginePrivate::get(engine):0;
+
+ if (!d.isValid()) {
+ m_type = Invalid;
+ } else if (d.userType() == QVariant::StringList) {
+ m_type = StringList;
+ } else if (d.userType() == QMetaType::QVariantList) {
+ m_type = VariantList;
+ } else if (d.canConvert(QVariant::Int)) {
+ m_type = Integer;
+ } else if ((!enginePrivate && QQmlMetaType::isQObject(d.userType())) ||
+ (enginePrivate && enginePrivate->isQObject(d.userType()))) {
+ QObject *data = enginePrivate?enginePrivate->toQObject(v):QQmlMetaType::toQObject(v);
+ d = QVariant::fromValue(data);
+ m_type = Instance;
+ } else if (d.userType() == qMetaTypeId<QQmlListReference>()) {
+ m_type = ListProperty;
+ } else {
+ m_type = Instance;
+ }
+}
+
+int QQmlListAccessor::count() const
+{
+ switch(m_type) {
+ case StringList:
+ return qvariant_cast<QStringList>(d).count();
+ case VariantList:
+ return qvariant_cast<QVariantList>(d).count();
+ case ListProperty:
+ return ((QQmlListReference *)d.constData())->count();
+ case Instance:
+ return 1;
+ case Integer:
+ return d.toInt();
+ default:
+ case Invalid:
+ return 0;
+ }
+}
+
+QVariant QQmlListAccessor::at(int idx) const
+{
+ Q_ASSERT(idx >= 0 && idx < count());
+ switch(m_type) {
+ case StringList:
+ return QVariant::fromValue(qvariant_cast<QStringList>(d).at(idx));
+ case VariantList:
+ return qvariant_cast<QVariantList>(d).at(idx);
+ case ListProperty:
+ return QVariant::fromValue(((QQmlListReference *)d.constData())->at(idx));
+ case Instance:
+ return d;
+ case Integer:
+ return QVariant(idx);
+ default:
+ case Invalid:
+ return QVariant();
+ }
+}
+
+bool QQmlListAccessor::isValid() const
+{
+ return m_type != Invalid;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/util/qqmllistaccessor_p.h b/src/qml/util/qqmllistaccessor_p.h
new file mode 100644
index 0000000000..71f7542105
--- /dev/null
+++ b/src/qml/util/qqmllistaccessor_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLLISTACCESSOR_H
+#define QQMLLISTACCESSOR_H
+
+#include <QtCore/QVariant>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQmlEngine;
+class Q_AUTOTEST_EXPORT QQmlListAccessor
+{
+public:
+ QQmlListAccessor();
+ ~QQmlListAccessor();
+
+ QVariant list() const;
+ void setList(const QVariant &, QQmlEngine * = 0);
+
+ bool isValid() const;
+
+ int count() const;
+ QVariant at(int) const;
+
+ enum Type { Invalid, StringList, VariantList, ListProperty, Instance, Integer };
+ Type type() const { return m_type; }
+
+private:
+ Type m_type;
+ QVariant d;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QQMLLISTACCESSOR_H
diff --git a/src/qml/util/qqmllistcompositor.cpp b/src/qml/util/qqmllistcompositor.cpp
new file mode 100644
index 0000000000..75d2f67b51
--- /dev/null
+++ b/src/qml/util/qqmllistcompositor.cpp
@@ -0,0 +1,1484 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmllistcompositor_p.h"
+
+#include <QtCore/qvarlengtharray.h>
+
+//#define QT_QML_VERIFY_MINIMAL
+//#define QT_QML_VERIFY_INTEGRITY
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QQmlListCompositor
+ \brief The QQmlListCompositor class provides a lookup table for filtered, or re-ordered list
+ indexes.
+ \internal
+
+ QQmlListCompositor is intended as an aid for developing proxy models. It doesn't however
+ directly proxy a list or model, instead a range of indexes from one or many lists can be
+ inserted into the compositor and then categorized and shuffled around and it will manage the
+ task of translating from an index in the combined space into an index in a particular list.
+
+ Within a compositor indexes are categorized into groups where a group is a sub-set of the
+ total indexes referenced by the compositor, each with an address space ranging from 0 to
+ the number of indexes in the group - 1. Group memberships are independent of each other with
+ the one exception that items always retain the same order so if an index is moved within a
+ group, its position in other groups will change as well.
+
+ The iterator classes encapsulate information about a specific position in a compositor group.
+ This includes a source list, the index of an item within that list and the groups that item
+ is a member of. The iterator for a specific position in a group can be retrieved with the
+ find() function and the addition and subtraction operators of the iterators can be used to
+ navigate to adjacent items in the same group.
+
+ Items can be added to the compositor with the append() and insert() functions, group
+ membership can be changed with the setFlags() and clearFlags() functions, and the position
+ of items in the compositor can be changed with the move() function. Each of these functions
+ optionally returns a list of the changes made to indexes within each group which can then
+ be propagated to view so that it can correctly refresh its contents; e.g. 3 items
+ removed at index 6, and 5 items inserted at index 1. The notification changes are always
+ ordered from the start of the list to the end and accumulate, so if 5 items are removed at
+ index 4, one is skipped and then 3 move are removed, the changes returned are 5 items removed
+ at index 4, followed by 3 items removed at index 4.
+
+ When the contents of a source list change, the mappings within the compositor can be updated
+ with the listItemsInserted(), listItemsRemoved(), listItemsMoved(), and listItemsChanged()
+ functions. Like the direct manipulation functions these too return a list of group indexes
+ affected by the change. If items are removed from a source list they are also removed from
+ any groups they belong to with the one exception being items belonging to the \l Cache group.
+ When items belonging to this group are removed the list, index, and other group membership
+ information are discarded but Cache membership is retained until explicitly removed. This
+ allows the cache index to be retained until cached resources for that item are actually
+ released.
+
+ Internally the index mapping is stored as a list of Range objects, each has a list identifier,
+ a start index, a count, and a set of flags which represent group membership and some other
+ properties. The group index of a range is the sum of all preceding ranges that are members of
+ that group. To avoid the inefficiency of iterating over potentially all ranges when looking
+ for a specific index, each time a lookup is done the range and its indexes are cached and the
+ next lookup is done relative to this. This works out to near constant time in most relevant
+ use cases because successive index lookups are most frequently adjacent. The total number of
+ ranges is often quite small, which helps as well. If there is a need for faster random access
+ then a skip list like index may be an appropriate addition.
+
+ \sa VisualDataModel
+*/
+
+#ifdef QT_QML_VERIFY_MINIMAL
+#define QT_QML_VERIFY_INTEGRITY
+/*
+ Diagnostic to verify there are no consecutive ranges, or that the compositor contains the
+ most compact representation possible.
+
+ Returns false and prints a warning if any range has a starting index equal to the end
+ (index + count) index of the previous range, and both ranges also have the same flags and list
+ property.
+
+ If there are no consecutive ranges this will return true.
+*/
+
+static bool qt_verifyMinimal(
+ const QQmlListCompositor::iterator &begin,
+ const QQmlListCompositor::iterator &end)
+{
+ bool minimal = true;
+ int index = 0;
+
+ for (const QQmlListCompositor::Range *range = begin->next; range != *end; range = range->next, ++index) {
+ if (range->previous->list == range->list
+ && range->previous->flags == (range->flags & ~QQmlListCompositor::AppendFlag)
+ && range->previous->end() == range->index) {
+ qWarning() << index << "Consecutive ranges";
+ qWarning() << *range->previous;
+ qWarning() << *range;
+ minimal = false;
+ }
+ }
+
+ return minimal;
+}
+
+#endif
+
+#ifdef QT_QML_VERIFY_INTEGRITY
+static bool qt_printInfo(const QQmlListCompositor &compositor)
+{
+ qWarning() << compositor;
+ return true;
+}
+
+/*
+ Diagnostic to verify the integrity of a compositor.
+
+ Per range this verifies there are no invalid range combinations, that non-append ranges have
+ positive non-zero counts, and that list ranges have non-negative indexes.
+
+ Accumulatively this verifies that the cached total group counts match the sum of counts
+ of member ranges.
+*/
+
+static bool qt_verifyIntegrity(
+ const QQmlListCompositor::iterator &begin,
+ const QQmlListCompositor::iterator &end,
+ const QQmlListCompositor::iterator &cachedIt)
+{
+ bool valid = true;
+
+ int index = 0;
+ QQmlListCompositor::iterator it;
+ for (it = begin; *it != *end; *it = it->next) {
+ if (it->count == 0 && !it->append()) {
+ qWarning() << index << "Empty non-append range";
+ valid = false;
+ }
+ if (it->count < 0) {
+ qWarning() << index << "Negative count";
+ valid = false;
+ }
+ if (it->list && it->flags != QQmlListCompositor::CacheFlag && it->index < 0) {
+ qWarning() << index <<"Negative index";
+ valid = false;
+ }
+ if (it->previous->next != it.range) {
+ qWarning() << index << "broken list: it->previous->next != it.range";
+ valid = false;
+ }
+ if (it->next->previous != it.range) {
+ qWarning() << index << "broken list: it->next->previous != it.range";
+ valid = false;
+ }
+ if (*it == *cachedIt) {
+ for (int i = 0; i < end.groupCount; ++i) {
+ int groupIndex = it.index[i];
+ if (cachedIt->flags & (1 << i))
+ groupIndex += cachedIt.offset;
+ if (groupIndex != cachedIt.index[i]) {
+ qWarning() << index
+ << "invalid cached index"
+ << QQmlListCompositor::Group(i)
+ << "Expected:"
+ << groupIndex
+ << "Actual"
+ << cachedIt.index[i]
+ << cachedIt;
+ valid = false;
+ }
+ }
+ }
+ it.incrementIndexes(it->count);
+ ++index;
+ }
+
+ for (int i = 0; i < end.groupCount; ++i) {
+ if (end.index[i] != it.index[i]) {
+ qWarning() << "Group" << i << "count invalid. Expected:" << end.index[i] << "Actual:" << it.index[i];
+ valid = false;
+ }
+ }
+ return valid;
+}
+#endif
+
+#if defined(QT_QML_VERIFY_MINIMAL)
+# define QT_QML_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!(qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt) \
+ && qt_verifyMinimal(iterator(m_ranges.next, 0, Default, m_groupCount), m_end)) \
+ && qt_printInfo(*this)));
+#elif defined(QT_QML_VERIFY_INTEGRITY)
+# define QT_QML_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt) \
+ && qt_printInfo(*this)));
+#else
+# define QT_QML_VERIFY_LISTCOMPOSITOR
+#endif
+
+//#define QT_QML_TRACE_LISTCOMPOSITOR(args) qDebug() << m_end.index[1] << m_end.index[0] << Q_FUNC_INFO args;
+#define QT_QML_TRACE_LISTCOMPOSITOR(args)
+
+QQmlListCompositor::iterator &QQmlListCompositor::iterator::operator +=(int difference)
+{
+ // Update all indexes to the start of the range.
+ decrementIndexes(offset);
+
+ // If the iterator group isn't a member of the current range ignore the current offset.
+ if (!(range->flags & groupFlag))
+ offset = 0;
+
+ offset += difference;
+
+ // Iterate backwards looking for a range with a positive offset.
+ while (offset <= 0 && range->previous->flags) {
+ range = range->previous;
+ if (range->flags & groupFlag)
+ offset += range->count;
+ decrementIndexes(range->count);
+ }
+
+ // Iterate forwards looking for the first range which contains both the offset and the
+ // iterator group.
+ while (range->flags && (offset >= range->count || !(range->flags & groupFlag))) {
+ if (range->flags & groupFlag)
+ offset -= range->count;
+ incrementIndexes(range->count);
+ range = range->next;
+ }
+
+ // Update all the indexes to inclue the remaining offset.
+ incrementIndexes(offset);
+
+ return *this;
+}
+
+QQmlListCompositor::insert_iterator &QQmlListCompositor::insert_iterator::operator +=(int difference)
+{
+ iterator::operator +=(difference);
+
+ // If the previous range contains the append flag move the iterator to the tail of the previous
+ // range so that appended appear after the insert position.
+ if (offset == 0 && range->previous->append()) {
+ range = range->previous;
+ offset = range->inGroup() ? range->count : 0;
+ }
+
+ return *this;
+}
+
+
+/*!
+ Constructs an empty list compositor.
+*/
+
+QQmlListCompositor::QQmlListCompositor()
+ : m_end(m_ranges.next, 0, Default, 2)
+ , m_cacheIt(m_end)
+ , m_groupCount(2)
+ , m_defaultFlags(PrependFlag | DefaultFlag)
+ , m_removeFlags(AppendFlag | PrependFlag | GroupMask)
+ , m_moveId(0)
+{
+}
+
+/*!
+ Destroys a list compositor.
+*/
+
+QQmlListCompositor::~QQmlListCompositor()
+{
+ for (Range *next, *range = m_ranges.next; range != &m_ranges; range = next) {
+ next = range->next;
+ delete range;
+ }
+}
+
+/*!
+ Inserts a range with the given source \a list, start \a index, \a count and \a flags, in front
+ of the existing range \a before.
+*/
+
+inline QQmlListCompositor::Range *QQmlListCompositor::insert(
+ Range *before, void *list, int index, int count, uint flags)
+{
+ return new Range(before, list, index, count, flags);
+}
+
+/*!
+ Erases a \a range from the compositor.
+
+ Returns a pointer to the next range in the compositor.
+*/
+
+inline QQmlListCompositor::Range *QQmlListCompositor::erase(
+ Range *range)
+{
+ Range *next = range->next;
+ next->previous = range->previous;
+ next->previous->next = range->next;
+ delete range;
+ return next;
+}
+
+/*!
+ Sets the number (\a count) of possible groups that items may belong to in a compositor.
+*/
+
+void QQmlListCompositor::setGroupCount(int count)
+{
+ m_groupCount = count;
+ m_end = iterator(&m_ranges, 0, Default, m_groupCount);
+ m_cacheIt = m_end;
+}
+
+/*!
+ Returns the number of items that belong to a \a group.
+*/
+
+int QQmlListCompositor::count(Group group) const
+{
+ return m_end.index[group];
+}
+
+/*!
+ Returns an iterator representing the item at \a index in a \a group.
+
+ The index must be between 0 and count(group) - 1.
+*/
+
+QQmlListCompositor::iterator QQmlListCompositor::find(Group group, int index)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< group << index)
+ Q_ASSERT(index >=0 && index < count(group));
+ if (m_cacheIt == m_end) {
+ m_cacheIt = iterator(m_ranges.next, 0, group, m_groupCount);
+ m_cacheIt += index;
+ } else {
+ const int offset = index - m_cacheIt.index[group];
+ m_cacheIt.setGroup(group);
+ m_cacheIt += offset;
+ }
+ Q_ASSERT(m_cacheIt.index[group] == index);
+ Q_ASSERT(m_cacheIt->inGroup(group));
+ QT_QML_VERIFY_LISTCOMPOSITOR
+ return m_cacheIt;
+}
+
+/*!
+ Returns an iterator representing the item at \a index in a \a group.
+
+ The index must be between 0 and count(group) - 1.
+*/
+
+QQmlListCompositor::iterator QQmlListCompositor::find(Group group, int index) const
+{
+ return const_cast<QQmlListCompositor *>(this)->find(group, index);
+}
+
+/*!
+ Returns an iterator representing an insert position in front of the item at \a index in a
+ \a group.
+
+ The iterator for an insert position can sometimes resolve to a different Range than a regular
+ iterator. This is because when items are inserted on a boundary between Ranges, if the first
+ range has the Append flag set then the items should be inserted into that range to ensure
+ that the append position for the existing range remains after the insert position.
+
+ The index must be between 0 and count(group) - 1.
+*/
+
+QQmlListCompositor::insert_iterator QQmlListCompositor::findInsertPosition(Group group, int index)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< group << index)
+ Q_ASSERT(index >=0 && index <= count(group));
+ insert_iterator it;
+ if (m_cacheIt == m_end) {
+ it = iterator(m_ranges.next, 0, group, m_groupCount);
+ it += index;
+ } else {
+ const int offset = index - m_cacheIt.index[group];
+ it = m_cacheIt;
+ it.setGroup(group);
+ it += offset;
+ }
+ Q_ASSERT(it.index[group] == index);
+ return it;
+}
+
+/*!
+ Appends a range of \a count indexes starting at \a index from a \a list into a compositor
+ with the given \a flags.
+
+ If supplied the \a inserts list will be populated with the positions of the inserted items
+ in each group.
+*/
+
+void QQmlListCompositor::append(
+ void *list, int index, int count, uint flags, QVector<Insert> *inserts)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count << flags)
+ insert(m_end, list, index, count, flags, inserts);
+}
+
+/*!
+ Inserts a range of \a count indexes starting at \a index from a \a list with the given \a flags
+ into a \a group at index \a before.
+
+ If supplied the \a inserts list will be populated with the positions of items inserted into
+ each group.
+*/
+
+void QQmlListCompositor::insert(
+ Group group, int before, void *list, int index, int count, uint flags, QVector<Insert> *inserts)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< group << before << list << index << count << flags)
+ insert(findInsertPosition(group, before), list, index, count, flags, inserts);
+}
+
+/*!
+ Inserts a range of \a count indexes starting at \a index from a \a list with the given \a flags
+ into a compositor at position \a before.
+
+ If supplied the \a inserts list will be populated with the positions of items inserted into
+ each group.
+*/
+
+QQmlListCompositor::iterator QQmlListCompositor::insert(
+ iterator before, void *list, int index, int count, uint flags, QVector<Insert> *inserts)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< before << list << index << count << flags)
+ if (inserts) {
+ inserts->append(Insert(before, count, flags & GroupMask));
+ }
+ if (before.offset > 0) {
+ // Inserting into the middle of a range. Split it two and update the iterator so it's
+ // positioned at the start of the second half.
+ *before = insert(
+ *before, before->list, before->index, before.offset, before->flags & ~AppendFlag)->next;
+ before->index += before.offset;
+ before->count -= before.offset;
+ before.offset = 0;
+ }
+
+
+ if (!(flags & AppendFlag) && *before != m_ranges.next
+ && before->previous->list == list
+ && before->previous->flags == flags
+ && (!list || before->previous->end() == index)) {
+ // The insert arguments represent a continuation of the previous range so increment
+ // its count instead of inserting a new range.
+ before->previous->count += count;
+ before.incrementIndexes(count, flags);
+ } else {
+ *before = insert(*before, list, index, count, flags);
+ before.offset = 0;
+ }
+
+ if (!(flags & AppendFlag) && before->next != &m_ranges
+ && before->list == before->next->list
+ && before->flags == before->next->flags
+ && (!list || before->end() == before->next->index)) {
+ // The current range and the next are continuous so add their counts and delete one.
+ before->next->index = before->index;
+ before->next->count += before->count;
+ *before = erase(*before);
+ }
+
+ m_end.incrementIndexes(count, flags);
+ m_cacheIt = before;
+ QT_QML_VERIFY_LISTCOMPOSITOR
+ return before;
+}
+
+/*!
+ Sets the given flags \a flags on \a count items belonging to \a group starting at the position
+ identified by \a fromGroup and the index \a from.
+
+ If supplied the \a inserts list will be populated with insert notifications for affected groups.
+*/
+
+void QQmlListCompositor::setFlags(
+ Group fromGroup, int from, int count, Group group, int flags, QVector<Insert> *inserts)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << count << group << flags)
+ setFlags(find(fromGroup, from), count, group, flags, inserts);
+}
+
+/*!
+ Sets the given flags \a flags on \a count items belonging to \a group starting at the position
+ \a from.
+
+ If supplied the \a inserts list will be populated with insert notifications for affected groups.
+*/
+
+void QQmlListCompositor::setFlags(
+ iterator from, int count, Group group, uint flags, QVector<Insert> *inserts)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< from << count << flags)
+ if (!flags || !count)
+ return;
+
+ if (from != group) {
+ // Skip to the next full range if the start one is not a member of the target group.
+ from.incrementIndexes(from->count - from.offset);
+ from.offset = 0;
+ *from = from->next;
+ } else if (from.offset > 0) {
+ // If the start position is mid range split off the portion unaffected.
+ *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next;
+ from->index += from.offset;
+ from->count -= from.offset;
+ from.offset = 0;
+ }
+
+ for (; count > 0; *from = from->next) {
+ if (from != from.group) {
+ // Skip ranges that are not members of the target group.
+ from.incrementIndexes(from->count);
+ continue;
+ }
+ // Find the number of items affected in the current range.
+ const int difference = qMin(count, from->count);
+ count -= difference;
+
+ // Determine the actual changes made to the range and increment counts accordingly.
+ const uint insertFlags = ~from->flags & flags;
+ const uint setFlags = (from->flags | flags) & ~AppendFlag;
+ if (insertFlags && inserts)
+ inserts->append(Insert(from, difference, insertFlags | (from->flags & CacheFlag)));
+ m_end.incrementIndexes(difference, insertFlags);
+ from.incrementIndexes(difference, setFlags);
+
+ if (from->previous != &m_ranges
+ && from->previous->list == from->list
+ && (!from->list || from->previous->end() == from->index)
+ && from->previous->flags == setFlags) {
+ // If the additional flags make the current range a continuation of the previous
+ // then move the affected items over to the previous range.
+ from->previous->count += difference;
+ from->index += difference;
+ from->count -= difference;
+ if (from->count == 0) {
+ // Delete the current range if it is now empty, preserving the append flag
+ // in the previous range.
+ if (from->append())
+ from->previous->flags |= AppendFlag;
+ *from = erase(*from)->previous;
+ continue;
+ } else {
+ break;
+ }
+ } else if (!insertFlags) {
+ // No new flags, so roll onto the next range.
+ from.incrementIndexes(from->count - difference);
+ continue;
+ } else if (difference < from->count) {
+ // Create a new range with the updated flags, and remove the affected items
+ // from the current range.
+ *from = insert(*from, from->list, from->index, difference, setFlags)->next;
+ from->index += difference;
+ from->count -= difference;
+ } else {
+ // The whole range is affected so simply update the flags.
+ from->flags |= flags;
+ continue;
+ }
+ from.incrementIndexes(from->count);
+ }
+
+ if (from->previous != &m_ranges
+ && from->previous->list == from->list
+ && (!from->list || from->previous->end() == from->index)
+ && from->previous->flags == (from->flags & ~AppendFlag)) {
+ // If the following range is now a continuation, merge it with its previous range.
+ from.offset = from->previous->count;
+ from->previous->count += from->count;
+ from->previous->flags = from->flags;
+ *from = erase(*from)->previous;
+ }
+ m_cacheIt = from;
+ QT_QML_VERIFY_LISTCOMPOSITOR
+}
+
+/*!
+ Clears the given flags \a flags on \a count items belonging to \a group starting at the position
+ \a from.
+
+ If supplied the \a removes list will be populated with remove notifications for affected groups.
+*/
+
+void QQmlListCompositor::clearFlags(
+ Group fromGroup, int from, int count, Group group, uint flags, QVector<Remove> *removes)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << count << group << flags)
+ clearFlags(find(fromGroup, from), count, group, flags, removes);
+}
+
+/*!
+ Clears the given flags \a flags on \a count items belonging to \a group starting at the position
+ identified by \a fromGroup and the index \a from.
+
+ If supplied the \a removes list will be populated with remove notifications for affected groups.
+*/
+
+void QQmlListCompositor::clearFlags(
+ iterator from, int count, Group group, uint flags, QVector<Remove> *removes)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< from << count << flags)
+ if (!flags || !count)
+ return;
+
+ const bool clearCache = flags & CacheFlag;
+
+ if (from != group) {
+ // Skip to the next full range if the start one is not a member of the target group.
+ from.incrementIndexes(from->count - from.offset);
+ from.offset = 0;
+ *from = from->next;
+ } else if (from.offset > 0) {
+ // If the start position is mid range split off the portion unaffected.
+ *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next;
+ from->index += from.offset;
+ from->count -= from.offset;
+ from.offset = 0;
+ }
+
+ for (; count > 0; *from = from->next) {
+ if (from != group) {
+ // Skip ranges that are not members of the target group.
+ from.incrementIndexes(from->count);
+ continue;
+ }
+ // Find the number of items affected in the current range.
+ const int difference = qMin(count, from->count);
+ count -= difference;
+
+
+ // Determine the actual changes made to the range and decrement counts accordingly.
+ const uint removeFlags = from->flags & flags & ~(AppendFlag | PrependFlag);
+ const uint clearedFlags = from->flags & ~(flags | AppendFlag | UnresolvedFlag);
+ if (removeFlags && removes) {
+ const int maskedFlags = clearCache
+ ? (removeFlags & ~CacheFlag)
+ : (removeFlags | (from->flags & CacheFlag));
+ if (maskedFlags)
+ removes->append(Remove(from, difference, maskedFlags));
+ }
+ m_end.decrementIndexes(difference, removeFlags);
+ from.incrementIndexes(difference, clearedFlags);
+
+ if (from->previous != &m_ranges
+ && from->previous->list == from->list
+ && (!from->list || clearedFlags == CacheFlag || from->previous->end() == from->index)
+ && from->previous->flags == clearedFlags) {
+ // If the removed flags make the current range a continuation of the previous
+ // then move the affected items over to the previous range.
+ from->previous->count += difference;
+ from->index += difference;
+ from->count -= difference;
+ if (from->count == 0) {
+ // Delete the current range if it is now empty, preserving the append flag
+ if (from->append())
+ from->previous->flags |= AppendFlag;
+ *from = erase(*from)->previous;
+ } else {
+ from.incrementIndexes(from->count);
+ }
+ } else if (difference < from->count) {
+ // Create a new range with the reduced flags, and remove the affected items from
+ // the current range.
+ if (clearedFlags)
+ *from = insert(*from, from->list, from->index, difference, clearedFlags)->next;
+ from->index += difference;
+ from->count -= difference;
+ from.incrementIndexes(from->count);
+ } else if (clearedFlags) {
+ // The whole range is affected so simply update the flags.
+ from->flags &= ~flags;
+ } else {
+ // All flags have been removed from the range so remove it.
+ *from = erase(*from)->previous;
+ }
+ }
+
+ if (*from != &m_ranges && from->previous != &m_ranges
+ && from->previous->list == from->list
+ && (!from->list || from->previous->end() == from->index)
+ && from->previous->flags == (from->flags & ~AppendFlag)) {
+ // If the following range is now a continuation, merge it with its previous range.
+ from.offset = from->previous->count;
+ from->previous->count += from->count;
+ from->previous->flags = from->flags;
+ *from = erase(*from)->previous;
+ }
+ m_cacheIt = from;
+ QT_QML_VERIFY_LISTCOMPOSITOR
+}
+
+bool QQmlListCompositor::verifyMoveTo(
+ Group fromGroup, int from, Group toGroup, int to, int count, Group group) const
+{
+ if (group != toGroup) {
+ // determine how many items from the destination group intersect with the source group.
+ iterator fromIt = find(fromGroup, from);
+
+ int intersectingCount = 0;
+
+ for (; count > 0; *fromIt = fromIt->next) {
+ if (*fromIt == &m_ranges)
+ return false;
+ if (!fromIt->inGroup(group))
+ continue;
+ if (fromIt->inGroup(toGroup))
+ intersectingCount += qMin(count, fromIt->count - fromIt.offset);
+ count -= fromIt->count - fromIt.offset;
+ fromIt.offset = 0;
+ }
+ count = intersectingCount;
+ }
+
+ return to >= 0 && to + count <= m_end.index[toGroup];
+}
+
+/*!
+ \internal
+
+ Moves \a count items belonging to \a moveGroup from the index \a from in \a fromGroup
+ to the index \a to in \a toGroup.
+
+ If \a removes and \a inserts are not null they will be populated with per group notifications
+ of the items moved.
+ */
+
+void QQmlListCompositor::move(
+ Group fromGroup,
+ int from,
+ Group toGroup,
+ int to,
+ int count,
+ Group moveGroup,
+ QVector<Remove> *removes,
+ QVector<Insert> *inserts)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << toGroup << to << count)
+ Q_ASSERT(count > 0);
+ Q_ASSERT(from >=0);
+ Q_ASSERT(verifyMoveTo(fromGroup, from, toGroup, to, count, moveGroup));
+
+ // Find the position of the first item to move.
+ iterator fromIt = find(fromGroup, from);
+
+ if (fromIt != moveGroup) {
+ // If the range at the from index doesn't contain items from the move group; skip
+ // to the next range.
+ fromIt.incrementIndexes(fromIt->count - fromIt.offset);
+ fromIt.offset = 0;
+ *fromIt = fromIt->next;
+ } else if (fromIt.offset > 0) {
+ // If the range at the from index contains items from the move group and the index isn't
+ // at the start of the range; split the range at the index and move the iterator to start
+ // of the second range.
+ *fromIt = insert(
+ *fromIt, fromIt->list, fromIt->index, fromIt.offset, fromIt->flags & ~AppendFlag)->next;
+ fromIt->index += fromIt.offset;
+ fromIt->count -= fromIt.offset;
+ fromIt.offset = 0;
+ }
+
+ // Remove count items belonging to the move group from the list.
+ Range movedFlags;
+ for (int moveId = m_moveId; count > 0;) {
+ if (fromIt != moveGroup) {
+ // Skip ranges not containing items from the move group.
+ fromIt.incrementIndexes(fromIt->count);
+ *fromIt = fromIt->next;
+ continue;
+ }
+ int difference = qMin(count, fromIt->count);
+
+ // Create a new static range containing the moved items from an existing range.
+ new Range(
+ &movedFlags,
+ fromIt->list,
+ fromIt->index,
+ difference,
+ fromIt->flags & ~(PrependFlag | AppendFlag));
+ // Remove moved items from the count, the existing range, and a remove notification.
+ if (removes)
+ removes->append(Remove(fromIt, difference, fromIt->flags, ++moveId));
+ count -= difference;
+ fromIt->count -= difference;
+
+ // If the existing range contains the prepend flag replace the removed items with
+ // a placeholder range for new items inserted into the source model.
+ int removeIndex = fromIt->index;
+ if (fromIt->prepend()
+ && fromIt->previous != &m_ranges
+ && fromIt->previous->flags == PrependFlag
+ && fromIt->previous->list == fromIt->list
+ && fromIt->previous->end() == fromIt->index) {
+ // Grow the previous range instead of creating a new one if possible.
+ fromIt->previous->count += difference;
+ } else if (fromIt->prepend()) {
+ *fromIt = insert(*fromIt, fromIt->list, removeIndex, difference, PrependFlag)->next;
+ }
+ fromIt->index += difference;
+
+ if (fromIt->count == 0) {
+ // If the existing range has no items remaining; remove it from the list.
+ if (fromIt->append())
+ fromIt->previous->flags |= AppendFlag;
+ *fromIt = erase(*fromIt);
+
+ // If the ranges before and after the removed range can be joined, do so.
+ if (*fromIt != m_ranges.next && fromIt->flags == PrependFlag
+ && fromIt->previous != &m_ranges
+ && fromIt->previous->flags == PrependFlag
+ && fromIt->previous->list == fromIt->list
+ && fromIt->previous->end() == fromIt->index) {
+ fromIt.incrementIndexes(fromIt->count);
+ fromIt->previous->count += fromIt->count;
+ *fromIt = erase(*fromIt);
+ }
+ } else if (count > 0) {
+ *fromIt = fromIt->next;
+ }
+ }
+
+ // Try and join the range following the removed items to the range preceding it.
+ if (*fromIt != m_ranges.next
+ && *fromIt != &m_ranges
+ && fromIt->previous->list == fromIt->list
+ && (!fromIt->list || fromIt->previous->end() == fromIt->index)
+ && fromIt->previous->flags == (fromIt->flags & ~AppendFlag)) {
+ if (fromIt == fromIt.group)
+ fromIt.offset = fromIt->previous->count;
+ fromIt.offset = fromIt->previous->count;
+ fromIt->previous->count += fromIt->count;
+ fromIt->previous->flags = fromIt->flags;
+ *fromIt = erase(*fromIt)->previous;
+ }
+
+ // Find the destination position of the move.
+ insert_iterator toIt = fromIt;
+ toIt.setGroup(toGroup);
+
+ const int difference = to - toIt.index[toGroup];
+ toIt += difference;
+
+ // If the insert position is part way through a range; split it and move the iterator to the
+ // start of the second range.
+ if (toIt.offset > 0) {
+ *toIt = insert(*toIt, toIt->list, toIt->index, toIt.offset, toIt->flags & ~AppendFlag)->next;
+ toIt->index += toIt.offset;
+ toIt->count -= toIt.offset;
+ toIt.offset = 0;
+ }
+
+ // Insert the moved ranges before the insert iterator, growing the previous range if that
+ // is an option.
+ for (Range *range = movedFlags.previous; range != &movedFlags; range = range->previous) {
+ if (*toIt != &m_ranges
+ && range->list == toIt->list
+ && (!range->list || range->end() == toIt->index)
+ && range->flags == (toIt->flags & ~AppendFlag)) {
+ toIt->index -= range->count;
+ toIt->count += range->count;
+ } else {
+ *toIt = insert(*toIt, range->list, range->index, range->count, range->flags);
+ }
+ }
+
+ // Try and join the range after the inserted ranges to the last range inserted.
+ if (*toIt != m_ranges.next
+ && toIt->previous->list == toIt->list
+ && (!toIt->list || (toIt->previous->end() == toIt->index && toIt->previous->flags == (toIt->flags & ~AppendFlag)))) {
+ toIt.offset = toIt->previous->count;
+ toIt->previous->count += toIt->count;
+ toIt->previous->flags = toIt->flags;
+ *toIt = erase(*toIt)->previous;
+ }
+ // Create insert notification for the ranges moved.
+ Insert insert(toIt, 0, 0, 0);
+ for (Range *next, *range = movedFlags.next; range != &movedFlags; range = next) {
+ insert.count = range->count;
+ insert.flags = range->flags;
+ if (inserts) {
+ insert.moveId = ++m_moveId;
+ inserts->append(insert);
+ }
+ for (int i = 0; i < m_groupCount; ++i) {
+ if (insert.inGroup(i))
+ insert.index[i] += range->count;
+ }
+
+ next = range->next;
+ delete range;
+ }
+
+ m_cacheIt = toIt;
+
+ QT_QML_VERIFY_LISTCOMPOSITOR
+}
+
+/*!
+ Clears the contents of a compositor.
+*/
+
+void QQmlListCompositor::clear()
+{
+ QT_QML_TRACE_LISTCOMPOSITOR( )
+ for (Range *range = m_ranges.next; range != &m_ranges; range = erase(range)) {}
+ m_end = iterator(m_ranges.next, 0, Default, m_groupCount);
+ m_cacheIt = m_end;
+}
+
+void QQmlListCompositor::listItemsInserted(
+ QVector<Insert> *translatedInsertions,
+ void *list,
+ const QVector<QQmlChangeSet::Insert> &insertions,
+ const QVector<MovedFlags> *movedFlags)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< list << insertions)
+ for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) {
+ if (it->list != list || it->flags == CacheFlag) {
+ // Skip ranges that don't reference list.
+ it.incrementIndexes(it->count);
+ continue;
+ } else if (it->flags & MovedFlag) {
+ // Skip ranges that were already moved in listItemsRemoved.
+ it->flags &= ~MovedFlag;
+ it.incrementIndexes(it->count);
+ continue;
+ }
+ foreach (const QQmlChangeSet::Insert &insertion, insertions) {
+ int offset = insertion.index - it->index;
+ if ((offset > 0 && offset < it->count)
+ || (offset == 0 && it->prepend())
+ || (offset == it->count && it->append())) {
+ // The insert index is within the current range.
+ if (it->prepend()) {
+ // The range has the prepend flag set so we insert new items into the range.
+ uint flags = m_defaultFlags;
+ if (insertion.isMove()) {
+ // If the insert was part of a move replace the default flags with
+ // the flags from the source range.
+ for (QVector<MovedFlags>::const_iterator move = movedFlags->begin();
+ move != movedFlags->end();
+ ++move) {
+ if (move->moveId == insertion.moveId) {
+ flags = move->flags;
+ break;
+ }
+ }
+ }
+ if (flags & ~(AppendFlag | PrependFlag)) {
+ // If any items are added to groups append an insert notification.
+ Insert translatedInsert(it, insertion.count, flags, insertion.moveId);
+ for (int i = 0; i < m_groupCount; ++i) {
+ if (it->inGroup(i))
+ translatedInsert.index[i] += offset;
+ }
+ translatedInsertions->append(translatedInsert);
+ }
+ if ((it->flags & ~AppendFlag) == flags) {
+ // Accumulate items on the current range it its flags are the same as
+ // the insert flags.
+ it->count += insertion.count;
+ } else if (offset == 0
+ && it->previous != &m_ranges
+ && it->previous->list == list
+ && it->previous->end() == insertion.index
+ && it->previous->flags == flags) {
+ // Attempt to append to the previous range if the insert position is at
+ // the start of the current range.
+ it->previous->count += insertion.count;
+ it->index += insertion.count;
+ it.incrementIndexes(insertion.count);
+ } else {
+ if (offset > 0) {
+ // Divide the current range at the insert position.
+ it.incrementIndexes(offset);
+ *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next;
+ }
+ // Insert a new range, and increment the start index of the current range.
+ *it = insert(*it, it->list, insertion.index, insertion.count, flags)->next;
+ it.incrementIndexes(insertion.count, flags);
+ it->index += offset + insertion.count;
+ it->count -= offset;
+ }
+ m_end.incrementIndexes(insertion.count, flags);
+ } else {
+ // The range doesn't have the prepend flag set so split the range into parts;
+ // one before the insert position and one after the last inserted item.
+ if (offset > 0) {
+ *it = insert(*it, it->list, it->index, offset, it->flags)->next;
+ it->index += offset;
+ it->count -= offset;
+ }
+ it->index += insertion.count;
+ }
+ } else if (offset <= 0) {
+ // The insert position was before the current range so increment the start index.
+ it->index += insertion.count;
+ }
+ }
+ it.incrementIndexes(it->count);
+ }
+ m_cacheIt = m_end;
+ QT_QML_VERIFY_LISTCOMPOSITOR
+}
+
+/*!
+ Updates the contents of a compositor when \a count items are inserted into a \a list at
+ \a index.
+
+ This corrects the indexes of each range for that list in the compositor, splitting the range
+ in two if the insert index is in the middle of that range. If a range at the insert position
+ has the Prepend flag set then a new range will be inserted at that position with the groups
+ specified in defaultGroups(). If the insert index corresponds to the end of a range with
+ the Append flag set a new range will be inserted before the end of the append range.
+
+ The \a translatedInsertions list is populated with insert notifications for affected
+ groups.
+*/
+
+void QQmlListCompositor::listItemsInserted(
+ void *list, int index, int count, QVector<Insert> *translatedInsertions)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count)
+ Q_ASSERT(count > 0);
+
+ QVector<QQmlChangeSet::Insert> insertions;
+ insertions.append(QQmlChangeSet::Insert(index, count));
+
+ listItemsInserted(translatedInsertions, list, insertions);
+}
+
+void QQmlListCompositor::listItemsRemoved(
+ QVector<Remove> *translatedRemovals,
+ void *list,
+ QVector<QQmlChangeSet::Remove> *removals,
+ QVector<QQmlChangeSet::Insert> *insertions,
+ QVector<MovedFlags> *movedFlags)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< list << *removals)
+
+ for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) {
+ if (it->list != list || it->flags == CacheFlag) {
+ // Skip ranges that don't reference list.
+ it.incrementIndexes(it->count);
+ continue;
+ }
+ bool removed = false;
+ for (QVector<QQmlChangeSet::Remove>::iterator removal = removals->begin();
+ !removed && removal != removals->end();
+ ++removal) {
+ int relativeIndex = removal->index - it->index;
+ int itemsRemoved = removal->count;
+ if (relativeIndex + removal->count > 0 && relativeIndex < it->count) {
+ // If the current range intersects the remove; remove the intersecting items.
+ const int offset = qMax(0, relativeIndex);
+ int removeCount = qMin(it->count, relativeIndex + removal->count) - offset;
+ it->count -= removeCount;
+ int removeFlags = it->flags & m_removeFlags;
+ Remove translatedRemoval(it, removeCount, it->flags);
+ for (int i = 0; i < m_groupCount; ++i) {
+ if (it->inGroup(i))
+ translatedRemoval.index[i] += offset;
+ }
+ if (removal->isMove()) {
+ // If the removal was part of a move find the corresponding insert.
+ QVector<QQmlChangeSet::Insert>::iterator insertion = insertions->begin();
+ for (; insertion != insertions->end() && insertion->moveId != removal->moveId;
+ ++insertion) {}
+ Q_ASSERT(insertion != insertions->end());
+ Q_ASSERT(insertion->count == removal->count);
+
+ if (relativeIndex < 0) {
+ // If the remove started before the current range, split it and the
+ // corresponding insert so we're only working with intersecting part.
+ int splitMoveId = ++m_moveId;
+ removal = removals->insert(removal, QQmlChangeSet::Remove(
+ removal->index, -relativeIndex, splitMoveId));
+ ++removal;
+ removal->count -= -relativeIndex;
+ insertion = insertions->insert(insertion, QQmlChangeSet::Insert(
+ insertion->index, -relativeIndex, splitMoveId));
+ ++insertion;
+ insertion->index += -relativeIndex;
+ insertion->count -= -relativeIndex;
+ }
+
+ if (it->prepend()) {
+ // If the current range has the prepend flag preserve its flags to transfer
+ // to its new location.
+ removeFlags |= it->flags & CacheFlag;
+ translatedRemoval.moveId = ++m_moveId;
+ movedFlags->append(MovedFlags(m_moveId, it->flags & ~AppendFlag));
+
+ if (removeCount < removal->count) {
+ // If the remove doesn't encompass all of the current range,
+ // split it and the corresponding insert.
+ removal = removals->insert(removal, QQmlChangeSet::Remove(
+ removal->index, removeCount, translatedRemoval.moveId));
+ ++removal;
+ insertion = insertions->insert(insertion, QQmlChangeSet::Insert(
+ insertion->index, removeCount, translatedRemoval.moveId));
+ ++insertion;
+
+ removal->count -= removeCount;
+ insertion->index += removeCount;
+ insertion->count -= removeCount;
+ } else {
+ // If there's no need to split the move simply replace its moveId
+ // with that of the translated move.
+ removal->moveId = translatedRemoval.moveId;
+ insertion->moveId = translatedRemoval.moveId;
+ }
+ } else {
+ // If the current range doesn't have prepend flags then insert a new range
+ // with list indexes from the corresponding insert location. The MoveFlag
+ // is to notify listItemsInserted that it can skip evaluation of that range.
+ if (offset > 0) {
+ *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next;
+ it->index += offset;
+ it->count -= offset;
+ it.incrementIndexes(offset);
+ }
+ if (it->previous != &m_ranges
+ && it->previous->list == it->list
+ && it->end() == insertion->index
+ && it->previous->flags == (it->flags | MovedFlag)) {
+ it->previous->count += removeCount;
+ } else {
+ *it = insert(*it, it->list, insertion->index, removeCount, it->flags | MovedFlag)->next;
+ }
+ // Clear the changed flags as the item hasn't been removed.
+ translatedRemoval.flags = 0;
+ removeFlags = 0;
+ }
+ } else if (it->inCache()) {
+ // If not moving and the current range has the cache flag, insert a new range
+ // with just the cache flag set to retain caching information for the removed
+ // range.
+ if (offset > 0) {
+ *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next;
+ it->index += offset;
+ it->count -= offset;
+ it.incrementIndexes(offset);
+ }
+ if (it->previous != &m_ranges
+ && it->previous->list == it->list
+ && it->previous->flags == CacheFlag) {
+ it->previous->count += removeCount;
+ } else {
+ *it = insert(*it, it->list, -1, removeCount, CacheFlag)->next;
+ }
+ it.index[Cache] += removeCount;
+ }
+ if (removeFlags & GroupMask)
+ translatedRemovals->append(translatedRemoval);
+ m_end.decrementIndexes(removeCount, removeFlags);
+ if (it->count == 0 && !it->append()) {
+ // Erase empty non-append ranges.
+ *it = erase(*it)->previous;
+ removed = true;
+ } else if (relativeIndex <= 0) {
+ // If the remove started before the current range move the start index of
+ // the range to the remove index.
+ it->index = removal->index;
+ }
+ } else if (relativeIndex < 0) {
+ // If the remove was before the current range decrement the start index by the
+ // number of items removed.
+ it->index -= itemsRemoved;
+
+ if (it->previous != &m_ranges
+ && it->previous->list == it->list
+ && it->previous->end() == it->index
+ && it->previous->flags == (it->flags & ~AppendFlag)) {
+ // Compress ranges made continuous by the removal of separating ranges.
+ it.decrementIndexes(it->previous->count);
+ it->previous->count += it->count;
+ it->previous->flags = it->flags;
+ *it = erase(*it)->previous;
+ }
+ }
+ }
+ if (it->flags == CacheFlag && it->next->flags == CacheFlag && it->next->list == it->list) {
+ // Compress consecutive cache only ranges.
+ it.index[Cache] += it->next->count;
+ it->count += it->next->count;
+ erase(it->next);
+ } else if (!removed) {
+ it.incrementIndexes(it->count);
+ }
+ }
+ m_cacheIt = m_end;
+ QT_QML_VERIFY_LISTCOMPOSITOR
+}
+
+
+/*!
+ Updates the contents of a compositor when \a count items are removed from a \a list at
+ \a index.
+
+ Ranges that intersect the specified range are removed from the compositor and the indexes of
+ ranges after index + count are updated.
+
+ The \a translatedRemovals list is populated with remove notifications for the affected
+ groups.
+*/
+
+
+void QQmlListCompositor::listItemsRemoved(
+ void *list, int index, int count, QVector<Remove> *translatedRemovals)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count)
+ Q_ASSERT(count >= 0);
+
+ QVector<QQmlChangeSet::Remove> removals;
+ removals.append(QQmlChangeSet::Remove(index, count));
+ listItemsRemoved(translatedRemovals, list, &removals, 0, 0);
+}
+
+/*!
+ Updates the contents of a compositor when \a count items are removed from a \a list at
+ \a index.
+
+ Ranges that intersect the specified range are removed from the compositor and the indexes of
+ ranges after index + count are updated.
+
+ The \a translatedRemovals and translatedInserts lists are populated with move
+ notifications for the affected groups.
+*/
+
+void QQmlListCompositor::listItemsMoved(
+ void *list,
+ int from,
+ int to,
+ int count,
+ QVector<Remove> *translatedRemovals,
+ QVector<Insert> *translatedInsertions)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< list << from << to << count)
+ Q_ASSERT(count >= 0);
+
+ QVector<QQmlChangeSet::Remove> removals;
+ QVector<QQmlChangeSet::Insert> insertions;
+ QVector<MovedFlags> movedFlags;
+ removals.append(QQmlChangeSet::Remove(from, count, 0));
+ insertions.append(QQmlChangeSet::Insert(to, count, 0));
+
+ listItemsRemoved(translatedRemovals, list, &removals, &insertions, &movedFlags);
+ listItemsInserted(translatedInsertions, list, insertions, &movedFlags);
+}
+
+void QQmlListCompositor::listItemsChanged(
+ QVector<Change> *translatedChanges,
+ void *list,
+ const QVector<QQmlChangeSet::Change> &changes)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< list << changes)
+ for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) {
+ if (it->list != list || it->flags == CacheFlag) {
+ it.incrementIndexes(it->count);
+ continue;
+ } else if (!it->inGroup()) {
+ continue;
+ }
+ foreach (const QQmlChangeSet::Change &change, changes) {
+ const int offset = change.index - it->index;
+ if (offset + change.count > 0 && offset < it->count) {
+ const int changeOffset = qMax(0, offset);
+ const int changeCount = qMin(it->count, offset + change.count) - changeOffset;
+
+ Change translatedChange(it, changeCount, it->flags);
+ for (int i = 0; i < m_groupCount; ++i) {
+ if (it->inGroup(i))
+ translatedChange.index[i] += changeOffset;
+ }
+ translatedChanges->append(translatedChange);
+ }
+ }
+ it.incrementIndexes(it->count);
+ }
+}
+
+
+/*!
+ Translates the positions of \a count changed items at \a index in a \a list.
+
+ The \a translatedChanges list is populated with change notifications for the
+ affected groups.
+*/
+
+void QQmlListCompositor::listItemsChanged(
+ void *list, int index, int count, QVector<Change> *translatedChanges)
+{
+ QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count)
+ Q_ASSERT(count >= 0);
+ QVector<QQmlChangeSet::Change> changes;
+ changes.append(QQmlChangeSet::Change(index, count));
+ listItemsChanged(translatedChanges, list, changes);
+}
+
+void QQmlListCompositor::transition(
+ Group from,
+ Group to,
+ QVector<QQmlChangeSet::Remove> *removes,
+ QVector<QQmlChangeSet::Insert> *inserts)
+{
+ int removeCount = 0;
+ for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) {
+ if (it == from && it != to) {
+ removes->append(QQmlChangeSet::Remove(it.index[from]- removeCount, it->count));
+ removeCount += it->count;
+ } else if (it != from && it == to) {
+ inserts->append(QQmlChangeSet::Insert(it.index[to], it->count));
+ }
+ it.incrementIndexes(it->count);
+ }
+}
+
+/*!
+ \internal
+ Writes the name of \a group to \a debug.
+*/
+
+QDebug operator <<(QDebug debug, const QQmlListCompositor::Group &group)
+{
+ switch (group) {
+ case QQmlListCompositor::Cache: return debug << "Cache";
+ case QQmlListCompositor::Default: return debug << "Default";
+ default: return (debug.nospace() << "Group" << int(group)).space();
+ }
+
+}
+
+/*!
+ \internal
+ Writes the contents of \a range to \a debug.
+*/
+
+QDebug operator <<(QDebug debug, const QQmlListCompositor::Range &range)
+{
+ (debug.nospace()
+ << "Range("
+ << range.list) << ' '
+ << range.index << ' '
+ << range.count << ' '
+ << (range.isUnresolved() ? 'U' : '0')
+ << (range.append() ? 'A' : '0')
+ << (range.prepend() ? 'P' : '0');
+ for (int i = QQmlListCompositor::MaximumGroupCount - 1; i >= 2; --i)
+ debug << (range.inGroup(i) ? '1' : '0');
+ return (debug
+ << (range.inGroup(QQmlListCompositor::Default) ? 'D' : '0')
+ << (range.inGroup(QQmlListCompositor::Cache) ? 'C' : '0'));
+}
+
+static void qt_print_indexes(QDebug &debug, int count, const int *indexes)
+{
+ for (int i = count - 1; i >= 0; --i)
+ debug << indexes[i];
+}
+
+/*!
+ \internal
+ Writes the contents of \a it to \a debug.
+*/
+
+QDebug operator <<(QDebug debug, const QQmlListCompositor::iterator &it)
+{
+ (debug.nospace() << "iterator(" << it.group).space() << "offset:" << it.offset;
+ qt_print_indexes(debug, it.groupCount, it.index);
+ return ((debug << **it).nospace() << ')').space();
+}
+
+static QDebug qt_print_change(QDebug debug, const char *name, const QQmlListCompositor::Change &change)
+{
+ debug.nospace() << name << '(' << change.moveId << ' ' << change.count << ' ';
+ for (int i = QQmlListCompositor::MaximumGroupCount - 1; i >= 2; --i)
+ debug << (change.inGroup(i) ? '1' : '0');
+ debug << (change.inGroup(QQmlListCompositor::Default) ? 'D' : '0')
+ << (change.inGroup(QQmlListCompositor::Cache) ? 'C' : '0');
+ int i = QQmlListCompositor::MaximumGroupCount - 1;
+ for (; i >= 0 && !change.inGroup(i); --i) {}
+ for (; i >= 0; --i)
+ debug << ' ' << change.index[i];
+ return (debug << ')').maybeSpace();
+}
+
+/*!
+ \internal
+ Writes the contents of \a change to \a debug.
+*/
+
+QDebug operator <<(QDebug debug, const QQmlListCompositor::Change &change)
+{
+ return qt_print_change(debug, "Change", change);
+}
+
+/*!
+ \internal
+ Writes the contents of \a remove to \a debug.
+*/
+
+QDebug operator <<(QDebug debug, const QQmlListCompositor::Remove &remove)
+{
+ return qt_print_change(debug, "Remove", remove);
+}
+
+/*!
+ \internal
+ Writes the contents of \a insert to \a debug.
+*/
+
+QDebug operator <<(QDebug debug, const QQmlListCompositor::Insert &insert)
+{
+ return qt_print_change(debug, "Insert", insert);
+}
+
+/*!
+ \internal
+ Writes the contents of \a list to \a debug.
+*/
+
+QDebug operator <<(QDebug debug, const QQmlListCompositor &list)
+{
+ int indexes[QQmlListCompositor::MaximumGroupCount];
+ for (int i = 0; i < QQmlListCompositor::MaximumGroupCount; ++i)
+ indexes[i] = 0;
+ debug.nospace() << "QQmlListCompositor(";
+ qt_print_indexes(debug, list.m_groupCount, list.m_end.index);
+ for (QQmlListCompositor::Range *range = list.m_ranges.next; range != &list.m_ranges; range = range->next) {
+ (debug << '\n').space();
+ qt_print_indexes(debug, list.m_groupCount, indexes);
+ debug << ' ' << *range;
+
+ for (int i = 0; i < list.m_groupCount; ++i) {
+ if (range->inGroup(i))
+ indexes[i] += range->count;
+ }
+ }
+ return (debug << ')').maybeSpace();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/util/qqmllistcompositor_p.h b/src/qml/util/qqmllistcompositor_p.h
new file mode 100644
index 0000000000..5d87051582
--- /dev/null
+++ b/src/qml/util/qqmllistcompositor_p.h
@@ -0,0 +1,375 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLLISTCOMPOSITOR_P_H
+#define QQMLLISTCOMPOSITOR_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 <QtCore/qglobal.h>
+#include <QtCore/qvector.h>
+
+#include <private/qqmlchangeset_p.h>
+
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QQmlListCompositor
+{
+public:
+ enum { MinimumGroupCount = 3, MaximumGroupCount = 11 };
+
+ enum Group
+ {
+ Cache = 0,
+ Default = 1,
+ Persisted = 2
+ };
+
+ enum Flag
+ {
+ CacheFlag = 1 << Cache,
+ DefaultFlag = 1 << Default,
+ PersistedFlag = 1 << Persisted,
+ PrependFlag = 0x10000000,
+ AppendFlag = 0x20000000,
+ UnresolvedFlag = 0x40000000,
+ MovedFlag = 0x80000000,
+ GroupMask = ~(PrependFlag | AppendFlag | UnresolvedFlag | MovedFlag | CacheFlag)
+ };
+
+ class Range
+ {
+ public:
+ Range() : next(this), previous(this), list(0), index(0), count(0), flags(0) {}
+ Range(Range *next, void *list, int index, int count, uint flags)
+ : next(next), previous(next->previous), list(list), index(index), count(count), flags(flags) {
+ next->previous = this; previous->next = this; }
+
+ Range *next;
+ Range *previous;
+ void *list;
+ int index;
+ int count;
+ uint flags;
+
+ inline int start() const { return index; }
+ inline int end() const { return index + count; }
+
+ inline int groups() const { return flags & GroupMask; }
+
+ inline bool inGroup() const { return flags & GroupMask; }
+ inline bool inCache() const { return flags & CacheFlag; }
+ inline bool inGroup(int group) const { return flags & (1 << group); }
+ inline bool isUnresolved() const { return flags & UnresolvedFlag; }
+
+ inline bool prepend() const { return flags & PrependFlag; }
+ inline bool append() const { return flags & AppendFlag; }
+ };
+
+ class Q_AUTOTEST_EXPORT iterator
+ {
+ public:
+ inline iterator();
+ inline iterator(const iterator &it);
+ inline iterator(Range *range, int offset, Group group, int groupCount);
+ inline ~iterator() {}
+
+ bool operator ==(const iterator &it) const { return range == it.range && offset == it.offset; }
+ bool operator !=(const iterator &it) const { return range != it.range || offset != it.offset; }
+
+ bool operator ==(Group group) const { return range->flags & (1 << group); }
+ bool operator !=(Group group) const { return !(range->flags & (1 << group)); }
+
+ Range *&operator *() { return range; }
+ Range * const &operator *() const { return range; }
+ Range *operator ->() { return range; }
+ const Range *operator ->() const { return range; }
+
+ iterator &operator +=(int difference);
+
+ template<typename T> T *list() const { return static_cast<T *>(range->list); }
+ int modelIndex() const { return range->index + offset; }
+
+ void incrementIndexes(int difference) { incrementIndexes(difference, range->flags); }
+ void decrementIndexes(int difference) { decrementIndexes(difference, range->flags); }
+
+ inline void incrementIndexes(int difference, uint flags);
+ inline void decrementIndexes(int difference, uint flags);
+
+ void setGroup(Group g) { group = g; groupFlag = 1 << g; }
+
+ Range *range;
+ int offset;
+ Group group;
+ int groupFlag;
+ int groupCount;
+ union {
+ struct {
+ int cacheIndex;
+ };
+ int index[MaximumGroupCount];
+ };
+ };
+
+ class Q_AUTOTEST_EXPORT insert_iterator : public iterator
+ {
+ public:
+ inline insert_iterator() {}
+ inline insert_iterator(const iterator &it) : iterator(it) {}
+ inline insert_iterator(Range *, int, Group, int);
+ inline ~insert_iterator() {}
+
+ insert_iterator &operator +=(int difference);
+ };
+
+ struct Change
+ {
+ inline Change() {}
+ inline Change(iterator it, int count, uint flags, int moveId = -1);
+ int count;
+ uint flags;
+ int moveId;
+ union {
+ struct {
+ int cacheIndex;
+ };
+ int index[MaximumGroupCount];
+ };
+
+ inline bool isMove() const { return moveId >= 0; }
+ inline bool inCache() const { return flags & CacheFlag; }
+ inline bool inGroup() const { return flags & GroupMask; }
+ inline bool inGroup(int group) const { return flags & (CacheFlag << group); }
+
+ inline int groups() const { return flags & GroupMask; }
+ };
+
+ struct Insert : public Change
+ {
+ Insert() {}
+ Insert(iterator it, int count, uint flags, int moveId = -1)
+ : Change(it, count, flags, moveId) {}
+ };
+
+ struct Remove : public Change
+ {
+ Remove() {}
+ Remove(iterator it, int count, uint flags, int moveId = -1)
+ : Change(it, count, flags, moveId) {}
+ };
+
+ QQmlListCompositor();
+ ~QQmlListCompositor();
+
+ int defaultGroups() const { return m_defaultFlags & ~PrependFlag; }
+ void setDefaultGroups(int groups) { m_defaultFlags = groups | PrependFlag; }
+ void setDefaultGroup(Group group) { m_defaultFlags |= (1 << group); }
+ void clearDefaultGroup(Group group) { m_defaultFlags &= ~(1 << group); }
+ void setRemoveGroups(int groups) { m_removeFlags = PrependFlag | AppendFlag | groups; }
+ void setGroupCount(int count);
+
+ int count(Group group) const;
+ iterator find(Group group, int index);
+ iterator find(Group group, int index) const;
+ insert_iterator findInsertPosition(Group group, int index);
+
+ const iterator &end() { return m_end; }
+
+ void append(void *list, int index, int count, uint flags, QVector<Insert> *inserts = 0);
+ void insert(Group group, int before, void *list, int index, int count, uint flags, QVector<Insert> *inserts = 0);
+ iterator insert(iterator before, void *list, int index, int count, uint flags, QVector<Insert> *inserts = 0);
+
+ void setFlags(Group fromGroup, int from, int count, Group group, int flags, QVector<Insert> *inserts = 0);
+ void setFlags(iterator from, int count, Group group, uint flags, QVector<Insert> *inserts = 0);
+ void setFlags(Group fromGroup, int from, int count, uint flags, QVector<Insert> *inserts = 0) {
+ setFlags(fromGroup, from, count, fromGroup, flags, inserts); }
+ void setFlags(iterator from, int count, uint flags, QVector<Insert> *inserts = 0) {
+ setFlags(from, count, from.group, flags, inserts); }
+
+ void clearFlags(Group fromGroup, int from, int count, Group group, uint flags, QVector<Remove> *removals = 0);
+ void clearFlags(iterator from, int count, Group group, uint flags, QVector<Remove> *removals = 0);
+ void clearFlags(Group fromGroup, int from, int count, uint flags, QVector<Remove> *removals = 0) {
+ clearFlags(fromGroup, from, count, fromGroup, flags, removals); }
+ void clearFlags(iterator from, int count, uint flags, QVector<Remove> *removals = 0) {
+ clearFlags(from, count, from.group, flags, removals); }
+
+ bool verifyMoveTo(Group fromGroup, int from, Group toGroup, int to, int count, Group group) const;
+
+ void move(
+ Group fromGroup,
+ int from,
+ Group toGroup,
+ int to,
+ int count,
+ Group group,
+ QVector<Remove> *removals = 0,
+ QVector<Insert> *inserts = 0);
+ void clear();
+
+ void listItemsInserted(void *list, int index, int count, QVector<Insert> *inserts);
+ void listItemsRemoved(void *list, int index, int count, QVector<Remove> *removals);
+ void listItemsMoved(void *list, int from, int to, int count, QVector<Remove> *removals, QVector<Insert> *inserts);
+ void listItemsChanged(void *list, int index, int count, QVector<Change> *changes);
+
+ void transition(
+ Group from,
+ Group to,
+ QVector<QQmlChangeSet::Remove> *removes,
+ QVector<QQmlChangeSet::Insert> *inserts);
+
+private:
+ Range m_ranges;
+ iterator m_end;
+ iterator m_cacheIt;
+ int m_groupCount;
+ int m_defaultFlags;
+ int m_removeFlags;
+ int m_moveId;
+
+ inline Range *insert(Range *before, void *list, int index, int count, uint flags);
+ inline Range *erase(Range *range);
+
+ struct MovedFlags
+ {
+ MovedFlags() {}
+ MovedFlags(int moveId, uint flags) : moveId(moveId), flags(flags) {}
+
+ int moveId;
+ uint flags;
+ };
+
+ void listItemsRemoved(
+ QVector<Remove> *translatedRemovals,
+ void *list,
+ QVector<QQmlChangeSet::Remove> *removals,
+ QVector<QQmlChangeSet::Insert> *insertions = 0,
+ QVector<MovedFlags> *movedFlags = 0);
+ void listItemsInserted(
+ QVector<Insert> *translatedInsertions,
+ void *list,
+ const QVector<QQmlChangeSet::Insert> &insertions,
+ const QVector<MovedFlags> *movedFlags = 0);
+ void listItemsChanged(
+ QVector<Change> *translatedChanges,
+ void *list,
+ const QVector<QQmlChangeSet::Change> &changes);
+
+ friend Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor &list);
+};
+
+Q_DECLARE_TYPEINFO(QQmlListCompositor::Change, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(QQmlListCompositor::Remove, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(QQmlListCompositor::Insert, Q_PRIMITIVE_TYPE);
+
+inline QQmlListCompositor::iterator::iterator()
+ : range(0), offset(0), group(Default), groupCount(0) {}
+inline QQmlListCompositor::iterator::iterator(const iterator &it)
+ : range(it.range)
+ , offset(it.offset)
+ , group(it.group)
+ , groupFlag(it.groupFlag)
+ , groupCount(it.groupCount)
+{
+ for (int i = 0; i < groupCount; ++i)
+ index[i] = it.index[i];
+}
+
+inline QQmlListCompositor::iterator::iterator(
+ Range *range, int offset, Group group, int groupCount)
+ : range(range)
+ , offset(offset)
+ , group(group)
+ , groupFlag(1 << group)
+ , groupCount(groupCount)
+{
+ for (int i = 0; i < groupCount; ++i)
+ index[i] = 0;
+}
+
+inline void QQmlListCompositor::iterator::incrementIndexes(int difference, uint flags)
+{
+ for (int i = 0; i < groupCount; ++i) {
+ if (flags & (1 << i))
+ index[i] += difference;
+ }
+}
+
+inline void QQmlListCompositor::iterator::decrementIndexes(int difference, uint flags)
+{
+ for (int i = 0; i < groupCount; ++i) {
+ if (flags & (1 << i))
+ index[i] -= difference;
+ }
+}
+
+inline QQmlListCompositor::insert_iterator::insert_iterator(
+ Range *range, int offset, Group group, int groupCount)
+ : iterator(range, offset, group, groupCount) {}
+
+inline QQmlListCompositor::Change::Change(iterator it, int count, uint flags, int moveId)
+ : count(count), flags(flags), moveId(moveId)
+{
+ for (int i = 0; i < MaximumGroupCount; ++i)
+ index[i] = it.index[i];
+}
+
+Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Group &group);
+Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Range &range);
+Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::iterator &it);
+Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Change &change);
+Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Remove &remove);
+Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Insert &insert);
+Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor &list);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/util/util.pri b/src/qml/util/util.pri
index 3b121ba3cb..a9c5ffe9b7 100644
--- a/src/qml/util/util.pri
+++ b/src/qml/util/util.pri
@@ -1,5 +1,13 @@
SOURCES += \
+ $$PWD/qqmlchangeset.cpp \
+ $$PWD/qqmllistaccessor.cpp \
+ $$PWD/qqmllistcompositor.cpp \
+ $$PWD/qqmladaptormodel.cpp \
$$PWD/qqmlpropertymap.cpp
HEADERS += \
+ $$PWD/qqmlchangeset_p.h \
+ $$PWD/qqmllistaccessor_p.h \
+ $$PWD/qqmllistcompositor_p.h \
+ $$PWD/qqmladaptormodel_p.h \
$$PWD/qqmlpropertymap.h