aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml
diff options
context:
space:
mode:
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