aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/src/snippets/declarative/visualdatagroup.qml81
-rw-r--r--examples/declarative/modelviews/visualdatamodel/dragselection.qml198
-rw-r--r--src/declarative/items/qsgitemsmodule.cpp1
-rw-r--r--src/declarative/items/qsgvisualdatamodel.cpp1772
-rw-r--r--src/declarative/items/qsgvisualdatamodel_p.h120
-rw-r--r--src/declarative/util/qdeclarativechangeset.cpp6
-rw-r--r--src/declarative/util/qdeclarativechangeset_p.h9
-rw-r--r--src/declarative/util/qdeclarativelistcompositor.cpp1198
-rw-r--r--src/declarative/util/qdeclarativelistcompositor_p.h370
-rw-r--r--src/declarative/util/util.pri2
-rw-r--r--tests/auto/declarative/declarative.pro1
-rw-r--r--tests/auto/declarative/qdeclarativelistcompositor/qdeclarativelistcompositor.pro17
-rw-r--r--tests/auto/declarative/qdeclarativelistcompositor/tst_qdeclarativelistcompositor.cpp666
-rw-r--r--tests/auto/declarative/qsgvisualdatamodel/data/groups.qml32
-rw-r--r--tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp560
15 files changed, 4811 insertions, 222 deletions
diff --git a/doc/src/snippets/declarative/visualdatagroup.qml b/doc/src/snippets/declarative/visualdatagroup.qml
new file mode 100644
index 0000000000..feb41e27cb
--- /dev/null
+++ b/doc/src/snippets/declarative/visualdatagroup.qml
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+//![0]
+import QtQuick 2.0
+
+Rectangle {
+ width: 200; height: 100
+
+ VisualDataModel {
+ id: visualModel
+ model: ListModel {
+ ListElement { name: "Apple" }
+ ListElement { name: "Orange" }
+ }
+
+ groups: [
+ VisualDataGroup { name: "selected" }
+ ]
+
+ delegate: Rectangle {
+ id: item
+ height: 25
+ width: 200
+ Text {
+ text: {
+ var text = "Name: " + name
+ if (item.VisualDataModel.inSelected)
+ text += " (" + item.VisualDataModel.selectedIndex + ")"
+ return text;
+ }
+ }
+ MouseArea {
+ anchors.fill: parent
+ onClicked: item.VisualDataModel.inSelected = !item.VisualDataModel.inSelected
+ }
+ }
+ }
+
+ ListView {
+ anchors.fill: parent
+ model: visualModel
+ }
+}
+//![0]
diff --git a/examples/declarative/modelviews/visualdatamodel/dragselection.qml b/examples/declarative/modelviews/visualdatamodel/dragselection.qml
new file mode 100644
index 0000000000..d4412f9719
--- /dev/null
+++ b/examples/declarative/modelviews/visualdatamodel/dragselection.qml
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the examples of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+** the names of its contributors may be used to endorse or promote
+** products derived from this software without specific prior written
+** permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ id: root
+
+ width: 320
+ height: 480
+
+ property bool dragging: false
+
+ Component {
+ id: packageDelegate
+ Package {
+ id: packageRoot
+
+ MouseArea {
+ id: visibleContainer
+ Package.name: "visible"
+
+ width: 64
+ height: 64
+ enabled: packageRoot.VisualDataModel.inSelected
+
+ drag.target: draggable
+
+ Item {
+ id: draggable
+
+ width: 64
+ height: 64
+
+ anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter }
+
+ states: State {
+ when: visibleContainer.drag.active
+ AnchorChanges { target: draggable; anchors { horizontalCenter: undefined; verticalCenter: undefined} }
+ ParentChange { target: selectionView; parent: draggable; x: 0; y: 0 }
+ PropertyChanges { target: root; dragging: true }
+ ParentChange { target: draggable; parent: root }
+ }
+ }
+ DragTarget {
+ anchors.fill: parent
+ onEntered: visualModel.items.move(selectedItems, 0, packageRoot.VisualDataModel.itemsIndex, selectedItems.count)
+ }
+ }
+ Item {
+ id: selectionContainer
+ Package.name: "selection"
+
+ width: 64
+ height: 64
+
+ visible: PathView.onPath
+ }
+ Rectangle {
+ id: content
+ parent: visibleContainer
+
+ width: 58
+ height: 58
+
+ radius: 8
+
+ gradient: Gradient {
+ GradientStop { id: gradientStart; position: 0.0; color: "#8AC953" }
+ GradientStop { id: gradientEnd; position: 1.0; color: "#8BC953" }
+ }
+
+ border.width: 2
+ border.color: "#007423"
+
+ state: root.dragging && packageRoot.VisualDataModel.inSelected ? "selected" : "visible"
+
+ Text {
+ anchors.fill: parent
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: "white"
+ text: modelData
+ font.pixelSize: 18
+ }
+
+ Rectangle {
+ anchors { right: parent.right; top: parent.top; margins: 3 }
+ width: 12; height: 12
+ color: packageRoot.VisualDataModel.inSelected ? "black" : "white"
+ radius: 6
+
+ border.color: "white"
+ border.width: 2
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: packageRoot.VisualDataModel.inSelected = !packageRoot.VisualDataModel.inSelected
+ }
+ }
+
+ states: [
+ State {
+ name: "selected"
+ ParentChange { target: content; parent: selectionContainer; x: 3; y: 3 }
+ PropertyChanges { target: packageRoot; VisualDataModel.inItems: visibleContainer.drag.active }
+ PropertyChanges { target: gradientStart; color: "#017423" }
+ PropertyChanges { target: gradientStart; color: "#007423" }
+ }, State {
+ name: "visible"
+ PropertyChanges { target: packageRoot; VisualDataModel.inItems: true }
+ ParentChange { target: content; parent: visibleContainer; x: 3; y: 3 }
+ PropertyChanges { target: gradientStart; color: "#8AC953" }
+ PropertyChanges { target: gradientStart; color: "#8BC953" }
+ }
+ ]
+ transitions: Transition {
+ PropertyAction { target: packageRoot; properties: "VisualDataModel.inItems" }
+ ParentAnimation {
+ target: content
+ NumberAnimation { target: content; properties: "x,y"; duration: 500 }
+ }
+ ColorAnimation { targets: [gradientStart, gradientEnd]; duration: 500 }
+ }
+ }
+ }
+ }
+
+ VisualDataModel {
+ id: visualModel
+ model: 35
+ delegate: packageDelegate
+
+ groups: VisualDataGroup { id: selectedItems; name: "selected" }
+
+ Component.onCompleted: parts.selection.filterOnGroup = "selected"
+ }
+
+ PathView {
+ id: selectionView
+
+ height: 64
+ width: 64
+
+ model: visualModel.parts.selection
+
+ path: Path {
+ startX: 0
+ startY: 0
+ PathLine { x: 64; y: 64 }
+ }
+ }
+
+ GridView {
+ id: itemsView
+ anchors { fill: parent }
+ cellWidth: 64
+ cellHeight: 64
+ model: visualModel.parts.visible
+ }
+}
diff --git a/src/declarative/items/qsgitemsmodule.cpp b/src/declarative/items/qsgitemsmodule.cpp
index 41823c1a4d..db98dc4ad5 100644
--- a/src/declarative/items/qsgitemsmodule.cpp
+++ b/src/declarative/items/qsgitemsmodule.cpp
@@ -149,6 +149,7 @@ static void qt_sgitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QSGTextInput>(uri,major,minor,"TextInput");
qmlRegisterType<QSGViewSection>(uri,major,minor,"ViewSection");
qmlRegisterType<QSGVisualDataModel>(uri,major,minor,"VisualDataModel");
+ qmlRegisterType<QSGVisualDataGroup>(uri,major,minor,"VisualDataGroup");
qmlRegisterType<QSGVisualItemModel>(uri,major,minor,"VisualItemModel");
qmlRegisterType<QSGAnchors>();
diff --git a/src/declarative/items/qsgvisualdatamodel.cpp b/src/declarative/items/qsgvisualdatamodel.cpp
index 09f5d5d917..72cebd70a4 100644
--- a/src/declarative/items/qsgvisualdatamodel.cpp
+++ b/src/declarative/items/qsgvisualdatamodel.cpp
@@ -58,8 +58,10 @@
#include <private/qdeclarativeglobal_p.h>
#include <private/qmetaobjectbuilder_p.h>
#include <private/qdeclarativeproperty_p.h>
-#include <private/qdeclarativechangeset_p.h>
#include <private/qsgvisualadaptormodel_p.h>
+#include <private/qdeclarativechangeset_p.h>
+#include <private/qdeclarativelistcompositor_p.h>
+#include <private/qdeclarativeengine_p.h>
#include <private/qobject_p.h>
#include <QtCore/qhash.h>
@@ -67,10 +69,56 @@
QT_BEGIN_NAMESPACE
+typedef QDeclarativeListCompositor Compositor;
+
+class QSGVisualDataGroupEmitter
+{
+public:
+ virtual void emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) = 0;
+ virtual void createdPackage(int, QDeclarativePackage *) {}
+ virtual void destroyingPackage(QDeclarativePackage *) {}
+
+ QIntrusiveListNode emitterNode;
+};
+
+typedef QIntrusiveList<QSGVisualDataGroupEmitter, &QSGVisualDataGroupEmitter::emitterNode> QSGVisualDataGroupEmitterList;
+
+//---------------------------------------------------------------------------
+
+class QSGVisualDataGroupPrivate : public QObjectPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QSGVisualDataGroup)
+
+ QSGVisualDataGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {}
+
+ static QSGVisualDataGroupPrivate *get(QSGVisualDataGroup *group) {
+ return static_cast<QSGVisualDataGroupPrivate *>(QObjectPrivate::get(group)); }
+
+ void setModel(QSGVisualDataModel *model, Compositor::Group group);
+ void emitChanges(QV8Engine *engine);
+ void emitModelUpdated(bool reset);
+
+ void createdPackage(int index, QDeclarativePackage *package);
+ void destroyingPackage(QDeclarativePackage *package);
+
+ bool parseGroupArgs(QDeclarativeV8Function *args, int *index, int *count, int *groups) const;
+
+ Compositor::Group group;
+ QDeclarativeGuard<QSGVisualDataModel> model;
+ QSGVisualDataGroupEmitterList emitters;
+ QDeclarativeChangeSet changeSet;
+ QString name;
+ bool defaultInclude;
+};
+
+//---------------------------------------------------------------------------
+
+class QSGVisualDataModelCacheItem;
+class QSGVisualDataModelCacheMetaType;
class QSGVisualDataModelParts;
-class QSGVisualDataModelData;
-class QSGVisualDataModelDataMetaObject;
-class QSGVisualDataModelPrivate : public QObjectPrivate
+
+class QSGVisualDataModelPrivate : public QObjectPrivate, public QSGVisualDataGroupEmitter
{
Q_DECLARE_PUBLIC(QSGVisualDataModel)
public:
@@ -83,88 +131,91 @@ public:
void init();
void connectModel(QSGVisualAdaptorModel *model);
- QObject *object(int index, bool complete);
+ QObject *object(Compositor::Group group, int index, bool complete);
+ QSGItem *item(Compositor::Group group, int index, bool complete);
+ void destroy(QObject *object);
QSGVisualDataModel::ReleaseFlags release(QObject *object);
- QString stringValue(int index, const QString &name);
- void emitCreatedPackage(int index, QDeclarativePackage *package) {
- emit q_func()->createdPackage(index, package); }
- void emitDestroyingPackage(QDeclarativePackage *package) {
- emit q_func()->destroyingPackage(package); }
-
+ QString stringValue(Compositor::Group group, int index, const QString &name);
+ int cacheIndexOf(QObject *object) const;
+ void emitCreatedPackage(Compositor::iterator at, QDeclarativePackage *package);
+ void emitCreatedItem(int index, QSGItem *item) { emit q_func()->createdItem(index, item); }
+ void emitDestroyingPackage(QDeclarativePackage *package);
+ void emitDestroyingItem(QSGItem *item) { emit q_func()->destroyingItem(item); }
+
+ void updateFilterGroup();
+
+ void addGroups(Compositor::Group group, int index, int count, int groupFlags);
+ void removeGroups(Compositor::Group group, int index, int count, int groupFlags);
+ void setGroups(Compositor::Group group, int index, int count, int groupFlags);
+
+ void itemsInserted(
+ const QVector<Compositor::Insert> &inserts,
+ QVarLengthArray<QVector<QDeclarativeChangeSet::Insert>, Compositor::MaximumGroupCount> *translatedInserts,
+ QHash<int, QList<QSGVisualDataModelCacheItem *> > *movedItems = 0);
+ void itemsInserted(const QVector<Compositor::Insert> &inserts);
+ void itemsRemoved(
+ const QVector<Compositor::Remove> &removes,
+ QVarLengthArray<QVector<QDeclarativeChangeSet::Remove>, Compositor::MaximumGroupCount> *translatedRemoves,
+ QHash<int, QList<QSGVisualDataModelCacheItem *> > *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 QDeclarativeChangeSet &changeSet, bool reset);
+
+
+ static void group_append(QDeclarativeListProperty<QSGVisualDataGroup> *property, QSGVisualDataGroup *group);
+ static int group_count(QDeclarativeListProperty<QSGVisualDataGroup> *property);
+ static QSGVisualDataGroup *group_at(QDeclarativeListProperty<QSGVisualDataGroup> *property, int index);
QSGVisualAdaptorModel *m_adaptorModel;
QDeclarativeComponent *m_delegate;
+ QSGVisualDataModelCacheMetaType *m_cacheMetaType;
QDeclarativeGuard<QDeclarativeContext> m_context;
- struct ObjectRef {
- ObjectRef(QObject *object=0) : obj(object), ref(1) {}
- QObject *obj;
- int ref;
- };
- class Cache : public QHash<int, ObjectRef> {
- public:
- QObject *getItem(int index) {
- QObject *item = 0;
- QHash<int,ObjectRef>::iterator it = find(index);
- if (it != end()) {
- (*it).ref++;
- item = (*it).obj;
- }
- return item;
- }
- QObject *item(int index) {
- QObject *item = 0;
- QHash<int, ObjectRef>::const_iterator it = find(index);
- if (it != end())
- item = (*it).obj;
- return item;
- }
- void insertItem(int index, QObject *obj) {
- insert(index, ObjectRef(obj));
- }
- bool releaseItem(QObject *obj) {
- QHash<int, ObjectRef>::iterator it = begin();
- for (; it != end(); ++it) {
- ObjectRef &objRef = *it;
- if (objRef.obj == obj) {
- if (--objRef.ref == 0) {
- erase(it);
- return true;
- }
- break;
- }
- }
- return false;
- }
- };
-
- Cache m_cache;
- QHash<QObject *, QDeclarativePackage*> m_packaged;
-
+ QList<QSGVisualDataModelCacheItem *> m_cache;
QSGVisualDataModelParts *m_parts;
- friend class QSGVisualItemParts;
+ QSGVisualDataGroupEmitterList m_pendingParts;
- friend class QSGVisualDataModelData;
+ QDeclarativeListCompositor m_compositor;
+ QDeclarativeListCompositor::Group m_compositorGroup;
+ bool m_complete : 1;
bool m_delegateValidated : 1;
bool m_completePending : 1;
bool m_reset : 1;
+ bool m_transaction : 1;
- QDeclarativeChangeSet m_absoluteChangeSet;
-
+ QString m_filterGroup;
QList<QByteArray> watchedRoles;
+
+ union {
+ struct {
+ QSGVisualDataGroup *m_cacheItems;
+ QSGVisualDataGroup *m_items;
+ };
+ QSGVisualDataGroup *m_groups[Compositor::MaximumGroupCount];
+ };
+ int m_groupCount;
};
//---------------------------------------------------------------------------
-class QSGVisualPartsModel : public QSGVisualModel
+class QSGVisualPartsModel : public QSGVisualModel, public QSGVisualDataGroupEmitter
{
Q_OBJECT
-
+ Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup)
public:
QSGVisualPartsModel(QSGVisualDataModel *model, const QString &part, QObject *parent = 0);
~QSGVisualPartsModel();
+ QString filterGroup() const;
+ void setFilterGroup(const QString &group);
+ void resetFilterGroup();
+ void updateFilterGroup();
+ void updateFilterGroup(Compositor::Group group, const QDeclarativeChangeSet &changeSet);
+
int count() const;
bool isValid() const;
QSGItem *item(int index, bool complete=true);
@@ -176,15 +227,22 @@ public:
int indexOf(QSGItem *item, QObject *objectContext) const;
-public Q_SLOTS:
+ void emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset);
+
void createdPackage(int index, QDeclarativePackage *package);
void destroyingPackage(QDeclarativePackage *package);
+Q_SIGNALS:
+ void filterGroupChanged();
+
private:
QSGVisualDataModel *m_model;
QHash<QObject *, QDeclarativePackage *> m_packaged;
QString m_part;
+ QString m_filterGroup;
QList<QByteArray> m_watchedRoles;
+ Compositor::Group m_compositorGroup;
+ bool m_inheritGroup;
};
class QSGVisualDataModelPartsMetaObject : public QDeclarativeOpenMetaObject
@@ -203,9 +261,8 @@ Q_OBJECT
public:
QSGVisualDataModelParts(QSGVisualDataModel *parent);
-private:
- friend class QSGVisualDataModelPartsMetaObject;
QSGVisualDataModel *model;
+ QList<QSGVisualPartsModel *> models;
};
void QSGVisualDataModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop)
@@ -215,10 +272,10 @@ void QSGVisualDataModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilde
QVariant QSGVisualDataModelPartsMetaObject::initialValue(int id)
{
+ QSGVisualDataModelParts *parts = static_cast<QSGVisualDataModelParts *>(object());
QSGVisualPartsModel *m = new QSGVisualPartsModel(
- static_cast<QSGVisualDataModelParts *>(object())->model,
- QString::fromUtf8(name(id)),
- object());
+ parts->model, QString::fromUtf8(name(id)), parts);
+ parts->models.append(m);
return QVariant::fromValue(static_cast<QObject *>(m));
}
@@ -228,19 +285,77 @@ QSGVisualDataModelParts::QSGVisualDataModelParts(QSGVisualDataModel *parent)
new QSGVisualDataModelPartsMetaObject(this);
}
-QSGVisualDataModelPrivate::QSGVisualDataModelPrivate(QDeclarativeContext *ctxt)
- : m_adaptorModel(0)
- , m_delegate(0)
- , m_context(ctxt)
- , m_parts(0)
- , m_delegateValidated(false)
- , m_completePending(false)
- , m_reset(false)
+//---------------------------------------------------------------------------
+
+class QSGVisualDataModelCacheMetaType : public QDeclarativeRefCount
{
-}
+public:
+ QSGVisualDataModelCacheMetaType(QSGVisualDataModel *model, const QStringList &groupNames);
+ ~QSGVisualDataModelCacheMetaType();
+
+ int parseGroups(const QStringList &groupNames) const;
+ int parseGroups(QV8Engine *engine, const v8::Local<v8::Value> &groupNames) const;
+
+ QDeclarativeGuard<QSGVisualDataModel> model;
+ const int groupCount;
+ const int memberPropertyOffset;
+ const int indexPropertyOffset;
+ QMetaObject *metaObject;
+ const QStringList groupNames;
+};
+
+class QSGVisualDataModelCacheItem
+{
+public:
+ QSGVisualDataModelCacheItem(QSGVisualDataModelCacheMetaType *metaType)
+ : metaType(metaType)
+ , object(0)
+ , attached(0)
+ , objectRef(0)
+ , groups(0)
+ {
+ metaType->addref();
+ }
+
+ ~QSGVisualDataModelCacheItem()
+ {
+ Q_ASSERT(objectRef == 0);
+ Q_ASSERT(!object);
+
+ metaType->release();
+ }
+
+ void referenceObject() { ++objectRef; }
+ bool releaseObject() { return --objectRef == 0; }
+
+ bool isReferenced() const { return objectRef; }
+
+ QSGVisualDataModelCacheMetaType * const metaType;
+ QDeclarativeGuard<QObject> object;
+ QSGVisualDataModelAttached *attached;
+ int objectRef;
+ int groups;
+ int index[Compositor::MaximumGroupCount];
+};
+
+class QSGVisualDataModelAttachedMetaObject : public QAbstractDynamicMetaObject
+{
+public:
+ QSGVisualDataModelAttachedMetaObject(
+ QSGVisualDataModelAttached *attached, QSGVisualDataModelCacheMetaType *metaType);
+ ~QSGVisualDataModelAttachedMetaObject();
+
+ int metaCall(QMetaObject::Call, int _id, void **);
+
+private:
+ QSGVisualDataModelAttached *attached;
+ QSGVisualDataModelCacheMetaType *metaType;
+};
//---------------------------------------------------------------------------
+QHash<QObject*, QSGVisualDataModelAttached*> QSGVisualDataModelAttached::attachedProperties;
+
/*!
\qmlclass VisualDataModel QSGVisualDataModel
\inqmlmodule QtQuick 2
@@ -261,6 +376,25 @@ QSGVisualDataModelPrivate::QSGVisualDataModelPrivate(QDeclarativeContext *ctxt)
\snippet doc/src/snippets/declarative/visualdatamodel.qml 0
*/
+QSGVisualDataModelPrivate::QSGVisualDataModelPrivate(QDeclarativeContext *ctxt)
+ : m_adaptorModel(0)
+ , m_delegate(0)
+ , m_cacheMetaType(0)
+ , m_context(ctxt)
+ , m_parts(0)
+ , m_compositorGroup(Compositor::Cache)
+ , m_complete(false)
+ , m_delegateValidated(false)
+ , m_completePending(false)
+ , m_reset(false)
+ , m_transaction(false)
+ , m_filterGroup(QStringLiteral("items"))
+ , m_cacheItems(0)
+ , m_items(0)
+ , m_groupCount(2)
+{
+}
+
void QSGVisualDataModelPrivate::connectModel(QSGVisualAdaptorModel *model)
{
Q_Q(QSGVisualDataModel);
@@ -277,7 +411,10 @@ void QSGVisualDataModelPrivate::init()
Q_Q(QSGVisualDataModel);
m_adaptorModel = new QSGVisualAdaptorModel;
QObject::connect(m_adaptorModel, SIGNAL(rootIndexChanged()), q, SIGNAL(rootIndexChanged()));
- connectModel(m_adaptorModel);
+
+ m_items = new QSGVisualDataGroup(QStringLiteral("items"), q, Compositor::Default, q);
+ m_items->setDefaultInclude(true);
+ QSGVisualDataGroupPrivate::get(m_items)->emitters.insert(this);
}
QSGVisualDataModel::QSGVisualDataModel()
@@ -292,12 +429,81 @@ QSGVisualDataModel::QSGVisualDataModel(QDeclarativeContext *ctxt, QObject *paren
{
Q_D(QSGVisualDataModel);
d->init();
+ componentComplete();
}
QSGVisualDataModel::~QSGVisualDataModel()
{
Q_D(QSGVisualDataModel);
+ foreach (QSGVisualDataModelCacheItem *cacheItem, d->m_cache) {
+ cacheItem->object = 0;
+ cacheItem->objectRef = 0;
+ delete cacheItem;
+ }
+
delete d->m_adaptorModel;
+ if (d->m_cacheMetaType)
+ d->m_cacheMetaType->release();
+}
+
+
+void QSGVisualDataModel::classBegin()
+{
+}
+
+void QSGVisualDataModel::componentComplete()
+{
+ Q_D(QSGVisualDataModel);
+ d->m_complete = true;
+
+ int defaultGroups = 0;
+ QStringList groupNames;
+ groupNames.append(QStringLiteral("items"));
+ if (QSGVisualDataGroupPrivate::get(d->m_items)->defaultInclude)
+ defaultGroups |= Compositor::DefaultFlag;
+ for (int i = 2; 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]) << QSGVisualDataGroup::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);
+
+ QSGVisualDataGroupPrivate *group = QSGVisualDataGroupPrivate::get(d->m_groups[i]);
+ group->setModel(this, Compositor::Group(i));
+ if (group->defaultInclude)
+ defaultGroups |= (1 << i);
+ }
+ }
+ if (!d->m_context)
+ d->m_context = qmlContext(this);
+
+ d->m_cacheMetaType = new QSGVisualDataModelCacheMetaType(this, groupNames);
+
+ d->m_compositor.setGroupCount(d->m_groupCount);
+ d->m_compositor.setDefaultGroups(defaultGroups);
+ d->updateFilterGroup();
+
+ while (!d->m_pendingParts.isEmpty())
+ static_cast<QSGVisualPartsModel *>(d->m_pendingParts.first())->updateFilterGroup();
+
+ d->connectModel(d->m_adaptorModel);
+ QVector<Compositor::Insert> inserts;
+ d->m_reset = true;
+ d->m_compositor.append(
+ d->m_adaptorModel,
+ 0,
+ qMax(0, d->m_adaptorModel->count()),
+ defaultGroups | Compositor::AppendFlag | Compositor::PrependFlag,
+ &inserts);
+ d->itemsInserted(inserts);
+ d->emitChanges();
}
/*!
@@ -344,13 +550,26 @@ QDeclarativeComponent *QSGVisualDataModel::delegate() const
void QSGVisualDataModel::setDelegate(QDeclarativeComponent *delegate)
{
Q_D(QSGVisualDataModel);
+ 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_adaptorModel->count() && d->m_delegate)
- _q_itemsInserted(0, d->m_adaptorModel->count());
- if (wasValid && !d->m_delegate && d->m_adaptorModel->count())
- _q_itemsRemoved(0, d->m_adaptorModel->count());
+ if (wasValid && d->m_complete) {
+ for (int i = 1; i < d->m_groupCount; ++i) {
+ QSGVisualDataGroupPrivate::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) {
+ QSGVisualDataGroupPrivate::get(d->m_groups[i])->changeSet.insert(
+ 0, d->m_compositor.count(Compositor::Group(i)));
+ }
+ }
+ d->emitChanges();
}
/*!
@@ -430,31 +649,44 @@ QVariant QSGVisualDataModel::parentModelIndex() const
return d->m_adaptorModel->parentModelIndex();
}
+/*!
+ \qmlproperty int QtQuick2::VisualDataModel::count
+*/
+
int QSGVisualDataModel::count() const
{
Q_D(const QSGVisualDataModel);
if (!d->m_delegate)
return 0;
- return d->m_adaptorModel->count();
+ return d->m_compositor.count(d->m_compositorGroup);
}
QSGVisualDataModel::ReleaseFlags QSGVisualDataModelPrivate::release(QObject *object)
{
QSGVisualDataModel::ReleaseFlags stat = 0;
-
- if (m_cache.releaseItem(object)) {
- // Remove any bindings to avoid warnings due to parent change.
- QObjectPrivate *p = QObjectPrivate::get(object);
- Q_ASSERT(p->declarativeData);
- QDeclarativeData *d = static_cast<QDeclarativeData*>(p->declarativeData);
- if (d->ownContext && d->context)
- d->context->clearContext();
- stat |= QSGVisualDataModel::Destroyed;
- object->deleteLater();
- } else {
- stat |= QSGVisualDataModel::Referenced;
+ if (!object)
+ return stat;
+
+ int cacheIndex = cacheIndexOf(object);
+ if (cacheIndex != -1) {
+ QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex);
+ if (cacheItem->releaseObject()) {
+ QObjectPrivate *p = QObjectPrivate::get(object);
+ Q_ASSERT(p->declarativeData);
+ QDeclarativeData *data = static_cast<QDeclarativeData*>(p->declarativeData);
+ if (data->ownContext && data->context)
+ data->context->clearContext();
+ object->deleteLater();
+ cacheItem->object = 0;
+ stat |= QSGVisualModel::Destroyed;
+ m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag);
+ m_cache.removeAt(cacheIndex);
+ delete cacheItem;
+ Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache));
+ } else {
+ stat |= QSGVisualDataModel::Referenced;
+ }
}
-
return stat;
}
@@ -471,6 +703,149 @@ QSGVisualDataModel::ReleaseFlags QSGVisualDataModel::release(QSGItem *item)
return stat;
}
+void QSGVisualDataModelPrivate::group_append(
+ QDeclarativeListProperty<QSGVisualDataGroup> *property, QSGVisualDataGroup *group)
+{
+ QSGVisualDataModelPrivate *d = static_cast<QSGVisualDataModelPrivate *>(property->data);
+ if (d->m_complete)
+ return;
+ if (d->m_groupCount == 10) {
+ qmlInfo(d->q_func()) << QSGVisualDataModel::tr("The maximum number of supported VisualDataGroups is 8");
+ return;
+ }
+ d->m_groups[d->m_groupCount] = group;
+ d->m_groupCount += 1;
+}
+
+int QSGVisualDataModelPrivate::group_count(
+ QDeclarativeListProperty<QSGVisualDataGroup> *property)
+{
+ QSGVisualDataModelPrivate *d = static_cast<QSGVisualDataModelPrivate *>(property->data);
+ return d->m_groupCount - 1;
+}
+
+QSGVisualDataGroup *QSGVisualDataModelPrivate::group_at(
+ QDeclarativeListProperty<QSGVisualDataGroup> *property, int index)
+{
+ QSGVisualDataModelPrivate *d = static_cast<QSGVisualDataModelPrivate *>(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 doc/src/snippets/declarative/visualdatagroup.qml 0
+*/
+
+QDeclarativeListProperty<QSGVisualDataGroup> QSGVisualDataModel::groups()
+{
+ Q_D(QSGVisualDataModel);
+ return QDeclarativeListProperty<QSGVisualDataGroup>(
+ this,
+ d,
+ QSGVisualDataModelPrivate::group_append,
+ QSGVisualDataModelPrivate::group_count,
+ QSGVisualDataModelPrivate::group_at);
+}
+
+/*!
+ \qmlproperty VisualDataGroup QtQuick2::VisualDataModel::items
+
+ This property holds visual data model's default group to which all new items are added.
+*/
+
+QSGVisualDataGroup *QSGVisualDataModel::items()
+{
+ Q_D(QSGVisualDataModel);
+ return d->m_items;
+}
+
+/*!
+ \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 QSGVisualDataModel::filterGroup() const
+{
+ Q_D(const QSGVisualDataModel);
+ return d->m_filterGroup;
+}
+
+void QSGVisualDataModel::setFilterGroup(const QString &group)
+{
+ Q_D(QSGVisualDataModel);
+
+ 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 QSGVisualDataModel::resetFilterGroup()
+{
+ setFilterGroup(QStringLiteral("items"));
+}
+
+void QSGVisualDataModelPrivate::updateFilterGroup()
+{
+ Q_Q(QSGVisualDataModel);
+ if (!m_cacheMetaType)
+ return;
+
+ QDeclarativeListCompositor::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;
+ }
+ }
+
+ QSGVisualDataGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this);
+ if (m_compositorGroup != previousGroup) {
+ QVector<QDeclarativeChangeSet::Remove> removes;
+ QVector<QDeclarativeChangeSet::Insert> inserts;
+ m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts);
+
+ QDeclarativeChangeSet changeSet;
+ changeSet.apply(removes, inserts);
+ emit q->modelUpdated(changeSet, false);
+
+ if (changeSet.difference() != 0)
+ emit q->countChanged();
+
+ if (m_parts) {
+ foreach (QSGVisualPartsModel *model, m_parts->models)
+ model->updateFilterGroup(m_compositorGroup, changeSet);
+ }
+ }
+}
+
/*!
\qmlproperty object QtQuick2::VisualDataModel::parts
@@ -507,18 +882,40 @@ QObject *QSGVisualDataModel::parts()
return d->m_parts;
}
-QObject *QSGVisualDataModelPrivate::object(int index, bool complete)
+void QSGVisualDataModelPrivate::emitCreatedPackage(Compositor::iterator at, QDeclarativePackage *package)
+{
+ for (int i = 1; i < m_groupCount; ++i)
+ QSGVisualDataGroupPrivate::get(m_groups[i])->createdPackage(at.index[i], package);
+}
+
+void QSGVisualDataModelPrivate::emitDestroyingPackage(QDeclarativePackage *package)
+{
+ for (int i = 1; i < m_groupCount; ++i)
+ QSGVisualDataGroupPrivate::get(m_groups[i])->destroyingPackage(package);
+}
+
+QObject *QSGVisualDataModelPrivate::object(Compositor::Group group, int index, bool complete)
{
Q_Q(QSGVisualDataModel);
- if (m_adaptorModel->count() <= 0 || !m_delegate)
+ if (!m_delegate || index < 0 || index >= m_compositor.count(group)) {
+ qWarning() << "VisualDataModel::item: index out range" << index << m_compositor.count(group);
return 0;
- QObject *nobj = m_cache.getItem(index);
- bool needComplete = false;
- if (!nobj) {
- QObject *data = m_adaptorModel->data(index);
+ }
+
+ Compositor::iterator it = m_compositor.find(group, index);
+ QSGVisualDataModelCacheItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0;
+
+ if (!cacheItem) {
+ cacheItem = new QSGVisualDataModelCacheItem(m_cacheMetaType);
+ for (int i = 0; i < m_groupCount; ++i)
+ cacheItem->index[i] = it.index[i];
+ cacheItem->groups = it->flags & Compositor::GroupMask;
+ }
+
+ if (!cacheItem->object) {
+ QObject *data = m_adaptorModel->data(it.modelIndex());
- QDeclarativeContext *rootContext = new QDeclarativeContext(
- m_context ? m_context.data() : qmlContext(q));
+ QDeclarativeContext *rootContext = new QDeclarativeContext(m_context);
QDeclarativeContext *ctxt = rootContext;
if (m_adaptorModel->flags() & QSGVisualAdaptorModel::ProxiedObject) {
if (QSGVisualAdaptorModelProxyInterface *proxy = qobject_cast<QSGVisualAdaptorModelProxyInterface *>(data)) {
@@ -532,42 +929,54 @@ QObject *QSGVisualDataModelPrivate::object(int index, bool complete)
ctxt->setContextObject(data);
m_completePending = false;
- nobj = m_delegate->beginCreate(ctxt);
- if (complete) {
- m_delegate->completeCreate();
- } else {
- m_completePending = true;
- needComplete = true;
- }
- if (nobj) {
- QDeclarative_setParent_noEvent(rootContext, nobj);
- m_cache.insertItem(index, nobj);
- if (QDeclarativePackage *package = qobject_cast<QDeclarativePackage *>(nobj))
- emitCreatedPackage(index, package);
+ cacheItem->object = m_delegate->beginCreate(ctxt);
+
+ if (cacheItem->object) {
+ QDeclarative_setParent_noEvent(rootContext, cacheItem->object);
+ if (!it->inCache()) {
+ m_cache.insert(it.cacheIndex, cacheItem);
+ m_compositor.setFlags(it, 1, Compositor::CacheFlag);
+ Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache));
+ }
+
+ cacheItem->attached = QSGVisualDataModelAttached::properties(cacheItem->object);
+ cacheItem->attached->m_cacheItem = cacheItem;
+ new QSGVisualDataModelAttachedMetaObject(cacheItem->attached, m_cacheMetaType);
+ cacheItem->attached->emitChanges();
+
+ if (QDeclarativePackage *package = qobject_cast<QDeclarativePackage *>(cacheItem->object))
+ emitCreatedPackage(it, package);
+
+ m_completePending = !complete;
+ if (complete)
+ m_delegate->completeCreate();
} else {
delete rootContext;
+ if (!it->inCache())
+ delete cacheItem;
qmlInfo(q, m_delegate->errors()) << "Error creating delegate";
+ return 0;
}
}
- if (index == m_adaptorModel->count() -1 && m_adaptorModel->canFetchMore())
+ if (index == m_compositor.count(group) - 1 && m_adaptorModel->canFetchMore())
QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest));
-
- return nobj;
+ cacheItem->referenceObject();
+ return cacheItem->object;
}
QSGItem *QSGVisualDataModel::item(int index, bool complete)
{
Q_D(QSGVisualDataModel);
- QObject *object = d->object(index, complete);
+ QObject *object = d->object(d->m_compositorGroup, index, complete);
if (QSGItem *item = qobject_cast<QSGItem *>(object))
return item;
-
- if (completePending())
+ if (d->m_completePending)
completeItem();
d->release(object);
if (!d->m_delegateValidated) {
- qmlInfo(d->m_delegate) << tr("Delegate component must be Item type.");
+ if (object)
+ qmlInfo(d->m_delegate) << QSGVisualDataModel::tr("Delegate component must be Item type.");
d->m_delegateValidated = true;
}
return 0;
@@ -586,21 +995,37 @@ void QSGVisualDataModel::completeItem()
d->m_completePending = false;
}
-QString QSGVisualDataModelPrivate::stringValue(int index, const QString &name)
+QString QSGVisualDataModelPrivate::stringValue(Compositor::Group group, int index, const QString &name)
{
- return m_adaptorModel->stringValue(index, name);
+ Compositor::iterator it = m_compositor.find(group, index);
+ if (QSGVisualAdaptorModel *model = it.list<QSGVisualAdaptorModel>()) {
+ return model->stringValue(it.modelIndex(), name);
+ }
+ return QString();
}
QString QSGVisualDataModel::stringValue(int index, const QString &name)
{
Q_D(QSGVisualDataModel);
- return d->stringValue(index, name);
+ return d->stringValue(d->m_compositorGroup, index, name);
+}
+
+int QSGVisualDataModelPrivate::cacheIndexOf(QObject *object) const
+{
+ for (int cacheIndex = 0; cacheIndex < m_cache.count(); ++cacheIndex) {
+ if (m_cache.at(cacheIndex)->object == object)
+ return cacheIndex;
+ }
+ return -1;
}
int QSGVisualDataModel::indexOf(QSGItem *item, QObject *) const
{
Q_D(const QSGVisualDataModel);
- return d->m_adaptorModel->indexOf(item);
+ const int cacheIndex = d->cacheIndexOf(item);
+ return cacheIndex != -1
+ ? d->m_cache.at(cacheIndex)->index[d->m_compositorGroup]
+ : -1;
}
void QSGVisualDataModel::setWatchedRoles(QList<QByteArray> roles)
@@ -610,6 +1035,36 @@ void QSGVisualDataModel::setWatchedRoles(QList<QByteArray> roles)
d->watchedRoles = roles;
}
+void QSGVisualDataModelPrivate::addGroups(Compositor::Group group, int index, int count, int groupFlags)
+{
+ QVector<Compositor::Insert> inserts;
+ m_compositor.setFlags(group, index, count, groupFlags, &inserts);
+ itemsInserted(inserts);
+ emitChanges();
+}
+
+void QSGVisualDataModelPrivate::removeGroups(Compositor::Group group, int index, int count, int groupFlags)
+{
+ QVector<Compositor::Remove> removes;
+ m_compositor.clearFlags(group, index, count, groupFlags, &removes);
+ itemsRemoved(removes);
+ emitChanges();
+}
+
+void QSGVisualDataModelPrivate::setGroups(Compositor::Group group, int index, int count, int groupFlags)
+{
+ QVector<Compositor::Insert> inserts;
+ m_compositor.setFlags(group, index, count, groupFlags, &inserts);
+ itemsInserted(inserts);
+
+ const int removeFlags = ~groupFlags & Compositor::GroupMask;
+ QVector<Compositor::Remove> removes;
+ m_compositor.clearFlags(group, index, count, removeFlags, &removes);
+ itemsRemoved(removes);
+
+ emitChanges();
+}
+
bool QSGVisualDataModel::event(QEvent *e)
{
Q_D(QSGVisualDataModel);
@@ -618,115 +1073,293 @@ bool QSGVisualDataModel::event(QEvent *e)
return QSGVisualModel::event(e);
}
+void QSGVisualDataModelPrivate::itemsChanged(const QVector<Compositor::Change> &changes)
+{
+ if (!m_delegate)
+ return;
+
+ QVarLengthArray<QVector<QDeclarativeChangeSet::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(
+ QDeclarativeChangeSet::Change(change.index[i], change.count));
+ }
+ }
+ }
+
+ for (int i = 1; i < m_groupCount; ++i)
+ QSGVisualDataGroupPrivate::get(m_groups[i])->changeSet.apply(translatedChanges.at(i));
+}
+
void QSGVisualDataModel::_q_itemsChanged(int index, int count)
{
Q_D(QSGVisualDataModel);
- if (!d->m_delegate)
+ if (count <= 0)
return;
- d->m_absoluteChangeSet.change(index, count);
+ QVector<Compositor::Change> changes;
+ d->m_compositor.listItemsChanged(d->m_adaptorModel, index, count, &changes);
+ d->itemsChanged(changes);
d->emitChanges();
}
+void QSGVisualDataModelPrivate::itemsInserted(
+ const QVector<Compositor::Insert> &inserts,
+ QVarLengthArray<QVector<QDeclarativeChangeSet::Insert>, Compositor::MaximumGroupCount> *translatedInserts,
+ QHash<int, QList<QSGVisualDataModelCacheItem *> > *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) {
+ QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex);
+ if (!cacheItem->groups)
+ continue;
+ for (int i = 1; i < m_groupCount; ++i)
+ cacheItem->index[i] += inserted[i];
+ }
+ for (int i = 1; i < m_groupCount; ++i) {
+ if (insert.inGroup(i)) {
+ (*translatedInserts)[i].append(
+ QDeclarativeChangeSet::Insert(insert.index[i], insert.count, insert.moveId));
+ inserted[i] += insert.count;
+ }
+ }
+
+ if (!insert.inCache())
+ continue;
+
+ if (movedItems && insert.isMove()) {
+ QList<QSGVisualDataModelCacheItem *> 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) {
+ QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex);
+ cacheItem->groups |= insert.flags & Compositor::GroupMask;
+ for (int i = 1; i < m_groupCount; ++i) {
+ cacheItem->index[i] = cacheItem->groups & (1 << i)
+ ? insert.index[i] + offset
+ : insert.index[i];
+ }
+ }
+ } else {
+ cacheIndex = insert.cacheIndex + insert.count;
+ }
+ }
+ for (; cacheIndex < m_cache.count(); ++cacheIndex) {
+ QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex);
+ if (!cacheItem->groups)
+ continue;
+ for (int i = 1; i < m_groupCount; ++i)
+ cacheItem->index[i] += inserted[i];
+ }
+}
+
+void QSGVisualDataModelPrivate::itemsInserted(const QVector<Compositor::Insert> &inserts)
+{
+ QVarLengthArray<QVector<QDeclarativeChangeSet::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)
+ QSGVisualDataGroupPrivate::get(m_groups[i])->changeSet.apply(translatedInserts.at(i));
+}
+
void QSGVisualDataModel::_q_itemsInserted(int index, int count)
{
+
Q_D(QSGVisualDataModel);
- if (!d->m_delegate)
+ if (count <= 0)
return;
+ QVector<Compositor::Insert> inserts;
+ d->m_compositor.listItemsInserted(d->m_adaptorModel, index, count, &inserts);
+ d->itemsInserted(inserts);
+ d->emitChanges();
+}
- // XXX - highly inefficient
- QHash<int,QSGVisualDataModelPrivate::ObjectRef> items;
- for (QHash<int,QSGVisualDataModelPrivate::ObjectRef>::Iterator iter = d->m_cache.begin();
- iter != d->m_cache.end(); ) {
+void QSGVisualDataModelPrivate::itemsRemoved(
+ const QVector<Compositor::Remove> &removes,
+ QVarLengthArray<QVector<QDeclarativeChangeSet::Remove>, Compositor::MaximumGroupCount> *translatedRemoves,
+ QHash<int, QList<QSGVisualDataModelCacheItem *> > *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) {
+ QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex);
+ if (!cacheItem->groups)
+ continue;
+ for (int i = 1; i < m_groupCount; ++i)
+ cacheItem->index[i] -= removed[i];
+ }
+ for (int i = 1; i < m_groupCount; ++i) {
+ if (remove.inGroup(i)) {
+ (*translatedRemoves)[i].append(
+ QDeclarativeChangeSet::Remove(remove.index[i], remove.count, remove.moveId));
+ removed[i] += remove.count;
+ }
+ }
- if (iter.key() >= index) {
- QSGVisualDataModelPrivate::ObjectRef objRef = *iter;
- int index = iter.key() + count;
- iter = d->m_cache.erase(iter);
+ if (!remove.inCache())
+ continue;
- items.insert(index, objRef);
+ if (movedItems && remove.isMove()) {
+ movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex, remove.count));
+ QList<QSGVisualDataModelCacheItem *>::iterator begin = m_cache.begin() + remove.cacheIndex;
+ QList<QSGVisualDataModelCacheItem *>::iterator end = begin + remove.count;
+ m_cache.erase(begin, end);
} else {
- ++iter;
+ for (; cacheIndex < remove.cacheIndex + remove.count - removedCache; ++cacheIndex) {
+ QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex);
+ if (remove.groups() == cacheItem->groups && !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;
+ for (int i = 1; i < m_groupCount; ++i)
+ cacheItem->index[i] = -1;
+ } else {
+ for (int i = 1; i < m_groupCount; ++i) {
+ if (remove.inGroup(i))
+ cacheItem->index[i] = remove.index[i];
+ }
+ cacheItem->groups &= ~remove.flags & Compositor::GroupMask;
+ }
+ }
}
}
- d->m_cache.unite(items);
- d->m_absoluteChangeSet.insert(index, count);
- d->emitChanges();
- emit countChanged();
+ for (; cacheIndex < m_cache.count(); ++cacheIndex) {
+ QSGVisualDataModelCacheItem *cacheItem = m_cache.at(cacheIndex);
+ if (!cacheItem->groups)
+ continue;
+ for (int i = 1; i < m_groupCount; ++i)
+ cacheItem->index[i] -= removed[i];
+ }
+}
+
+void QSGVisualDataModelPrivate::itemsRemoved(const QVector<Compositor::Remove> &removes)
+{
+ QVarLengthArray<QVector<QDeclarativeChangeSet::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)
+ QSGVisualDataGroupPrivate::get(m_groups[i])->changeSet.apply(translatedRemoves.at(i));
}
void QSGVisualDataModel::_q_itemsRemoved(int index, int count)
{
Q_D(QSGVisualDataModel);
- if (!d->m_delegate)
+ if (count <= 0)
return;
- // XXX - highly inefficient
- QHash<int, QSGVisualDataModelPrivate::ObjectRef> items;
- for (QHash<int, QSGVisualDataModelPrivate::ObjectRef>::Iterator iter = d->m_cache.begin();
- iter != d->m_cache.end(); ) {
- if (iter.key() >= index && iter.key() < index + count) {
- QSGVisualDataModelPrivate::ObjectRef objRef = *iter;
- iter = d->m_cache.erase(iter);
- items.insertMulti(-1, objRef); //XXX perhaps better to maintain separately
- } else if (iter.key() >= index + count) {
- QSGVisualDataModelPrivate::ObjectRef objRef = *iter;
- int index = iter.key() - count;
- iter = d->m_cache.erase(iter);
- items.insert(index, objRef);
- } else {
- ++iter;
- }
- }
- d->m_cache.unite(items);
- d->m_absoluteChangeSet.remove(index, count);
+ QVector<Compositor::Remove> removes;
+ d->m_compositor.listItemsRemoved(d->m_adaptorModel, index, count, &removes);
+ d->itemsRemoved(removes);
d->emitChanges();
- emit countChanged();
+}
+
+void QSGVisualDataModelPrivate::itemsMoved(
+ const QVector<Compositor::Remove> &removes, const QVector<Compositor::Insert> &inserts)
+{
+ QHash<int, QList<QSGVisualDataModelCacheItem *> > movedItems;
+
+ QVarLengthArray<QVector<QDeclarativeChangeSet::Remove>, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount);
+ itemsRemoved(removes, &translatedRemoves, &movedItems);
+
+ QVarLengthArray<QVector<QDeclarativeChangeSet::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) {
+ QSGVisualDataGroupPrivate::get(m_groups[i])->changeSet.apply(
+ translatedRemoves.at(i),
+ translatedInserts.at(i));
+ }
}
void QSGVisualDataModel::_q_itemsMoved(int from, int to, int count)
{
Q_D(QSGVisualDataModel);
- if (!d->m_delegate)
+ if (count <= 0)
return;
- // XXX - highly inefficient
- QHash<int,QSGVisualDataModelPrivate::ObjectRef> items;
- for (QHash<int,QSGVisualDataModelPrivate::ObjectRef>::Iterator iter = d->m_cache.begin();
- iter != d->m_cache.end(); ) {
- if (iter.key() >= from && iter.key() < from + count) {
- QSGVisualDataModelPrivate::ObjectRef objRef = *iter;
- int index = iter.key() - from + to;
- items.insert(index, objRef);
- iter = d->m_cache.erase(iter);
- } else {
- ++iter;
- }
- }
- for (QHash<int,QSGVisualDataModelPrivate::ObjectRef>::Iterator iter = d->m_cache.begin();
- iter != d->m_cache.end(); ) {
- int diff = from > to ? count : -count;
- if (iter.key() >= qMin(from,to) && iter.key() < qMax(from+count,to+count)) {
- QSGVisualDataModelPrivate::ObjectRef objRef = *iter;
- int index = iter.key() + diff;
- iter = d->m_cache.erase(iter);
- items.insert(index, objRef);
- } else {
- ++iter;
- }
- }
- d->m_cache.unite(items);
- d->m_absoluteChangeSet.move(from, to, count);
+ QVector<Compositor::Remove> removes;
+ QVector<Compositor::Insert> inserts;
+ d->m_compositor.listItemsMoved(d->m_adaptorModel, from, to, count, &removes, &inserts);
+ d->itemsMoved(removes, inserts);
d->emitChanges();
}
-void QSGVisualDataModelPrivate::emitChanges()
+template <typename T> v8::Local<v8::Array>
+QSGVisualDataModelPrivate::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 QSGVisualDataModelPrivate::emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset)
{
Q_Q(QSGVisualDataModel);
- if (!m_absoluteChangeSet.isEmpty()) {
- emit q->modelUpdated(m_absoluteChangeSet, m_reset);
- m_absoluteChangeSet.clear();
- m_reset = false;
+ emit q->modelUpdated(changeSet, reset);
+ if (changeSet.difference() != 0)
+ emit q->countChanged();
+}
+
+void QSGVisualDataModelPrivate::emitChanges()
+{
+ if (m_transaction || !m_complete)
+ return;
+
+ m_transaction = true;
+ QV8Engine *engine = QDeclarativeEnginePrivate::getV8Engine(m_context->engine());
+ for (int i = 1; i < m_groupCount; ++i)
+ QSGVisualDataGroupPrivate::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)
+ QSGVisualDataGroupPrivate::get(m_groups[i])->emitModelUpdated(reset);
+
+ foreach (QSGVisualDataModelCacheItem *cacheItem, m_cache) {
+ if (cacheItem->object)
+ cacheItem->attached->emitChanges();
}
}
@@ -735,35 +1368,731 @@ void QSGVisualDataModel::_q_modelReset(int oldCount, int newCount)
Q_D(QSGVisualDataModel);
if (!d->m_delegate)
return;
- d->m_absoluteChangeSet.remove(0, oldCount);
- d->m_absoluteChangeSet.insert(0, newCount);
+
+ QVector<Compositor::Remove> removes;
+ QVector<Compositor::Insert> inserts;
+ if (oldCount)
+ d->m_compositor.listItemsRemoved(d->m_adaptorModel, 0, oldCount, &removes);
+ if (newCount)
+ d->m_compositor.listItemsInserted(d->m_adaptorModel, 0, newCount, &inserts);
+ d->itemsMoved(removes, inserts);
d->m_reset = true;
d->emitChanges();
- emit countChanged();
+}
+
+QSGVisualDataModelAttached *QSGVisualDataModel::qmlAttachedProperties(QObject *obj)
+{
+ return QSGVisualDataModelAttached::properties(obj);
}
//============================================================================
+QSGVisualDataModelCacheMetaType::QSGVisualDataModelCacheMetaType(
+ QSGVisualDataModel *model, const QStringList &groupNames)
+ : model(model)
+ , groupCount(groupNames.count() + 1)
+ , memberPropertyOffset(QSGVisualDataModelAttached::staticMetaObject.propertyCount())
+ , indexPropertyOffset(QSGVisualDataModelAttached::staticMetaObject.propertyCount() + groupNames.count())
+ , metaObject(0)
+ , groupNames(groupNames)
+{
+ QMetaObjectBuilder builder;
+ builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
+ builder.setClassName(QSGVisualDataModelAttached::staticMetaObject.className());
+ builder.setSuperClass(&QSGVisualDataModelAttached::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 = builder.toMetaObject();
+}
+
+QSGVisualDataModelCacheMetaType::~QSGVisualDataModelCacheMetaType()
+{
+ qFree(metaObject);
+}
+
+int QSGVisualDataModelCacheMetaType::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 QSGVisualDataModelCacheMetaType::parseGroups(QV8Engine *engine, const v8::Local<v8::Value> &groups) const
+{
+ int groupFlags = 0;
+ if (groups->IsString()) {
+ const QString groupName = engine->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 = engine->toString(array->Get(i));
+ int index = groupNames.indexOf(groupName);
+ if (index != -1)
+ groupFlags |= 2 << index;
+ }
+ }
+ return groupFlags;
+}
+
+//---------------------------------------------------------------------------
+
+QSGVisualDataModelAttachedMetaObject::QSGVisualDataModelAttachedMetaObject(
+ QSGVisualDataModelAttached *attached, QSGVisualDataModelCacheMetaType *metaType)
+ : attached(attached)
+ , metaType(metaType)
+{
+ metaType->addref();
+ *static_cast<QMetaObject *>(this) = *metaType->metaObject;
+ QObjectPrivate::get(attached)->metaObject = this;
+}
+
+QSGVisualDataModelAttachedMetaObject::~QSGVisualDataModelAttachedMetaObject()
+{
+ metaType->release();
+}
+
+int QSGVisualDataModelAttachedMetaObject::metaCall(QMetaObject::Call call, int _id, void **arguments)
+{
+ if (call == QMetaObject::ReadProperty) {
+ if (_id >= metaType->indexPropertyOffset) {
+ Compositor::Group group = Compositor::Group(_id - metaType->indexPropertyOffset + 1);
+ *static_cast<int *>(arguments[0]) = attached->m_cacheItem->index[group];
+ return -1;
+ } else if (_id >= metaType->memberPropertyOffset) {
+ Compositor::Group group = Compositor::Group(_id - metaType->memberPropertyOffset + 1);
+ *static_cast<bool *>(arguments[0]) = attached->m_cacheItem->groups & (1 << group);
+ return -1;
+ }
+ } else if (call == QMetaObject::WriteProperty) {
+ if (_id >= metaType->memberPropertyOffset) {
+ if (!metaType->model)
+ return -1;
+ Compositor::Group group = Compositor::Group(_id - metaType->memberPropertyOffset + 1);
+ const bool member = attached->m_cacheItem->groups & (1 << group);
+ if (member != *static_cast<bool *>(arguments[0])) {
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(metaType->model);
+ const int cacheIndex = model->m_cache.indexOf(attached->m_cacheItem);
+ if (member)
+ model->removeGroups(Compositor::Cache, cacheIndex, 1, (1 << group));
+ else
+ model->addGroups(Compositor::Cache, cacheIndex, 1, (1 << group));
+ }
+ return -1;
+ }
+ }
+ return attached->qt_metacall(call, _id, arguments);
+}
+
+/*!
+ \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.
+*/
+
+QSGVisualDataModel *QSGVisualDataModelAttached::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 QSGVisualDataModelAttached::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 QSGVisualDataModelAttached::setGroups(const QStringList &groups)
+{
+ if (!m_cacheItem)
+ return;
+
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_cacheItem->metaType->model);
+
+ const int cacheIndex = model->m_cache.indexOf(m_cacheItem);
+ const int groupFlags = model->m_cacheMetaType->parseGroups(groups);
+ model->setGroups(Compositor::Cache, cacheIndex, 1, groupFlags);
+}
+
+/*!
+ \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.
+*/
+
+void QSGVisualDataModelAttached::emitChanges()
+{
+ if (m_modelChanged) {
+ m_modelChanged = false;
+ emit modelChanged();
+ }
+
+ 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_cacheItem->index[i]) {
+ m_previousIndex[i] = m_cacheItem->index[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 QSGVisualDataGroupPrivate::setModel(QSGVisualDataModel *m, Compositor::Group g)
+{
+ Q_ASSERT(!model);
+ model = m;
+ group = g;
+}
+
+void QSGVisualDataGroupPrivate::emitChanges(QV8Engine *engine)
+{
+ Q_Q(QSGVisualDataGroup);
+ static int idx = signalIndex("changed(QDeclarativeV8Handle,QDeclarativeV8Handle)");
+ if (isSignalConnected(idx)) {
+ v8::HandleScope handleScope;
+ v8::Context::Scope contextScope(engine->context());
+ v8::Local<v8::Array> removed = QSGVisualDataModelPrivate::buildChangeList(changeSet.removes());
+ v8::Local<v8::Array> inserted = QSGVisualDataModelPrivate::buildChangeList(changeSet.inserts());
+ emit q->changed(
+ QDeclarativeV8Handle::fromHandle(removed), QDeclarativeV8Handle::fromHandle(inserted));
+ }
+ if (changeSet.difference() != 0)
+ emit q->countChanged();
+}
+
+void QSGVisualDataGroupPrivate::emitModelUpdated(bool reset)
+{
+ for (QSGVisualDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it)
+ it->emitModelUpdated(changeSet, reset);
+ changeSet.clear();
+}
+
+void QSGVisualDataGroupPrivate::createdPackage(int index, QDeclarativePackage *package)
+{
+ for (QSGVisualDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it)
+ it->createdPackage(index, package);
+}
+
+void QSGVisualDataGroupPrivate::destroyingPackage(QDeclarativePackage *package)
+{
+ for (QSGVisualDataGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it)
+ it->destroyingPackage(package);
+}
+
+/*!
+ \qmlclass VisualDataGroup QSGVisualDataGroup
+ \inqmlmodule QtQuick 2
+ \ingroup qml-working-with-data
+ \brief The VisualDataGroup encapsulates a filtered set of visual data items.
+
+*/
+
+QSGVisualDataGroup::QSGVisualDataGroup(QObject *parent)
+ : QObject(*new QSGVisualDataGroupPrivate, parent)
+{
+}
+
+QSGVisualDataGroup::QSGVisualDataGroup(
+ const QString &name, QSGVisualDataModel *model, int index, QObject *parent)
+ : QObject(*new QSGVisualDataGroupPrivate, parent)
+{
+ Q_D(QSGVisualDataGroup);
+ d->name = name;
+ d->setModel(model, Compositor::Group(index));
+}
+
+QSGVisualDataGroup::~QSGVisualDataGroup()
+{
+}
+
+/*!
+ \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 QSGVisualDataGroup::name() const
+{
+ Q_D(const QSGVisualDataGroup);
+ return d->name;
+}
+
+void QSGVisualDataGroup::setName(const QString &name)
+{
+ Q_D(QSGVisualDataGroup);
+ 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 QSGVisualDataGroup::count() const
+{
+ Q_D(const QSGVisualDataGroup);
+ if (!d->model)
+ return 0;
+ return QSGVisualDataModelPrivate::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 QSGVisualDataGroup::defaultInclude() const
+{
+ Q_D(const QSGVisualDataGroup);
+ return d->defaultInclude;
+}
+
+void QSGVisualDataGroup::setDefaultInclude(bool include)
+{
+ Q_D(QSGVisualDataGroup);
+ if (d->defaultInclude != include) {
+ d->defaultInclude = include;
+
+ if (d->model) {
+ if (include)
+ QSGVisualDataModelPrivate::get(d->model)->m_compositor.setDefaultGroup(d->group);
+ else
+ QSGVisualDataModelPrivate::get(d->model)->m_compositor.clearDefaultGroup(d->group);
+ }
+ emit defaultIncludeChanged();
+ }
+}
+
+/*!
+ \qmlmethod QtQuick2::VisualDataGroup::remove(int index, int count)
+
+ Removes \a count items starting at \a index from the group.
+*/
+
+void QSGVisualDataGroup::remove(QDeclarativeV8Function *args)
+{
+ Q_D(QSGVisualDataGroup);
+ if (!d->model)
+ return;
+ int index = -1;
+ int count = 1;
+
+ if (args->Length() == 0)
+ return;
+
+ int i = 0;
+ v8::Local<v8::Value> v = (*args)[i];
+ if (!v->IsInt32())
+ return;
+ index = v->Int32Value();
+
+ if (++i < args->Length()) {
+ v = (*args)[i];
+ if (v->IsInt32())
+ count = v->Int32Value();
+ }
+
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(d->model);
+ if (count < 0) {
+ qmlInfo(this) << tr("remove: invalid count");
+ } else if (index < 0 || index + count > model->m_compositor.count(d->group)) {
+ qmlInfo(this) << tr("remove: index out of range");
+ } else if (count > 0) {
+ model->removeGroups(d->group, index, count, 1 << d->group);
+ }
+}
+
+bool QSGVisualDataGroupPrivate::parseGroupArgs(
+ QDeclarativeV8Function *args, int *index, int *count, int *groups) const
+{
+ if (!model)
+ return false;
+
+ if (args->Length() < 2)
+ return false;
+
+ int i = 0;
+ v8::Local<v8::Value> v = (*args)[i];
+ if (!v->IsInt32())
+ return false;
+ *index = v->Int32Value();
+
+ v = (*args)[++i];
+ if (v->IsInt32()) {
+ *count = v->Int32Value();
+
+ if (++i == args->Length())
+ return false;
+ v = (*args)[i];
+ }
+
+ *groups = QSGVisualDataModelPrivate::get(model)->m_cacheMetaType->parseGroups(args->engine(), 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 QSGVisualDataGroup::addGroups(QDeclarativeV8Function *args)
+{
+ Q_D(QSGVisualDataGroup);
+ int index = -1;
+ int count = 1;
+ int groups = 0;
+
+ if (!d->parseGroupArgs(args, &index, &count, &groups))
+ return;
+
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(d->model);
+ if (count < 0) {
+ qmlInfo(this) << tr("addGroups: invalid count");
+ } else if (index < 0 || index + count > model->m_compositor.count(d->group)) {
+ qmlInfo(this) << tr("addGroups: index out of range");
+ } else if (count > 0 && groups) {
+ model->addGroups(d->group, index, count, groups);
+ }
+}
+
+/*!
+ \qmlmethod QtQuick2::VisualDataGroup::removeGroups(int index, int count, stringlist groups)
+
+ Removes \a count items starting at \a index from \a groups.
+*/
+
+void QSGVisualDataGroup::removeGroups(QDeclarativeV8Function *args)
+{
+ Q_D(QSGVisualDataGroup);
+ int index = -1;
+ int count = 1;
+ int groups = 0;
+
+ if (!d->parseGroupArgs(args, &index, &count, &groups))
+ return;
+
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(d->model);
+ if (count < 0) {
+ qmlInfo(this) << tr("removeGroups: invalid count");
+ } else if (index < 0 || index + count > model->m_compositor.count(d->group)) {
+ qmlInfo(this) << tr("removeGroups: index out of range");
+ } else if (count > 0 && groups) {
+ model->removeGroups(d->group, index, count, 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 QSGVisualDataGroup::setGroups(QDeclarativeV8Function *args)
+{
+ Q_D(QSGVisualDataGroup);
+ int index = -1;
+ int count = 1;
+ int groups = 0;
+
+ if (!d->parseGroupArgs(args, &index, &count, &groups))
+ return;
+
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(d->model);
+ if (count < 0) {
+ qmlInfo(this) << tr("setGroups: invalid count");
+ } else if (index < 0 || index + count > model->m_compositor.count(d->group)) {
+ qmlInfo(this) << tr("setGroups: index out of range");
+ } else if (count > 0) {
+ model->setGroups(d->group, index, count, 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(int from, int to, int count)
+
+ Moves \a count at \a from in a group \a to a new position.
+*/
+
+void QSGVisualDataGroup::move(QDeclarativeV8Function *args)
+{
+ Q_D(QSGVisualDataGroup);
+
+ if (args->Length() < 2)
+ return;
+
+ Compositor::Group fromGroup = d->group;
+ Compositor::Group toGroup = d->group;
+ int from = -1;
+ int to = -1;
+ int count = 1;
+
+ int i = 0;
+ v8::Local<v8::Value> v = (*args)[i];
+ if (QSGVisualDataGroup *group = qobject_cast<QSGVisualDataGroup *>(args->engine()->toQObject(v))) {
+ QSGVisualDataGroupPrivate *g_d = QSGVisualDataGroupPrivate::get(group);
+ if (g_d->model != d->model)
+ return;
+ fromGroup = g_d->group;
+ v = (*args)[++i];
+ }
+
+ if (!v->IsInt32())
+ return;
+ from = v->Int32Value();
+
+ if (++i == args->Length())
+ return;
+ v = (*args)[i];
+
+ if (QSGVisualDataGroup *group = qobject_cast<QSGVisualDataGroup *>(args->engine()->toQObject(v))) {
+ QSGVisualDataGroupPrivate *g_d = QSGVisualDataGroupPrivate::get(group);
+ if (g_d->model != d->model)
+ return;
+ toGroup = g_d->group;
+
+ if (++i == args->Length())
+ return;
+ v = (*args)[i];
+ }
+
+ if (!v->IsInt32())
+ return;
+ to = v->Int32Value();
+
+ if (++i < args->Length()) {
+ v = (*args)[i];
+ if (v->IsInt32())
+ count = v->Int32Value();
+ }
+
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::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)) {
+ 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, &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.
+*/
+
+//============================================================================
+
QSGVisualPartsModel::QSGVisualPartsModel(QSGVisualDataModel *model, const QString &part, QObject *parent)
: QSGVisualModel(*new QObjectPrivate, parent)
, m_model(model)
, m_part(part)
+ , m_compositorGroup(Compositor::Cache)
+ , m_inheritGroup(true)
{
- connect(m_model, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)),
- this, SIGNAL(modelUpdated(QDeclarativeChangeSet,bool)));
- connect(m_model, SIGNAL(createdPackage(int,QDeclarativePackage*)),
- this, SLOT(createdPackage(int,QDeclarativePackage*)));
- connect(m_model, SIGNAL(destroyingPackage(QDeclarativePackage*)),
- this, SLOT(destroyingPackage(QDeclarativePackage*)));
+ QSGVisualDataModelPrivate *d = QSGVisualDataModelPrivate::get(m_model);
+ if (d->m_cacheMetaType) {
+ QSGVisualDataGroupPrivate::get(d->m_groups[1])->emitters.insert(this);
+ m_compositorGroup = Compositor::Default;
+ } else {
+ d->m_pendingParts.insert(this);
+ }
}
QSGVisualPartsModel::~QSGVisualPartsModel()
{
}
+QString QSGVisualPartsModel::filterGroup() const
+{
+ if (m_inheritGroup)
+ return m_model->filterGroup();
+ return m_filterGroup;
+}
+
+void QSGVisualPartsModel::setFilterGroup(const QString &group)
+{
+ if (QSGVisualDataModelPrivate::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 QSGVisualPartsModel::resetFilterGroup()
+{
+ if (!m_inheritGroup) {
+ m_inheritGroup = true;
+ updateFilterGroup();
+ emit filterGroupChanged();
+ }
+}
+
+void QSGVisualPartsModel::updateFilterGroup()
+{
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model);
+ if (!model->m_cacheMetaType)
+ return;
+
+ if (m_inheritGroup)
+ return;
+
+ QDeclarativeListCompositor::Group previousGroup = model->m_compositorGroup;
+ m_compositorGroup = Compositor::Default;
+ QSGVisualDataGroupPrivate::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;
+ }
+ }
+
+ QSGVisualDataGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this);
+ if (m_compositorGroup != previousGroup) {
+ QVector<QDeclarativeChangeSet::Remove> removes;
+ QVector<QDeclarativeChangeSet::Insert> inserts;
+ model->m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts);
+
+ QDeclarativeChangeSet changeSet;
+ changeSet.apply(removes, inserts);
+ if (!changeSet.isEmpty())
+ emit modelUpdated(changeSet, false);
+
+ if (changeSet.difference() != 0)
+ emit countChanged();
+ }
+}
+
+void QSGVisualPartsModel::updateFilterGroup(
+ Compositor::Group group, const QDeclarativeChangeSet &changeSet)
+{
+ if (!m_inheritGroup)
+ return;
+
+ m_compositorGroup = group;
+ QSGVisualDataGroupPrivate::get(QSGVisualDataModelPrivate::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 QSGVisualPartsModel::count() const
{
- return m_model->count();
+ QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model);
+ return model->m_delegate
+ ? model->m_compositor.count(m_compositorGroup)
+ : 0;
}
bool QSGVisualPartsModel::isValid() const
@@ -775,7 +2104,7 @@ QSGItem *QSGVisualPartsModel::item(int index, bool complete)
{
QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model);
- QObject *object = model->object(index, complete);
+ QObject *object = model->object(m_compositorGroup, index, complete);
if (QDeclarativePackage *package = qobject_cast<QDeclarativePackage *>(object)) {
QObject *part = package->part(m_part);
@@ -791,7 +2120,8 @@ QSGItem *QSGVisualPartsModel::item(int index, bool complete)
m_model->completeItem();
model->release(object);
if (!model->m_delegateValidated) {
- qmlInfo(model->m_delegate) << tr("Delegate component must be Package type.");
+ if (object)
+ qmlInfo(model->m_delegate) << tr("Delegate component must be Package type.");
model->m_delegateValidated = true;
}
@@ -828,7 +2158,7 @@ void QSGVisualPartsModel::completeItem()
QString QSGVisualPartsModel::stringValue(int index, const QString &role)
{
- return QSGVisualDataModelPrivate::get(m_model)->stringValue(index, role);
+ return QSGVisualDataModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role);
}
void QSGVisualPartsModel::setWatchedRoles(QList<QByteArray> roles)
@@ -840,11 +2170,15 @@ void QSGVisualPartsModel::setWatchedRoles(QList<QByteArray> roles)
int QSGVisualPartsModel::indexOf(QSGItem *item, QObject *) const
{
- const QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model);
QHash<QObject *, QDeclarativePackage *>::const_iterator it = m_packaged.find(item);
- return it != m_packaged.end()
- ? model->m_adaptorModel->indexOf(*it)
- : -1;
+ if (it != m_packaged.end()) {
+ const QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model);
+ const int cacheIndex = model->cacheIndexOf(*it);
+ return cacheIndex != -1
+ ? model->m_cache.at(cacheIndex)->index[m_compositorGroup]
+ : -1;
+ }
+ return -1;
}
void QSGVisualPartsModel::createdPackage(int index, QDeclarativePackage *package)
@@ -861,6 +2195,14 @@ void QSGVisualPartsModel::destroyingPackage(QDeclarativePackage *package)
}
}
+void QSGVisualPartsModel::emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset)
+{
+ emit modelUpdated(changeSet, reset);
+ if (changeSet.difference() != 0)
+ emit countChanged();
+}
+
+
QT_END_NAMESPACE
#include <qsgvisualdatamodel.moc>
diff --git a/src/declarative/items/qsgvisualdatamodel_p.h b/src/declarative/items/qsgvisualdatamodel_p.h
index d3f1d18f65..94640a65a0 100644
--- a/src/declarative/items/qsgvisualdatamodel_p.h
+++ b/src/declarative/items/qsgvisualdatamodel_p.h
@@ -1,3 +1,4 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
@@ -42,8 +43,12 @@
#ifndef QSGVISUALDATAMODEL_P_H
#define QSGVISUALDATAMODEL_P_H
+#include <private/qdeclarativelistcompositor_p.h>
#include <private/qsgvisualitemmodel_p.h>
+
+
#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qstringlist.h>
QT_BEGIN_HEADER
@@ -53,24 +58,39 @@ QT_BEGIN_NAMESPACE
QT_MODULE(Declarative)
+
+class QDeclarativeChangeSet;
class QDeclarativeComponent;
class QDeclarativePackage;
+class QDeclarativeV8Function;
+class QDeclarativeV8Handle;
+class QSGVisualDataGroup;
+class QSGVisualDataModelAttached;
class QSGVisualDataModelPrivate;
-class Q_DECLARATIVE_EXPORT QSGVisualDataModel : public QSGVisualModel
+
+
+class Q_DECLARATIVE_EXPORT QSGVisualDataModel : public QSGVisualModel, public QDeclarativeParserStatus
{
Q_OBJECT
Q_DECLARE_PRIVATE(QSGVisualDataModel)
Q_PROPERTY(QVariant model READ model WRITE setModel)
Q_PROPERTY(QDeclarativeComponent *delegate READ delegate WRITE setDelegate)
+ Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup)
+ Q_PROPERTY(QSGVisualDataGroup *items READ items CONSTANT)
+ Q_PROPERTY(QDeclarativeListProperty<QSGVisualDataGroup> 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(QDeclarativeParserStatus)
public:
QSGVisualDataModel();
QSGVisualDataModel(QDeclarativeContext *, QObject *parent=0);
virtual ~QSGVisualDataModel();
+ void classBegin();
+ void componentComplete();
+
QVariant model() const;
void setModel(const QVariant &);
@@ -94,13 +114,21 @@ public:
int indexOf(QSGItem *item, QObject *objectContext) const;
+ QString filterGroup() const;
+ void setFilterGroup(const QString &group);
+ void resetFilterGroup();
+
+ QSGVisualDataGroup *items();
+ QDeclarativeListProperty<QSGVisualDataGroup> groups();
QObject *parts();
bool event(QEvent *);
+ static QSGVisualDataModelAttached *qmlAttachedProperties(QObject *obj);
+
Q_SIGNALS:
- void createdPackage(int index, QDeclarativePackage *package);
- void destroyingPackage(QDeclarativePackage *package);
+ void filterGroupChanged();
+ void defaultGroupsChanged();
void rootIndexChanged();
private Q_SLOTS:
@@ -113,9 +141,95 @@ private:
Q_DISABLE_COPY(QSGVisualDataModel)
};
+class QSGVisualDataGroupPrivate;
+class Q_AUTOTEST_EXPORT QSGVisualDataGroup : 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:
+ QSGVisualDataGroup(QObject *parent = 0);
+ QSGVisualDataGroup(const QString &name, QSGVisualDataModel *model, int compositorType, QObject *parent = 0);
+ ~QSGVisualDataGroup();
+
+ QString name() const;
+ void setName(const QString &name);
+
+ int count() const;
+
+ bool defaultInclude() const;
+ void setDefaultInclude(bool include);
+
+public Q_SLOTS:
+ void remove(QDeclarativeV8Function *);
+ void addGroups(QDeclarativeV8Function *);
+ void removeGroups(QDeclarativeV8Function *);
+ void setGroups(QDeclarativeV8Function *);
+ void move(QDeclarativeV8Function *);
+
+Q_SIGNALS:
+ void countChanged();
+ void nameChanged();
+ void defaultIncludeChanged();
+ void changed(const QDeclarativeV8Handle &removed, const QDeclarativeV8Handle &inserted);
+private:
+ Q_DECLARE_PRIVATE(QSGVisualDataGroup)
+};
+
+class QSGVisualDataModelCacheItem;
+class QSGVisualDataModelAttachedMetaObject;
+class QSGVisualDataModelAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QSGVisualDataModel *model READ model NOTIFY modelChanged)
+ Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged)
+public:
+ QSGVisualDataModelAttached(QObject *parent)
+ : QObject(parent)
+ , m_cacheItem(0)
+ , m_previousGroups(0)
+ , m_modelChanged(false)
+ {}
+ ~QSGVisualDataModelAttached() { attachedProperties.remove(parent()); }
+
+ QSGVisualDataModel *model() const;
+
+ QStringList groups() const;
+ void setGroups(const QStringList &groups);
+
+ void emitChanges();
+
+ static QSGVisualDataModelAttached *properties(QObject *obj)
+ {
+ QSGVisualDataModelAttached *rv = attachedProperties.value(obj);
+ if (!rv) {
+ rv = new QSGVisualDataModelAttached(obj);
+ attachedProperties.insert(obj, rv);
+ }
+ return rv;
+ }
+
+Q_SIGNALS:
+ void modelChanged();
+ void groupsChanged();
+
+public:
+ QSGVisualDataModelCacheItem *m_cacheItem;
+ int m_previousGroups;
+ int m_previousIndex[QDeclarativeListCompositor::MaximumGroupCount];
+ bool m_modelChanged;
+
+ static QHash<QObject*, QSGVisualDataModelAttached*> attachedProperties;
+
+ friend class QSGVisualDataModelAttachedMetaObject;
+};
+
QT_END_NAMESPACE
QML_DECLARE_TYPE(QSGVisualDataModel)
+QML_DECLARE_TYPEINFO(QSGVisualDataModel, QML_HAS_ATTACHED_PROPERTIES)
+QML_DECLARE_TYPE(QSGVisualDataGroup)
QT_END_HEADER
diff --git a/src/declarative/util/qdeclarativechangeset.cpp b/src/declarative/util/qdeclarativechangeset.cpp
index 33757062a1..81cbe3e10a 100644
--- a/src/declarative/util/qdeclarativechangeset.cpp
+++ b/src/declarative/util/qdeclarativechangeset.cpp
@@ -45,6 +45,7 @@ QT_BEGIN_NAMESPACE
QDeclarativeChangeSet::QDeclarativeChangeSet()
: m_moveCounter(0)
+ , m_difference(0)
{
}
@@ -53,6 +54,7 @@ QDeclarativeChangeSet::QDeclarativeChangeSet(const QDeclarativeChangeSet &change
, m_inserts(changeSet.m_inserts)
, m_changes(changeSet.m_changes)
, m_moveCounter(changeSet.m_moveCounter)
+ , m_difference(0)
{
}
@@ -66,6 +68,7 @@ QDeclarativeChangeSet &QDeclarativeChangeSet::operator =(const QDeclarativeChang
m_inserts = changeSet.m_inserts;
m_changes = changeSet.m_changes;
m_moveCounter = changeSet.m_moveCounter;
+ m_difference = changeSet.m_difference;
return *this;
}
@@ -330,6 +333,7 @@ void QDeclarativeChangeSet::applyRemovals(QVector<Remove> &removals, QVector<Ins
}
for (; remove != m_removes.end(); ++remove)
remove->index -= removeCount;
+ m_difference -= removeCount;
}
void QDeclarativeChangeSet::applyInsertions(QVector<Insert> &insertions)
@@ -354,6 +358,7 @@ void QDeclarativeChangeSet::applyInsertions(QVector<Insert> &insertions)
if (insert == m_inserts.end()) {
insert = m_inserts.insert(insert, *iit);
++insert;
+ insertCount += iit->count;
} else {
const int offset = index - insert->index;
if (offset < 0 || (offset == 0 && (iit->moveId != -1 || insert->moveId != -1))) {
@@ -395,6 +400,7 @@ void QDeclarativeChangeSet::applyInsertions(QVector<Insert> &insertions)
change->index += insertCount;
for (; insert != m_inserts.end(); ++insert)
insert->index += insertCount;
+ m_difference += insertCount;
}
void QDeclarativeChangeSet::applyChanges(QVector<Change> &changes)
diff --git a/src/declarative/util/qdeclarativechangeset_p.h b/src/declarative/util/qdeclarativechangeset_p.h
index 67533b02d5..b7554da503 100644
--- a/src/declarative/util/qdeclarativechangeset_p.h
+++ b/src/declarative/util/qdeclarativechangeset_p.h
@@ -103,10 +103,6 @@ public:
};
QDeclarativeChangeSet();
- QDeclarativeChangeSet(
- const QVector<Remove> &removals,
- const QVector<Insert> &insertions,
- const QVector<Change> &changes = QVector<Change>());
QDeclarativeChangeSet(const QDeclarativeChangeSet &changeSet);
~QDeclarativeChangeSet();
@@ -138,8 +134,12 @@ public:
m_inserts.clear();
m_changes.clear();
m_moveCounter = 0;
+ m_difference = 0;
}
+ int moveCounter() const { return m_moveCounter; }
+ int difference() const { return m_difference; }
+
private:
void applyRemovals(QVector<Remove> &removals, QVector<Insert> &insertions);
void applyInsertions(QVector<Insert> &insertions);
@@ -149,6 +149,7 @@ private:
QVector<Insert> m_inserts;
QVector<Change> m_changes;
int m_moveCounter;
+ int m_difference;
};
inline uint qHash(const QDeclarativeChangeSet::MoveKey &key) { return qHash(qMakePair(key.moveId, key.offset)); }
diff --git a/src/declarative/util/qdeclarativelistcompositor.cpp b/src/declarative/util/qdeclarativelistcompositor.cpp
new file mode 100644
index 0000000000..8fd4ec44dd
--- /dev/null
+++ b/src/declarative/util/qdeclarativelistcompositor.cpp
@@ -0,0 +1,1198 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdeclarativelistcompositor_p.h"
+
+#include <QtCore/qvarlengtharray.h>
+
+//#define QT_DECLARATIVE_VERIFY_MINIMAL
+//#define QT_DECLARATIVE_VERIFY_INTEGRITY
+
+#ifdef QT_DECLARATIVE_VERIFY_MINIMAL
+#define QT_DECLARATIVE_VERIFY_INTEGRITY
+static bool qt_verifyMinimal(
+ const QDeclarativeListCompositor::iterator &begin,
+ const QDeclarativeListCompositor::iterator &end)
+{
+ bool minimal = true;
+ int index = 0;
+
+ for (const QDeclarativeListCompositor::Range *range = begin->next; range != end; range = range->next, ++index) {
+ if (range->previous->list == range->list
+ && range->previous->flags == (range->flags & ~QDeclarativeListCompositor::AppendFlag)
+ && range->previous->end() == range->index) {
+ qWarning() << index << "Consecutive ranges";
+ qWarning() << *range->previous;
+ qWarning() << *range;
+ minimal = false;
+ }
+ }
+
+ return minimal;
+}
+
+#endif
+
+#ifdef QT_DECLARATIVE_VERIFY_INTEGRITY
+static bool qt_printInfo(const QDeclarativeListCompositor &compositor)
+{
+ qWarning() << compositor;
+ return true;
+}
+
+static bool qt_verifyIntegrity(
+ const QDeclarativeListCompositor::iterator &begin,
+ const QDeclarativeListCompositor::iterator &end,
+ const QDeclarativeListCompositor::iterator &cachedIt)
+{
+ bool valid = true;
+
+ int index = 0;
+ QDeclarativeListCompositor::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 != QDeclarativeListCompositor::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"
+ << QDeclarativeListCompositor::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_DECLARATIVE_VERIFY_MINIMAL)
+# define QT_DECLARATIVE_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_DECLARATIVE_VERIFY_INTEGRITY)
+# define QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt) \
+ && qt_printInfo(*this)));
+#else
+# define QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR
+#endif
+
+//#define QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(args) qDebug() << m_end.index[1] << m_end.index[0] << Q_FUNC_INFO args;
+#define QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(args)
+
+QDeclarativeListCompositor::iterator &QDeclarativeListCompositor::iterator::operator ++()
+{
+ while (!(range->flags & groupFlag)) {
+ incrementIndexes(range->count - offset);
+ offset = 0;
+ range = range->next;
+ }
+ incrementIndexes(1);
+ if (++offset == range->count) {
+ while (!((range = range->next)->flags & groupFlag))
+ incrementIndexes(range->count);
+ offset = 0;
+ }
+ return *this;
+}
+
+QDeclarativeListCompositor::iterator &QDeclarativeListCompositor::iterator::operator +=(int difference)
+{
+ Q_ASSERT(difference >= 0);
+ while (!(range->flags & groupFlag)) {
+ incrementIndexes(range->count - offset);
+ offset = 0;
+ range = range->next;
+ }
+ decrementIndexes(offset);
+ offset += difference;
+ while (offset >= range->count || !(range->flags & groupFlag)) {
+ if (range->flags & groupFlag)
+ offset -= range->count;
+ incrementIndexes(range->count);
+ range = range->next;
+ }
+ incrementIndexes(offset);
+ return *this;
+}
+
+QDeclarativeListCompositor::iterator &QDeclarativeListCompositor::iterator::operator -=(int difference)
+{
+ Q_ASSERT(difference >= 0);
+ while (!(range->flags & groupFlag)) {
+ decrementIndexes(offset);
+ range = range->previous;
+ offset = range->count;
+ }
+ decrementIndexes(offset);
+ offset -= difference;
+ while (offset < 0) {
+ range = range->previous;
+ if (range->flags & groupFlag)
+ offset += range->count;
+ decrementIndexes(range->count);
+ }
+ incrementIndexes(offset);
+ return *this;
+}
+
+QDeclarativeListCompositor::insert_iterator &QDeclarativeListCompositor::insert_iterator::operator ++()
+{
+ while (!(range->flags & groupFlag)) {
+ incrementIndexes(range->count - offset);
+ offset = 0;
+ range = range->next;
+ }
+ incrementIndexes(1);
+ if (++offset == range->count && !range->append()) {
+ while (!((range = range->next)->flags & groupFlag) ){
+ incrementIndexes(range->count);
+ }
+ offset = 0;
+ }
+ return *this;
+}
+
+QDeclarativeListCompositor::insert_iterator &QDeclarativeListCompositor::insert_iterator::operator +=(int difference)
+{
+ Q_ASSERT(difference >= 0);
+ while (!(range->flags & groupFlag) && range->flags & GroupMask) {
+ incrementIndexes(range->count - offset);
+ offset = 0;
+ range = range->next;
+ }
+ decrementIndexes(offset);
+ offset += difference;
+ while (offset > range->count
+ || (offset == range->count && !range->append() && offset > 0)
+ || (!(range->flags & groupFlag) && offset > 0)) {
+ if (range->flags & groupFlag)
+ offset -= range->count;
+ incrementIndexes(range->count);
+ range = range->next;
+ }
+ incrementIndexes(offset);
+ return *this;
+}
+
+QDeclarativeListCompositor::insert_iterator &QDeclarativeListCompositor::insert_iterator::operator -=(int difference)
+{
+ Q_ASSERT(difference >= 0);
+ while (!(range->flags & groupFlag) && range->flags & GroupMask) {
+ decrementIndexes(offset);
+ range = range->previous;
+ offset = range->count;
+ }
+ decrementIndexes(offset);
+ offset -= difference;
+ while (offset < 0) {
+ range = range->previous;
+ if (range->flags & groupFlag)
+ offset += range->count;
+ decrementIndexes(range->count);
+ }
+ incrementIndexes(offset);
+ for (Range *previous = range->previous; offset == 0 && previous->prepend(); previous = previous->previous) {
+ if (previous->append() && previous->inGroup()) {
+ offset = previous->count;
+ range = previous;
+ } else if (!previous->inGroup()) {
+ break;
+ }
+ }
+
+ return *this;
+}
+
+QDeclarativeListCompositor::QDeclarativeListCompositor()
+ : m_end(m_ranges.next, 0, Default, 2)
+ , m_cacheIt(m_end)
+ , m_groupCount(2)
+ , m_defaultFlags(PrependFlag | DefaultFlag)
+{
+}
+
+QDeclarativeListCompositor::~QDeclarativeListCompositor()
+{
+ for (Range *next, *range = m_ranges.next; range != &m_ranges; range = next) {
+ next = range->next;
+ delete range;
+ }
+}
+
+inline QDeclarativeListCompositor::Range *QDeclarativeListCompositor::insert(
+ Range *before, void *list, int index, int count, int flags)
+{
+ return new Range(before, list, index, count, flags);
+}
+
+inline QDeclarativeListCompositor::Range *QDeclarativeListCompositor::erase(
+ Range *range)
+{
+ Range *next = range->next;
+ next->previous = range->previous;
+ next->previous->next = range->next;
+ delete range;
+ return next;
+}
+
+void QDeclarativeListCompositor::setGroupCount(int count)
+{
+ m_groupCount = count;
+ m_end = iterator(&m_ranges, 0, Default, m_groupCount);
+ m_cacheIt = m_end;
+}
+
+int QDeclarativeListCompositor::count(Group group) const
+{
+ return m_end.index[group];
+}
+
+QDeclarativeListCompositor::iterator QDeclarativeListCompositor::find(Group group, int index)
+{
+ QT_DECLARATIVE_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);
+ if (offset > 0) {
+ m_cacheIt += offset;
+ } else if (offset < 0) {
+ m_cacheIt -= -offset;
+ } else if (offset == 0) {
+ m_cacheIt -= 0;
+ m_cacheIt += 0;
+ }
+ }
+ Q_ASSERT(m_cacheIt.index[group] == index);
+ Q_ASSERT(m_cacheIt->inGroup(group));
+ QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR
+ return m_cacheIt;
+}
+
+QDeclarativeListCompositor::iterator QDeclarativeListCompositor::find(Group group, int index) const
+{
+ return const_cast<QDeclarativeListCompositor *>(this)->find(group, index);
+}
+
+QDeclarativeListCompositor::insert_iterator QDeclarativeListCompositor::findInsertPosition(Group group, int index)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< group << index)
+ Q_ASSERT(index >=0 && index <= count(group));
+ insert_iterator it;
+ if (m_cacheIt == m_end) {
+ m_cacheIt = 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);
+ if (offset > 0) {
+ it += offset;
+ } else if (offset < 0) {
+ it -= -offset;
+ } else if (offset == 0) {
+ it -= 0;
+ it += 0;
+ }
+ }
+ Q_ASSERT(it.index[group] == index);
+ return it;
+}
+
+QDeclarativeListCompositor::iterator QDeclarativeListCompositor::begin(Group group)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< group)
+ m_cacheIt = iterator(m_ranges.next, 0, group, m_groupCount);
+ m_cacheIt += 0;
+ return m_cacheIt;
+}
+
+void QDeclarativeListCompositor::append(
+ void *list, int index, int count, int flags, QVector<Insert> *inserts)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << index << count << flags)
+ insert(m_end, list, index, count, flags, inserts);
+}
+
+void QDeclarativeListCompositor::insert(
+ Group group, int before, void *list, int index, int count, int flags, QVector<Insert> *inserts)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< group << before << list << index << count << flags)
+ insert(findInsertPosition(group, before), list, index, count, flags, inserts);
+}
+
+QDeclarativeListCompositor::iterator QDeclarativeListCompositor::insert(
+ iterator before, void *list, int index, int count, int flags, QVector<Insert> *inserts)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< before << list << index << count << flags)
+ if (inserts) {
+ inserts->append(Insert(before, count, flags & GroupMask));
+ }
+ if (before.offset > 0) {
+ *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)) {
+ 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)) {
+ before->next->index = before->index;
+ before->next->count += before->count;
+ *before = erase(*before);
+ }
+
+ m_end.incrementIndexes(count, flags);
+ m_cacheIt = before;
+ QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR
+ return before;
+}
+
+void QDeclarativeListCompositor::setFlags(
+ Group group, int index, int count, int flags, QVector<Insert> *inserts)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< group << index << count << flags)
+ setFlags(find(group, index), count, flags, inserts);
+}
+
+void QDeclarativeListCompositor::setFlags(
+ iterator from, int count, int flags, QVector<Insert> *inserts)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< from << count << flags)
+ if (!flags || !count)
+ return;
+
+ if (from.offset > 0) {
+ *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) {
+ from.incrementIndexes(from->count);
+ continue;
+ }
+ const int difference = qMin(count, from->count);
+ count -= difference;
+
+ const int insertFlags = ~from->flags & flags;
+ const int 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) {
+ from->previous->count += difference;
+ from->index += difference;
+ from->count -= difference;
+ if (from->count == 0) {
+ if (from->append())
+ from->previous->flags |= AppendFlag;
+ *from = erase(*from)->previous;
+ continue;
+ }
+ } else if (difference < from->count) {
+ *from = insert(*from, from->list, from->index, difference, setFlags)->next;
+ from->index += difference;
+ from->count -= difference;
+ } else {
+ 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)) {
+ from.offset = from->previous->count;
+ from->previous->count += from->count;
+ from->previous->flags = from->flags;
+ *from = erase(*from)->previous;
+ }
+ m_cacheIt = from;
+ QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR
+}
+
+void QDeclarativeListCompositor::clearFlags(
+ Group group, int index, int count, int flags, QVector<Remove> *removes)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< group << index << count << flags)
+ clearFlags(find(group, index), count, flags, removes);
+}
+
+void QDeclarativeListCompositor::clearFlags(
+ iterator from, int count, int flags, QVector<Remove> *removes)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< from << count << flags)
+ if (!flags || !count)
+ return;
+
+ const bool clearCache = flags & CacheFlag;
+
+ if (from.offset > 0) {
+ *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) {
+ from.incrementIndexes(from->count);
+ continue;
+ }
+ const int difference = qMin(count, from->count);
+ count -= difference;
+
+ const int removeFlags = from->flags & flags;
+ const int clearedFlags = from->flags & ~(flags | AppendFlag);
+ if (removeFlags && removes) {
+ const int maskedFlags = clearCache
+ ? (removeFlags & ~CacheFlag)
+ : (removeFlags | (from->flags & CacheFlag));
+ 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) {
+ from->previous->count += difference;
+ from->index += difference;
+ from->count -= difference;
+ if (from->count == 0) {
+ if (from->append())
+ from->previous->flags |= AppendFlag;
+ *from = erase(*from)->previous;
+ } else {
+ from.incrementIndexes(from->count);
+ }
+ } else if (difference < from->count) {
+ 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) {
+ from->flags &= ~flags;
+ } else {
+ *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)) {
+ from.offset = from->previous->count;
+ from->previous->count += from->count;
+ from->previous->flags = from->flags;
+ *from = erase(*from)->previous;
+ }
+ m_cacheIt = from;
+ QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR
+}
+
+void QDeclarativeListCompositor::removeList(void *list, QVector<Remove> *removes, bool destroyed)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << destroyed)
+ for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) {
+ if (it->list == list) {
+ const int flags = it->flags & (GroupMask | CacheFlag);
+ if (flags) {
+ removes->append(Remove(it, it->count, flags));
+ m_end.decrementIndexes(it->count, flags);
+ }
+ if (destroyed)
+ it->list = 0;
+ if (it->inCache()) {
+ it->flags = CacheFlag;
+ it.cacheIndex += it->count;
+ } else {
+ *it = erase(*it)->previous;
+ }
+ } else {
+ it.incrementIndexes(it->count);
+ }
+ }
+ m_cacheIt = m_end;
+ QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR
+}
+
+bool QDeclarativeListCompositor::verifyMoveTo(
+ Group fromGroup, int from, Group toGroup, int to, int count) const
+{
+ if (fromGroup != 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(fromGroup))
+ 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];
+}
+
+void QDeclarativeListCompositor::move(
+ Group fromGroup,
+ int from,
+ Group toGroup,
+ int to,
+ int count,
+ QVector<Remove> *removes,
+ QVector<Insert> *inserts)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< fromGroup << from << toGroup << to << count)
+ Q_ASSERT(count != 0);
+ Q_ASSERT(from >=0 && from + count <= m_end.index[toGroup]);
+ Q_ASSERT(verifyMoveTo(fromGroup, from, toGroup, to, count));
+
+ iterator fromIt = find(fromGroup, from);
+ if (fromIt.offset > 0) {
+ *fromIt = insert(
+ *fromIt, fromIt->list, fromIt->index, fromIt.offset, fromIt->flags & ~AppendFlag)->next;
+ fromIt->index += fromIt.offset;
+ fromIt->count -= fromIt.offset;
+ fromIt.offset = 0;
+ }
+
+ Range movedFlags;
+ for (int moveId = 0; count > 0;) {
+ if (fromIt != fromIt.group) {
+ fromIt.incrementIndexes(fromIt->count);
+ *fromIt = fromIt->next;
+ continue;
+ }
+ int difference = qMin(count, fromIt->count);
+
+ new Range(
+ &movedFlags,
+ fromIt->list,
+ fromIt->index,
+ difference,
+ fromIt->flags & ~(PrependFlag | AppendFlag));
+ if (removes)
+ removes->append(Remove(fromIt, difference, fromIt->flags, moveId++));
+ count -= difference;
+ fromIt->count -= difference;
+
+ 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) {
+ 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 (fromIt->append())
+ fromIt->previous->flags |= AppendFlag;
+ *fromIt = erase(*fromIt);
+ } else if (count > 0) {
+ *fromIt = fromIt->next;
+ }
+ }
+
+ 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;
+ }
+
+ insert_iterator toIt = fromIt;
+ toIt.setGroup(toGroup);
+ const int difference = to - toIt.index[toGroup];
+ if (difference > 0)
+ toIt += difference;
+ else
+ toIt -= -difference;
+
+ 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;
+ }
+
+ 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);
+ }
+ }
+
+ 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;
+ }
+ 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)
+ inserts->append(insert);
+ for (int i = 0; i < m_groupCount; ++i) {
+ if (insert.inGroup(i))
+ insert.index[i] += range->count;
+ }
+ ++insert.moveId;
+ next = range->next;
+ delete range;
+ }
+
+ m_cacheIt = toIt;
+ QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR
+}
+
+void QDeclarativeListCompositor::clear()
+{
+ QT_DECLARATIVE_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 QDeclarativeListCompositor::listItemsInserted(
+ QVector<Insert> *translatedInsertions,
+ void *list,
+ const QVector<QDeclarativeChangeSet::Insert> &insertions,
+ const QVector<MovedFlags> *movedFlags)
+{
+ QT_DECLARATIVE_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.incrementIndexes(it->count);
+ continue;
+ } else if (it->flags & MovedFlag) {
+ it->flags &= ~MovedFlag;
+ it.incrementIndexes(it->count);
+ continue;
+ }
+ foreach (const QDeclarativeChangeSet::Insert &insertion, insertions) {
+ int offset = insertion.index - it->index;
+ if ((offset > 0 && offset < it->count)
+ || (offset == 0 && it->prepend())
+ || (offset == it->count && it->append())) {
+ if (it->prepend()) {
+ int flags = m_defaultFlags;
+ if (insertion.isMove()) {
+ for (QVector<MovedFlags>::const_iterator move = movedFlags->begin();
+ move != movedFlags->end();
+ ++move) {
+ if (move->moveId == insertion.moveId) {
+ flags = move->flags;
+ break;
+ }
+ }
+ }
+ 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) {
+ it->count += insertion.count;
+ } else {
+ if (offset > 0) {
+ it.incrementIndexes(offset);
+ *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next;
+ }
+ *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 {
+ 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) {
+ it->index += insertion.count;
+ }
+ }
+ it.incrementIndexes(it->count);
+ }
+ m_cacheIt = m_end;
+ QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR
+}
+
+void QDeclarativeListCompositor::listItemsInserted(
+ void *list, int index, int count, QVector<Insert> *translatedInsertions)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << index << count)
+ Q_ASSERT(count > 0);
+
+ QVector<QDeclarativeChangeSet::Insert> insertions;
+ insertions.append(QDeclarativeChangeSet::Insert(index, count));
+
+ listItemsInserted(translatedInsertions, list, insertions);
+}
+
+void QDeclarativeListCompositor::listItemsRemoved(
+ QVector<Remove> *translatedRemovals,
+ void *list,
+ QVector<QDeclarativeChangeSet::Remove> *removals,
+ QVector<QDeclarativeChangeSet::Insert> *insertions,
+ QVector<MovedFlags> *movedFlags, int moveId)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << *removals)
+
+ for (iterator it(m_ranges.next, 0, Default, m_groupCount);
+ *it != &m_ranges && !removals->isEmpty();
+ *it = it->next) {
+ if (it->list != list) {
+ it.incrementIndexes(it->count);
+ continue;
+ }
+ bool removed = false;
+ for (QVector<QDeclarativeChangeSet::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) {
+ const int offset = qMax(0, relativeIndex);
+ int removeCount = qMin(it->count, relativeIndex + removal->count) - offset;
+ it->count -= removeCount;
+ int removeFlags = it->flags & 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()) {
+ QVector<QDeclarativeChangeSet::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) {
+ int splitMoveId = ++moveId;
+ removal = removals->insert(removal, QDeclarativeChangeSet::Remove(
+ removal->index, -relativeIndex, splitMoveId));
+ ++removal;
+ removal->count -= -relativeIndex;
+ insertion = insertions->insert(insertion, QDeclarativeChangeSet::Insert(
+ insertion->index, -relativeIndex, splitMoveId));
+ ++insertion;
+ insertion->index += -relativeIndex;
+ insertion->count -= -relativeIndex;
+ }
+
+ if (it->prepend()) {
+ removeFlags |= it->flags & CacheFlag;
+ translatedRemoval.moveId = ++moveId;
+ movedFlags->append(MovedFlags(moveId, it->flags & ~AppendFlag));
+
+ removal = removals->insert(removal, QDeclarativeChangeSet::Remove(
+ removal->index, removeCount, translatedRemoval.moveId));
+ ++removal;
+ insertion = insertions->insert(insertion, QDeclarativeChangeSet::Insert(
+ insertion->index, removeCount, translatedRemoval.moveId));
+ ++insertion;
+
+ removal->count -= removeCount;
+ insertion->index += removeCount;
+ insertion->count -= removeCount;
+ if (removal->count == 0) {
+ removal = removals->erase(removal);
+ insertion = insertions->erase(insertion);
+ --removal;
+ --insertion;
+ }
+ } else {
+ 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;
+ }
+ translatedRemoval.flags = 0;
+ removeFlags = 0;
+ }
+ } else if (it->inCache()) {
+ 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;
+ }
+ }
+ if (removeFlags & GroupMask)
+ translatedRemovals->append(translatedRemoval);
+ m_end.decrementIndexes(removeCount, removeFlags);
+ if (it->count == 0 && !it->append()) {
+ *it = erase(*it)->previous;
+ removed = true;
+ } else if (relativeIndex <= 0) {
+ it->index = removal->index;
+ }
+ } else if (relativeIndex < 0) {
+ it->index -= itemsRemoved;
+ }
+ }
+ if (it->next != &m_ranges
+ && it->list == it->next->list
+ && (it->flags == CacheFlag || it->end() == it->next->index)
+ && it->flags == (it->next->flags & ~AppendFlag)) {
+ it->count += it->next->count;
+ it->flags = it->next->flags;
+ erase(it->next);
+ *it = it->previous;
+ } else if (!removed) {
+ it.incrementIndexes(it->count);
+ }
+ }
+ m_cacheIt = m_end;
+ QT_DECLARATIVE_VERIFY_LISTCOMPOSITOR
+}
+
+void QDeclarativeListCompositor::listItemsRemoved(
+ void *list, int index, int count, QVector<Remove> *translatedRemovals)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << index << count)
+ Q_ASSERT(count >= 0);
+
+ QVector<QDeclarativeChangeSet::Remove> removals;
+ removals.append(QDeclarativeChangeSet::Remove(index, count));
+ listItemsRemoved(translatedRemovals, list, &removals, 0, 0, 0);
+}
+
+void QDeclarativeListCompositor::listItemsMoved(
+ void *list,
+ int from,
+ int to,
+ int count,
+ QVector<Remove> *translatedRemovals,
+ QVector<Insert> *translatedInsertions)
+{
+
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << from << to << count)
+ Q_ASSERT(count >= 0);
+
+ QVector<QDeclarativeChangeSet::Remove> removals;
+ QVector<QDeclarativeChangeSet::Insert> insertions;
+ QVector<MovedFlags> movedFlags;
+ removals.append(QDeclarativeChangeSet::Remove(from, count, 0));
+ insertions.append(QDeclarativeChangeSet::Insert(to, count, 0));
+
+ listItemsRemoved(translatedRemovals, list, &removals, &insertions, &movedFlags, 0);
+ listItemsInserted(translatedInsertions, list, insertions, &movedFlags);
+}
+
+void QDeclarativeListCompositor::listItemsChanged(
+ QVector<Change> *translatedChanges,
+ void *list,
+ const QVector<QDeclarativeChangeSet::Change> &changes)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << changes)
+ for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) {
+ if (!it->inGroup()) {
+ continue;
+ } else if (it->list != list) {
+ it.incrementIndexes(it->count);
+ continue;
+ }
+ foreach (const QDeclarativeChangeSet::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);
+ }
+}
+
+void QDeclarativeListCompositor::listItemsChanged(
+ void *list, int index, int count, QVector<Change> *translatedChanges)
+{
+ QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << index << count)
+ Q_ASSERT(count >= 0);
+ QVector<QDeclarativeChangeSet::Change> changes;
+ changes.append(QDeclarativeChangeSet::Change(index, count));
+ listItemsChanged(translatedChanges, list, changes);
+}
+
+void QDeclarativeListCompositor::listChanged(
+ void *list,
+ const QDeclarativeChangeSet &changeSet,
+ QVector<Remove> *translatedRemovals,
+ QVector<Insert> *translatedInsertions,
+ QVector<Change> *translatedChanges)
+{
+ QVector<QDeclarativeChangeSet::Remove> removals = changeSet.removes();
+ QVector<QDeclarativeChangeSet::Insert> insertions = changeSet.inserts();
+ QVector<MovedFlags> movedFlags;
+ listItemsRemoved(translatedRemovals, list, &removals, &insertions, &movedFlags, changeSet.moveCounter());
+ listItemsInserted(translatedInsertions, list, insertions, &movedFlags);
+ listItemsChanged(translatedChanges, list, changeSet.changes());
+}
+
+void QDeclarativeListCompositor::transition(
+ Group from,
+ Group to,
+ QVector<QDeclarativeChangeSet::Remove> *removes,
+ QVector<QDeclarativeChangeSet::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(QDeclarativeChangeSet::Remove(it.index[from]- removeCount, it->count));
+ removeCount += it->count;
+ } else if (it != from && it == to) {
+ inserts->append(QDeclarativeChangeSet::Insert(it.index[to], it->count));
+ }
+ it.incrementIndexes(it->count);
+ }
+}
+
+QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Group &group)
+{
+ switch (group) {
+ case QDeclarativeListCompositor::Cache: return debug << "Cache";
+ case QDeclarativeListCompositor::Default: return debug << "Default";
+ default: return (debug.nospace() << "Group" << int(group)).space();
+ }
+
+}
+
+QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Range &range)
+{
+ (debug.nospace()
+ << "Range("
+ << range.list) << " "
+ << range.index << " "
+ << range.count << " "
+ << (range.append() ? "A" : "0")
+ << (range.prepend() ? "P" : "0");
+ for (int i = QDeclarativeListCompositor::MaximumGroupCount - 1; i >= 2; --i)
+ debug << (range.inGroup(i) ? "1" : "0");
+ return (debug
+ << (range.inGroup(QDeclarativeListCompositor::Default) ? "D" : "0")
+ << (range.inGroup(QDeclarativeListCompositor::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];
+}
+
+QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::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 QDeclarativeListCompositor::Change &change)
+{
+ debug.nospace() << name << "(" << change.moveId << " " << change.count << " ";
+ for (int i = QDeclarativeListCompositor::MaximumGroupCount - 1; i >= 2; --i)
+ debug << (change.inGroup(i) ? "1" : "0");
+ debug << (change.inGroup(QDeclarativeListCompositor::Default) ? "D" : "0")
+ << (change.inGroup(QDeclarativeListCompositor::Cache) ? "C" : "0");
+ int i = QDeclarativeListCompositor::MaximumGroupCount - 1;
+ for (; i >= 0 && !change.inGroup(i); --i) {}
+ for (; i >= 0; --i)
+ debug << " " << change.index[i];
+ return (debug << ")").maybeSpace();
+}
+
+QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Change &change)
+{
+ return qt_print_change(debug, "Change", change);
+}
+
+QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Remove &remove)
+{
+ return qt_print_change(debug, "Remove", remove);
+}
+
+QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Insert &insert)
+{
+ return qt_print_change(debug, "Insert", insert);
+}
+
+QDebug operator <<(QDebug debug, const QDeclarativeListCompositor &list)
+{
+ int indexes[QDeclarativeListCompositor::MaximumGroupCount];
+ for (int i = 0; i < QDeclarativeListCompositor::MaximumGroupCount; ++i)
+ indexes[i] = 0;
+ debug.nospace() << "QDeclarativeListCompositor(";
+ qt_print_indexes(debug, list.m_groupCount, list.m_end.index);
+ for (QDeclarativeListCompositor::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();
+}
diff --git a/src/declarative/util/qdeclarativelistcompositor_p.h b/src/declarative/util/qdeclarativelistcompositor_p.h
new file mode 100644
index 0000000000..cd2d9b3f5f
--- /dev/null
+++ b/src/declarative/util/qdeclarativelistcompositor_p.h
@@ -0,0 +1,370 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVELISTCOMPOSITOR_P_H
+#define QDECLARATIVELISTCOMPOSITOR_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/qdeclarativechangeset_p.h>
+
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QDeclarativeListCompositor
+{
+public:
+ enum { MaximumGroupCount = 10 };
+
+ enum Group
+ {
+ Cache = 0,
+ Default = 1
+ };
+
+ enum Flag
+ {
+ CacheFlag = 0x000001,
+ DefaultFlag = 0x000002,
+ GroupMask = 0x00FFFE,
+ PrependFlag = 0x100000,
+ AppendFlag = 0x200000,
+ MovedFlag = 0x400000,
+ RemoveFlags = GroupMask | PrependFlag | AppendFlag
+ };
+
+ 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;
+ int 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 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 ++();
+ iterator &operator +=(int difference);
+ 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, int flags);
+ inline void decrementIndexes(int difference, int 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 ++();
+ insert_iterator &operator +=(int difference);
+ insert_iterator &operator -=(int difference);
+ };
+
+ struct Change
+ {
+ inline Change() {}
+ inline Change(iterator it, int count, int flags, int moveId = -1);
+ int count;
+ int 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, int flags, int moveId = -1)
+ : Change(it, count, flags, moveId) {}
+ };
+
+ struct Remove : public Change
+ {
+ Remove() {}
+ Remove(iterator it, int count, int flags, int moveId = -1)
+ : Change(it, count, flags, moveId) {}
+ };
+
+ QDeclarativeListCompositor();
+ ~QDeclarativeListCompositor();
+
+ 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 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);
+
+ iterator begin(Group group);
+ const iterator &end() { return m_end; }
+
+ void append(void *list, int index, int count, int flags, QVector<Insert> *inserts = 0);
+ void insert(Group group, int before, void *list, int index, int count, int flags, QVector<Insert> *inserts = 0);
+ iterator insert(iterator before, void *list, int index, int count, int flags, QVector<Insert> *inserts = 0);
+
+ void setFlags(Group group, int index, int count, int flags, QVector<Insert> *inserts = 0);
+ void setFlags(iterator from, int count, int flags, QVector<Insert> *inserts = 0);
+
+ void clearFlags(Group group, int index, int count, int flags, QVector<Remove> *removals = 0);
+ void clearFlags(iterator from, int count, int flags, QVector<Remove> *removals = 0);
+
+ void removeList(void *list, QVector<Remove> *removals, bool destroyed);
+
+ bool verifyMoveTo(Group fromGroup, int from, Group toGroup, int to, int count) const;
+
+ void move(
+ Group fromGroup,
+ int from,
+ Group toGroup,
+ int to,
+ int count,
+ 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 listChanged(
+ void *list,
+ const QDeclarativeChangeSet &changeSet,
+ QVector<Remove> *removals,
+ QVector<Insert> *inserts,
+ QVector<Change> *changes);
+
+ void transition(
+ Group from,
+ Group to,
+ QVector<QDeclarativeChangeSet::Remove> *removes,
+ QVector<QDeclarativeChangeSet::Insert> *inserts);
+
+private:
+ Range m_ranges;
+ iterator m_end;
+ iterator m_cacheIt;
+ int m_groupCount;
+ int m_defaultFlags;
+
+ inline Range *insert(Range *before, void *list, int index, int count, int flags);
+ inline Range *erase(Range *range);
+
+ struct MovedFlags
+ {
+ MovedFlags() {}
+ MovedFlags(int moveId, int flags) : moveId(moveId), flags(flags) {}
+
+ int moveId;
+ int flags;
+ };
+
+ void listItemsRemoved(
+ QVector<Remove> *translatedRemovals,
+ void *list,
+ QVector<QDeclarativeChangeSet::Remove> *removals,
+ QVector<QDeclarativeChangeSet::Insert> *insertions = 0,
+ QVector<MovedFlags> *movedFlags = 0,
+ int moveId = 0);
+ void listItemsInserted(
+ QVector<Insert> *translatedInsertions,
+ void *list,
+ const QVector<QDeclarativeChangeSet::Insert> &insertions,
+ const QVector<MovedFlags> *movedFlags = 0);
+ void listItemsChanged(
+ QVector<Change> *translatedChanges,
+ void *list,
+ const QVector<QDeclarativeChangeSet::Change> &changes);
+
+ friend Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor &list);
+};
+
+inline QDeclarativeListCompositor::iterator::iterator()
+ : range(0), offset(0), group(Default), groupCount(0) {}
+inline QDeclarativeListCompositor::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 QDeclarativeListCompositor::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 QDeclarativeListCompositor::iterator::incrementIndexes(int difference, int flags)
+{
+ for (int i = 0; i < groupCount; ++i) {
+ if (flags & (1 << i))
+ index[i] += difference;
+ }
+}
+
+inline void QDeclarativeListCompositor::iterator::decrementIndexes(int difference, int flags)
+{
+ for (int i = 0; i < groupCount; ++i) {
+ if (flags & (1 << i))
+ index[i] -= difference;
+ }
+}
+
+inline QDeclarativeListCompositor::insert_iterator::insert_iterator(
+ Range *range, int offset, Group group, int groupCount)
+ : iterator(range, offset, group, groupCount) {}
+
+inline QDeclarativeListCompositor::Change::Change(iterator it, int count, int 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 QDeclarativeListCompositor::Group &group);
+Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Range &range);
+Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::iterator &it);
+Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Change &change);
+Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Remove &remove);
+Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Insert &insert);
+Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor &list);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/declarative/util/util.pri b/src/declarative/util/util.pri
index ddd702609c..ae67a8618d 100644
--- a/src/declarative/util/util.pri
+++ b/src/declarative/util/util.pri
@@ -29,6 +29,7 @@ SOURCES += \
$$PWD/qdeclarativelistmodelworkeragent.cpp \
$$PWD/qdeclarativepath.cpp \
$$PWD/qdeclarativechangeset.cpp \
+ $$PWD/qdeclarativelistcompositor.cpp \
$$PWD/qlistmodelinterface.cpp \
$$PWD/qdeclarativepathinterpolator.cpp \
$$PWD/qdeclarativesvgparser.cpp
@@ -67,6 +68,7 @@ HEADERS += \
$$PWD/qdeclarativepath_p.h \
$$PWD/qdeclarativepath_p_p.h \
$$PWD/qdeclarativechangeset_p.h \
+ $$PWD/qdeclarativelistcompositor_p.h \
$$PWD/qlistmodelinterface_p.h \
$$PWD/qdeclarativepathinterpolator_p.h \
$$PWD/qdeclarativesvgparser_p.h
diff --git a/tests/auto/declarative/declarative.pro b/tests/auto/declarative/declarative.pro
index f02bc511cb..324880120b 100644
--- a/tests/auto/declarative/declarative.pro
+++ b/tests/auto/declarative/declarative.pro
@@ -42,6 +42,7 @@ PRIVATETESTS += \
qdeclarativeimageprovider \
qdeclarativeinstruction \
qdeclarativelanguage \
+ qdeclarativelistcompositor \
qdeclarativelistmodel \
qdeclarativeproperty \
qdeclarativepropertymap \
diff --git a/tests/auto/declarative/qdeclarativelistcompositor/qdeclarativelistcompositor.pro b/tests/auto/declarative/qdeclarativelistcompositor/qdeclarativelistcompositor.pro
new file mode 100644
index 0000000000..e3988b33cb
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativelistcompositor/qdeclarativelistcompositor.pro
@@ -0,0 +1,17 @@
+load(qttest_p4)
+contains(QT_CONFIG,declarative): QT += declarative gui
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_qdeclarativelistcompositor.cpp
+
+symbian: {
+ importFiles.files = data
+ importFiles.path = .
+ DEPLOYMENT += importFiles
+} else {
+ DEFINES += SRCDIR=\\\"$$PWD\\\"
+}
+
+CONFIG += parallel_test
+
+QT += core-private gui-private declarative-private
diff --git a/tests/auto/declarative/qdeclarativelistcompositor/tst_qdeclarativelistcompositor.cpp b/tests/auto/declarative/qdeclarativelistcompositor/tst_qdeclarativelistcompositor.cpp
new file mode 100644
index 0000000000..030d2e20d0
--- /dev/null
+++ b/tests/auto/declarative/qdeclarativelistcompositor/tst_qdeclarativelistcompositor.cpp
@@ -0,0 +1,666 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qtest.h>
+#include <private/qdeclarativelistcompositor_p.h>
+
+template<typename T, int N> int lengthOf(const T (&)[N]) { return N; }
+
+typedef QDeclarativeListCompositor C;
+
+class tst_qdeclarativelistcompositor : public QObject
+{
+ Q_OBJECT
+
+ enum {
+ VisibleFlag = 0x04,
+ SelectionFlag = 0x08
+ };
+
+ static const C::Group Visible = C::Group(2);
+ static const C::Group Selection = C::Group(3);
+
+private slots:
+ void insert();
+ void clearFlags();
+ void setFlags();
+ void move();
+ void clear();
+ void listItemsInserted();
+ void listItemsRemoved();
+ void listItemsMoved();
+ void listItemsChanged();
+};
+
+void tst_qdeclarativelistcompositor::insert()
+{
+ QDeclarativeListCompositor compositor;
+ compositor.setGroupCount(4);
+ compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
+
+ C::iterator it;
+
+ int listA; int *a = &listA;
+ int listB; int *b = &listB;
+ int listC; int *c = &listC;
+
+ {
+ compositor.append(a, 0, 12, C::AppendFlag | C::PrependFlag | C::DefaultFlag);
+ const int indexes[] = {0,1,2,3,4,5,6,7,8,9,10,11};
+ const int *lists[] = {a,a,a,a,a,a,a,a,a,a, a, a};
+ QCOMPARE(compositor.count(C::Default), lengthOf(indexes));
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ it = compositor.find(C::Default, i);
+ QCOMPARE(it.list<int>(), lists[i]);
+ if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]);
+ }
+ } {
+ compositor.append(b, 4, 4, C::DefaultFlag);
+ const int indexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7};
+ const int *lists[] = {a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b};
+ QCOMPARE(compositor.count(C::Default), lengthOf(indexes));
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ it = compositor.find(C::Default, i);
+ QCOMPARE(it.list<int>(), lists[i]);
+ if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]);
+ }
+ } { // Insert at end.
+ compositor.insert(
+ C::Default, 16, c, 2, 2, C::DefaultFlag);
+ const int indexes[] = {0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,2,3};
+ const int *lists[] = {a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b,c,c};
+ QCOMPARE(compositor.count(C::Default), lengthOf(indexes));
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ it = compositor.find(C::Default, i);
+ QCOMPARE(it.list<int>(), lists[i]);
+ if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]);
+ }
+ } { // Insert at start
+ compositor.insert(
+ C::Default, 0, c, 6, 4, C::DefaultFlag);
+ const int indexes[] = {6,7,8,9,0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,2,3};
+ const int *lists[] = {c,c,c,c,a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b,c,c};
+ QCOMPARE(compositor.count(C::Default), lengthOf(indexes));
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ it = compositor.find(C::Default, i);
+ QCOMPARE(it.list<int>(), lists[i]);
+ if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]);
+ }
+ } { // Insert after static range.
+ compositor.insert(
+ C::Default, 4, b, 0, 8, C::AppendFlag | C::PrependFlag | C::DefaultFlag);
+ const int indexes[] = {6,7,8,9,0,1,2,3,4,5,6,7,0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,2,3};
+ const int *lists[] = {c,c,c,c,b,b,b,b,b,b,b,b,a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b,c,c};
+ QCOMPARE(compositor.count(C::Default), lengthOf(indexes));
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ it = compositor.find(C::Default, i);
+ QCOMPARE(it.list<int>(), lists[i]);
+ if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]);
+ }
+ } { // Insert at end of dynamic range.
+ compositor.insert(
+ C::Default, 12, c, 0, 4, C::AppendFlag | C::PrependFlag | C::DefaultFlag);
+ const int indexes[] = {6,7,8,9,0,1,2,3,4,5,6,7,0,1,2,3,0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,2,3};
+ const int *lists[] = {c,c,c,c,b,b,b,b,b,b,b,b,c,c,c,c,a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b,c,c};
+ QCOMPARE(compositor.count(C::Default), lengthOf(indexes));
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ it = compositor.find(C::Default, i);
+ QCOMPARE(it.list<int>(), lists[i]);
+ if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]);
+ }
+ } { // Insert into range.
+ compositor.insert(
+ C::Default, 8, c, 0, 4, C::AppendFlag | C::PrependFlag | C::DefaultFlag);
+ const int indexes[] = {6,7,8,9,0,1,2,3,0,1,2,3,4,5,6,7,0,1,2,3,0,1,2,3,4,5,6,7,8,9,10,11,4,5,6,7,2,3};
+ const int *lists[] = {c,c,c,c,b,b,b,b,c,c,c,c,b,b,b,b,c,c,c,c,a,a,a,a,a,a,a,a,a,a, a, a,b,b,b,b,c,c};
+ QCOMPARE(compositor.count(C::Default), lengthOf(indexes));
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ it = compositor.find(C::Default, i);
+ QCOMPARE(it.list<int>(), lists[i]);
+ if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]);
+ }
+ }
+}
+
+void tst_qdeclarativelistcompositor::clearFlags()
+{
+ QDeclarativeListCompositor compositor;
+ compositor.setGroupCount(4);
+ compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
+ int listA;
+
+ QVector<C::Remove> removes;
+ compositor.append(&listA, 0, 12, C::PrependFlag | C::DefaultFlag | VisibleFlag | C::CacheFlag | SelectionFlag);
+ compositor.append(0, 0, 4, C::DefaultFlag | VisibleFlag | C::CacheFlag | SelectionFlag);
+ QCOMPARE(compositor.count(C::Default), 16);
+ QCOMPARE(compositor.count(Visible), 16);
+ QCOMPARE(compositor.count(C::Cache), 16);
+ QCOMPARE(compositor.count(Selection), 16);
+
+ compositor.clearFlags(C::Default, 2, 2, SelectionFlag, &removes);
+ QCOMPARE(removes.count(), 1);
+ QCOMPARE(removes.at(0).index[C::Default], 2);
+ QCOMPARE(removes.at(0).index[Visible], 2);
+ QCOMPARE(removes.at(0).index[C::Cache], 2);
+ QCOMPARE(removes.at(0).index[Selection], 2);
+ QCOMPARE(removes.at(0).count, 2);
+ QCOMPARE(removes.at(0).flags, SelectionFlag | C::CacheFlag);
+ QCOMPARE(compositor.count(C::Default), 16);
+ QCOMPARE(compositor.count(Visible), 16);
+ QCOMPARE(compositor.count(C::Cache), 16);
+ QCOMPARE(compositor.count(Selection), 14);
+ QCOMPARE(compositor.find(C::Default, 1)->flags, C::PrependFlag | C::DefaultFlag | VisibleFlag | C::CacheFlag | SelectionFlag);
+ QCOMPARE(compositor.find(C::Default, 2)->flags, C::PrependFlag | C::DefaultFlag | VisibleFlag | C::CacheFlag);
+ QCOMPARE(compositor.find(C::Default, 3)->flags, C::PrependFlag | C::DefaultFlag | VisibleFlag | C::CacheFlag);
+ QCOMPARE(compositor.find(C::Default, 4)->flags, C::PrependFlag | C::DefaultFlag | VisibleFlag | C::CacheFlag | SelectionFlag);
+
+ removes.clear();
+ compositor.clearFlags(Selection, 1, 2, VisibleFlag, &removes);
+ QCOMPARE(removes.count(), 2);
+ QCOMPARE(removes.at(0).index[C::Default], 1);
+ QCOMPARE(removes.at(0).index[Visible], 1);
+ QCOMPARE(removes.at(0).index[C::Cache], 1);
+ QCOMPARE(removes.at(0).index[Selection], 1);
+ QCOMPARE(removes.at(0).count, 1);
+ QCOMPARE(removes.at(0).flags, VisibleFlag | C::CacheFlag);
+ QCOMPARE(removes.at(1).index[C::Default], 4);
+ QCOMPARE(removes.at(1).index[Visible], 3);
+ QCOMPARE(removes.at(1).index[C::Cache], 4);
+ QCOMPARE(removes.at(1).index[Selection], 2);
+ QCOMPARE(removes.at(1).count, 1);
+ QCOMPARE(removes.at(1).flags, VisibleFlag | C::CacheFlag);
+ QCOMPARE(compositor.count(C::Default), 16);
+ QCOMPARE(compositor.count(Visible), 14);
+ QCOMPARE(compositor.count(C::Cache), 16);
+ QCOMPARE(compositor.count(Selection), 14);
+ QCOMPARE(compositor.find(C::Default, 1)->flags, C::PrependFlag | C::DefaultFlag | C::CacheFlag | SelectionFlag);
+ QCOMPARE(compositor.find(C::Default, 2)->flags, C::PrependFlag | C::DefaultFlag | VisibleFlag | C::CacheFlag);
+ QCOMPARE(compositor.find(C::Default, 3)->flags, C::PrependFlag | C::DefaultFlag | VisibleFlag | C::CacheFlag);
+ QCOMPARE(compositor.find(C::Default, 4)->flags, C::PrependFlag | C::DefaultFlag | C::CacheFlag | SelectionFlag);
+
+ removes.clear();
+ compositor.clearFlags(C::Default, 13, 1, C::PrependFlag | C::DefaultFlag | VisibleFlag| SelectionFlag, &removes);
+ QCOMPARE(removes.count(), 1);
+ QCOMPARE(removes.at(0).index[C::Default], 13);
+ QCOMPARE(removes.at(0).index[Visible], 11);
+ QCOMPARE(removes.at(0).index[C::Cache], 13);
+ QCOMPARE(removes.at(0).index[Selection], 11);
+ QCOMPARE(removes.at(0).count, 1);
+ QCOMPARE(removes.at(0).flags, C::DefaultFlag | VisibleFlag | C::CacheFlag | SelectionFlag);
+ QCOMPARE(compositor.count(C::Default), 15);
+ QCOMPARE(compositor.count(Visible), 13);
+ QCOMPARE(compositor.count(C::Cache), 16);
+ QCOMPARE(compositor.count(Selection), 13);
+ QCOMPARE(compositor.find(C::Default, 11)->flags, C::PrependFlag | C::DefaultFlag | VisibleFlag | C::CacheFlag | SelectionFlag);
+ QCOMPARE(compositor.find(C::Cache, 11)->flags, C::PrependFlag | C::DefaultFlag | VisibleFlag | C::CacheFlag | SelectionFlag);
+ QCOMPARE(compositor.find(C::Default, 12)->flags, C::DefaultFlag | VisibleFlag | C::CacheFlag | SelectionFlag);
+ QCOMPARE(compositor.find(C::Cache, 12)->flags, C::DefaultFlag | VisibleFlag | C::CacheFlag | SelectionFlag);
+ QCOMPARE(compositor.find(C::Cache, 13)->flags, int(C::CacheFlag));
+ QCOMPARE(compositor.find(C::Default, 13)->flags, C::DefaultFlag | VisibleFlag | C::CacheFlag | SelectionFlag);
+ QCOMPARE(compositor.find(C::Cache, 14)->flags, C::DefaultFlag | VisibleFlag | C::CacheFlag | SelectionFlag);
+
+ removes.clear();
+ compositor.clearFlags(C::Cache, 11, 4, C::CacheFlag);
+ QCOMPARE(removes.count(), 0);
+ QCOMPARE(compositor.count(C::Default), 15);
+ QCOMPARE(compositor.count(Visible), 13);
+ QCOMPARE(compositor.count(C::Cache), 12);
+ QCOMPARE(compositor.count(Selection), 13);
+ QCOMPARE(compositor.find(C::Default, 11)->flags, C::PrependFlag | C::DefaultFlag | VisibleFlag| SelectionFlag);
+ QCOMPARE(compositor.find(C::Default, 12)->flags, C::DefaultFlag | VisibleFlag| SelectionFlag);
+ QCOMPARE(compositor.find(C::Default, 13)->flags, C::DefaultFlag | VisibleFlag| SelectionFlag);
+
+ removes.clear();
+ compositor.clearFlags(C::Default, 11, 3, C::DefaultFlag | VisibleFlag| SelectionFlag, &removes);
+ QCOMPARE(removes.count(), 2);
+ QCOMPARE(removes.at(0).index[C::Default], 11);
+ QCOMPARE(removes.at(0).index[Visible], 9);
+ QCOMPARE(removes.at(0).index[C::Cache], 11);
+ QCOMPARE(removes.at(0).index[Selection], 9);
+ QCOMPARE(removes.at(0).count, 1);
+ QCOMPARE(removes.at(0).flags, C::DefaultFlag | VisibleFlag| SelectionFlag);
+ QCOMPARE(removes.at(1).index[C::Default], 11);
+ QCOMPARE(removes.at(1).index[Visible], 9);
+ QCOMPARE(removes.at(1).index[C::Cache], 11);
+ QCOMPARE(removes.at(1).index[Selection], 9);
+ QCOMPARE(removes.at(1).count, 2);
+ QCOMPARE(removes.at(1).flags, C::DefaultFlag | VisibleFlag| SelectionFlag);
+}
+
+void tst_qdeclarativelistcompositor::setFlags()
+{
+ QDeclarativeListCompositor compositor;
+ compositor.setGroupCount(4);
+ compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
+ int listA;
+
+ QVector<C::Insert> inserts;
+ compositor.append(&listA, 0, 12, C::DefaultFlag);
+ compositor.append(0, 0, 4, C::CacheFlag);
+ QCOMPARE(compositor.count(C::Default), 12);
+ QCOMPARE(compositor.count(Visible), 0);
+ QCOMPARE(compositor.count(C::Cache), 4);
+ QCOMPARE(compositor.count(Selection), 0);
+
+ compositor.setFlags(C::Default, 2, 2, C::DefaultFlag, &inserts);
+ QCOMPARE(inserts.count(), 0);
+ QCOMPARE(compositor.count(C::Default), 12);
+ QCOMPARE(compositor.count(Visible), 0);
+ QCOMPARE(compositor.count(C::Cache), 4);
+ QCOMPARE(compositor.count(Selection), 0);
+
+ compositor.setFlags(C::Default, 2, 2, VisibleFlag, &inserts);
+ QCOMPARE(inserts.count(), 1);
+ QCOMPARE(inserts.at(0).index[C::Default], 2);
+ QCOMPARE(inserts.at(0).index[Visible], 0);
+ QCOMPARE(inserts.at(0).index[C::Cache], 0);
+ QCOMPARE(inserts.at(0).index[Selection], 0);
+ QCOMPARE(inserts.at(0).flags, int(VisibleFlag));
+ QCOMPARE(compositor.find(C::Default, 1)->flags, int(C::DefaultFlag));
+ QCOMPARE(compositor.find(C::Default, 2)->flags, C::DefaultFlag | VisibleFlag);
+ QCOMPARE(compositor.find(C::Default, 3)->flags, C::DefaultFlag | VisibleFlag);
+ QCOMPARE(compositor.count(C::Default), 12);
+ QCOMPARE(compositor.count(Visible), 2);
+ QCOMPARE(compositor.count(C::Cache), 4);
+ QCOMPARE(compositor.count(Selection), 0);
+
+ inserts.clear();
+ compositor.setFlags(C::Default, 6, 2, VisibleFlag);
+ compositor.setFlags(Visible, 1, 2, SelectionFlag | C::CacheFlag, &inserts);
+ QCOMPARE(inserts.count(), 2);
+ QCOMPARE(inserts.at(0).index[C::Default], 3);
+ QCOMPARE(inserts.at(0).index[Visible], 1);
+ QCOMPARE(inserts.at(0).index[C::Cache], 0);
+ QCOMPARE(inserts.at(0).index[Selection], 0);
+ QCOMPARE(inserts.at(0).flags, SelectionFlag | C::CacheFlag);
+ QCOMPARE(inserts.at(1).index[C::Default], 6);
+ QCOMPARE(inserts.at(1).index[Visible], 2);
+ QCOMPARE(inserts.at(1).index[C::Cache], 1);
+ QCOMPARE(inserts.at(1).index[Selection], 1);
+ QCOMPARE(inserts.at(1).flags, SelectionFlag | C::CacheFlag);
+ QCOMPARE(compositor.find(C::Default, 3)->flags, C::DefaultFlag | VisibleFlag| SelectionFlag | C::CacheFlag);
+ QCOMPARE(compositor.find(C::Default, 4)->flags, int(C::DefaultFlag));
+ QCOMPARE(compositor.find(C::Default, 5)->flags, int(C::DefaultFlag));
+ QCOMPARE(compositor.find(C::Default, 6)->flags, C::DefaultFlag | VisibleFlag | SelectionFlag | C::CacheFlag);
+ QCOMPARE(compositor.count(C::Default), 12);
+ QCOMPARE(compositor.count(Visible), 4);
+ QCOMPARE(compositor.count(C::Cache), 6);
+ QCOMPARE(compositor.count(Selection), 2);
+
+ inserts.clear();
+ compositor.setFlags(C::Cache, 3, 1, VisibleFlag, &inserts);
+ QCOMPARE(inserts.count(), 1);
+ QCOMPARE(inserts.at(0).index[C::Default], 12);
+ QCOMPARE(inserts.at(0).index[Visible], 4);
+ QCOMPARE(inserts.at(0).index[C::Cache], 3);
+ QCOMPARE(inserts.at(0).index[Selection], 2);
+ QCOMPARE(inserts.at(0).flags, VisibleFlag | C::CacheFlag);
+ QCOMPARE(compositor.find(C::Cache, 3)->flags, VisibleFlag | C::CacheFlag);
+ QCOMPARE(compositor.count(C::Default), 12);
+ QCOMPARE(compositor.count(Visible), 5);
+ QCOMPARE(compositor.count(C::Cache), 6);
+ QCOMPARE(compositor.count(Selection), 2);
+}
+
+void tst_qdeclarativelistcompositor::move()
+{
+ QDeclarativeListCompositor compositor;
+ compositor.setGroupCount(4);
+ compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
+ C::iterator it;
+
+ int listA; const int *a = &listA;
+ int listB; const int *b = &listB;
+ int listC; const int *c = &listC;
+
+ compositor.append(&listA, 0, 6, C::DefaultFlag);
+ compositor.append(&listB, 0, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag);
+ compositor.append(0, 0, 4, C::CacheFlag | C::DefaultFlag);
+ compositor.append(&listC, 0, 6, C::AppendFlag | C::PrependFlag | C::DefaultFlag);
+ compositor.setFlags(C::Default, 18, 2, C::CacheFlag);
+ QCOMPARE(compositor.count(C::Default), 22);
+
+ { const int indexes[] = {0,1,2,3,4,5,0,1,2,3,4,5,0,1,2,3,0,1,2,3,4,5};
+ const int *lists[] = {a,a,a,a,a,a,b,b,b,b,b,b,0,0,0,0,c,c,c,c,c,c};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ it = compositor.find(C::Default, i);
+ QCOMPARE(it.list<int>(), lists[i]);
+ if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]);
+ }
+ }
+
+ compositor.move(C::Default, 15, C::Default, 0, 1);
+ QCOMPARE(compositor.count(C::Default), 22);
+ { const int indexes[] = {0,0,1,2,3,4,5,0,1,2,3,4,5,1,2,3,0,1,2,3,4,5};
+ const int *lists[] = {0,a,a,a,a,a,a,b,b,b,b,b,b,0,0,0,c,c,c,c,c,c};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ it = compositor.find(C::Default, i);
+ QCOMPARE(it.list<int>(), lists[i]);
+ if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]);
+ }
+ }
+
+ compositor.move(C::Default, 15, C::Default, 1, 1);
+ QCOMPARE(compositor.count(C::Default), 22);
+ { const int indexes[] = {0,1,0,1,2,3,4,5,0,1,2,3,4,5,2,3,0,1,2,3,4,5};
+ const int *lists[] = {0,0,a,a,a,a,a,a,b,b,b,b,b,b,0,0,c,c,c,c,c,c};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ it = compositor.find(C::Default, i);
+ QCOMPARE(it.list<int>(), lists[i]);
+ if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]);
+ }
+ }
+
+ compositor.move(C::Default, 0, C::Default, 3, 2);
+ QCOMPARE(compositor.count(C::Default), 22);
+ { const int indexes[] = {0,1,2,0,1,3,4,5,0,1,2,3,4,5,2,3,0,1,2,3,4,5};
+ const int *lists[] = {a,a,a,0,0,a,a,a,b,b,b,b,b,b,0,0,c,c,c,c,c,c};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ it = compositor.find(C::Default, i);
+ QCOMPARE(it.list<int>(), lists[i]);
+ if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]);
+ }
+ }
+
+ compositor.move(C::Default, 7, C::Default, 1, 10);
+ QCOMPARE(compositor.count(C::Default), 22);
+ { const int indexes[] = {0,5,0,1,2,3,4,5,0,1,0,1,2,2,3,3,4,1,2,3,4,5};
+ const int *lists[] = {a,a,b,b,b,b,b,b,0,0,c,a,a,0,0,a,a,c,c,c,c,c};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ it = compositor.find(C::Default, i);
+ QCOMPARE(it.list<int>(), lists[i]);
+ if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]);
+ }
+ }
+
+ compositor.move(C::Default, 17, C::Default, 20, 2);
+ QCOMPARE(compositor.count(C::Default), 22);
+ { const int indexes[] = {0,5,0,1,2,3,4,5,0,1,0,1,2,2,3,3,4,3,4,5,1,2};
+ const int *lists[] = {a,a,b,b,b,b,b,b,0,0,c,a,a,0,0,a,a,c,c,c,c,c};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ it = compositor.find(C::Default, i);
+ QCOMPARE(it.list<int>(), lists[i]);
+ if (lists[i]) QCOMPARE(it.modelIndex(), indexes[i]);
+ }
+ }
+}
+
+void tst_qdeclarativelistcompositor::clear()
+{
+ QDeclarativeListCompositor compositor;
+ compositor.setGroupCount(4);
+ compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
+
+ int listA;
+ int listB;
+
+ compositor.append(&listA, 0, 8, C::AppendFlag | C::PrependFlag | VisibleFlag | C::DefaultFlag);
+ compositor.append(&listB, 4, 5, VisibleFlag | C::DefaultFlag);
+ compositor.append(0, 0, 3, VisibleFlag | C::DefaultFlag | C::CacheFlag);
+
+ QCOMPARE(compositor.count(C::Default), 16);
+ QCOMPARE(compositor.count(Visible), 16);
+ QCOMPARE(compositor.count(C::Cache), 3);
+
+ compositor.clear();
+ QCOMPARE(compositor.count(C::Default), 0);
+ QCOMPARE(compositor.count(Visible), 0);
+ QCOMPARE(compositor.count(C::Cache), 0);
+}
+
+void tst_qdeclarativelistcompositor::listItemsInserted()
+{
+ QDeclarativeListCompositor compositor;
+ compositor.setGroupCount(4);
+ compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
+
+ int listA;
+ int listB;
+ QVector<C::Insert> inserts;
+
+ compositor.append(&listA, 0, 7, C::AppendFlag | C::PrependFlag | C::DefaultFlag);
+ compositor.append(&listB, 0, 4, C::DefaultFlag);
+ compositor.move(C::Default, 2, C::Default, 8, 3);
+ QCOMPARE(compositor.count(C::Default), 11);
+ { const int indexes[] = {/*A*/0,1,5,6,/*B*/0,1,2,3,/*A*/2,3,4};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ QCOMPARE(compositor.find(C::Default, i).modelIndex(), indexes[i]);
+ }
+ }
+
+ compositor.listItemsInserted(&listA, 10, 2, &inserts);
+ QCOMPARE(compositor.count(C::Default), 11);
+ QCOMPARE(inserts.count(), 0);
+ { const int indexes[] = {/*A*/0,1,5,6,/*B*/0,1,2,3,/*A*/2,3,4};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ QCOMPARE(compositor.find(C::Default, i).modelIndex(), indexes[i]);
+ }
+ }
+
+ compositor.listItemsInserted(&listB, 10, 2, &inserts);
+ QCOMPARE(compositor.count(C::Default), 11);
+ QCOMPARE(inserts.count(), 0);
+ { const int indexes[] = {/*A*/0,1,5,6,/*B*/0,1,2,3,/*A*/2,3,4};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ QCOMPARE(compositor.find(C::Default, i).modelIndex(), indexes[i]);
+ }
+ }
+
+ compositor.listItemsInserted(&listA, 0, 2, &inserts);
+ QCOMPARE(compositor.count(C::Default), 13);
+ QCOMPARE(inserts.count(), 1);
+ QCOMPARE(inserts.at(0).index[C::Default], 0); QCOMPARE(inserts.at(0).count, 2);
+ { const int indexes[] = {/*A*/0,1,2,3,7,8,/*B*/0,1,2,3,/*A*/4,5,6};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ QCOMPARE(compositor.find(C::Default, i).modelIndex(), indexes[i]);
+ }
+ }
+
+ inserts.clear();
+ compositor.listItemsInserted(&listA, 5, 1, &inserts);
+ QCOMPARE(compositor.count(C::Default), 14);
+ QCOMPARE(inserts.count(), 1);
+ QCOMPARE(inserts.at(0).index[C::Default], 4); QCOMPARE(inserts.at(0).count, 1);
+ { const int indexes[] = {/*A*/0,1,2,3,5,8,9,/*B*/0,1,2,3,/*A*/4,6,7};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ QCOMPARE(compositor.find(C::Default, i).modelIndex(), indexes[i]);
+ }
+ }
+
+ inserts.clear();
+ compositor.listItemsInserted(&listA, 10, 2, &inserts);
+ QCOMPARE(compositor.count(C::Default), 16);
+ QCOMPARE(inserts.count(), 1);
+ QCOMPARE(inserts.at(0).index[C::Default], 7); QCOMPARE(inserts.at(0).count, 2);
+ { const int indexes[] = {/*A*/0,1,2,3,5,8,9,10,11,/*B*/0,1,2,3,/*A*/4,6,7};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ QCOMPARE(compositor.find(C::Default, i).modelIndex(), indexes[i]);
+ }
+ }
+}
+
+void tst_qdeclarativelistcompositor::listItemsRemoved()
+{
+ QDeclarativeListCompositor compositor;
+ compositor.setGroupCount(4);
+ compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
+
+ int listA;
+ int listB;
+ QVector<C::Remove> removes;
+
+ compositor.append(&listA, 0, 7, C::AppendFlag | C::PrependFlag | C::DefaultFlag);
+ compositor.append(&listB, 0, 4, C::DefaultFlag);
+ compositor.move(C::Default, 2, C::Default, 8, 3);
+
+ QCOMPARE(compositor.count(C::Default), 11);
+ { const int indexes[] = {/*A*/0,1,5,6,/*B*/0,1,2,3,/*A*/2,3,4};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ QCOMPARE(compositor.find(C::Default, i).modelIndex(), indexes[i]);
+ }
+ }
+
+ compositor.listItemsRemoved(&listA, 12, 2, &removes);
+ QCOMPARE(compositor.count(C::Default), 11);
+ QCOMPARE(removes.count(), 0);
+
+ compositor.listItemsRemoved(&listB, 12, 2, &removes);
+ QCOMPARE(compositor.count(C::Default), 11);
+ QCOMPARE(removes.count(), 0);
+
+ compositor.listItemsRemoved(&listA, 4, 3, &removes);
+ QCOMPARE(compositor.count(C::Default), 8);
+ QCOMPARE(removes.count(), 2);
+ QCOMPARE(removes.at(0).index[C::Default], 2); QCOMPARE(removes.at(0).count, 2);
+ QCOMPARE(removes.at(1).index[C::Default], 8); QCOMPARE(removes.at(1).count, 1);
+ { const int indexes[] = {/*A*/0,1,/*B*/0,1,2,3,/*A*/2,3};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ QCOMPARE(compositor.find(C::Default, i).modelIndex(), indexes[i]);
+ }
+ }
+}
+
+void tst_qdeclarativelistcompositor::listItemsMoved()
+{
+ QDeclarativeListCompositor compositor;
+ compositor.setGroupCount(4);
+ compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
+
+ int listA;
+ int listB;
+ QVector<C::Remove> removes;
+ QVector<C::Insert> inserts;
+
+ compositor.append(&listA, 0, 7, C::AppendFlag | C::PrependFlag | C::DefaultFlag);
+ { const int indexes[] = {/*A*/0,1,2,3,4,5,6};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ QCOMPARE(compositor.find(C::Default, i).modelIndex(), indexes[i]);
+ }
+ }
+
+ removes.clear();
+ inserts.clear();
+ compositor.listItemsMoved(&listA, 4, 3, 1, &removes, &inserts);
+ { const int indexes[] = {/*A*/0,1,2,3,4,5,6};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ QCOMPARE(compositor.find(C::Default, i).modelIndex(), indexes[i]);
+ }
+ const int from[] = {4, 1};
+ const int to[] = {3, 1};
+
+ QCOMPARE(removes.count(), lengthOf(from) / 2);
+ for (int i = 0; i < lengthOf(from); i += 2) {
+ QCOMPARE(removes.at(i).index[C::Default], from[i]);
+ QCOMPARE(removes.at(i).count, from[i + 1]);
+ }
+ QCOMPARE(inserts.count(), lengthOf(to) / 2);
+ for (int i = 0; i < lengthOf(to); i += 2) {
+ QCOMPARE(inserts.at(i).index[C::Default], to[i]);
+ QCOMPARE(inserts.at(i).count, to[i + 1]);
+ }
+ }
+
+ compositor.append(&listB, 0, 4, C::DefaultFlag);
+ compositor.move(C::Default, 2, C::Default, 8, 3);
+ QCOMPARE(compositor.count(C::Default), 11);
+ { const int indexes[] = {/*A*/0,1,5,6,/*B*/0,1,2,3,/*A*/2,3,4};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ QCOMPARE(compositor.find(C::Default, i).modelIndex(), indexes[i]);
+ }
+ }
+
+ removes.clear();
+ inserts.clear();
+ compositor.listItemsMoved(&listA, 4, 1, 3, &removes, &inserts);
+ { const int indexes[] = {/*A*/0,2,3,4,/*B*/0,1,2,3,/*A*/5,6,1};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ QCOMPARE(compositor.find(C::Default, i).modelIndex(), indexes[i]);
+ }
+ }
+
+ removes.clear();
+ inserts.clear();
+ compositor.listItemsMoved(&listA, 0, 6, 1, &removes, &inserts);
+ { const int indexes[] = {/*A*/1,2,3,6,/*B*/0,1,2,3,/*A*/4,5,0};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ QCOMPARE(compositor.find(C::Default, i).modelIndex(), indexes[i]);
+ }
+ }
+
+ compositor.clear();
+ compositor.append(&listA, 0, 8, C::AppendFlag | C::PrependFlag | C::DefaultFlag);
+ for (int i = 0; i < 4; ++i)
+ compositor.setFlags(C::Default, 0, 4, C::CacheFlag);
+ removes.clear();
+ inserts.clear();
+ compositor.listItemsMoved(&listA, 6, 2, 1, &removes, &inserts);
+}
+
+void tst_qdeclarativelistcompositor::listItemsChanged()
+{
+ QDeclarativeListCompositor compositor;
+ compositor.setGroupCount(4);
+ compositor.setDefaultGroups(VisibleFlag | C::DefaultFlag);
+
+ int listA;
+ int listB;
+ QVector<C::Change> changes;
+
+ compositor.append(&listA, 0, 7, C::AppendFlag | C::PrependFlag | C::DefaultFlag);
+ compositor.append(&listB, 0, 4, C::DefaultFlag);
+ compositor.move(C::Default, 2, C::Default, 8, 3);
+
+ QCOMPARE(compositor.count(C::Default), 11);
+ { const int indexes[] = {/*A*/0,1,5,6,/*B*/0,1,2,3,/*A*/2,3,4};
+ for (int i = 0; i < lengthOf(indexes); ++i) {
+ QCOMPARE(compositor.find(C::Default, i).modelIndex(), indexes[i]);
+ }
+ }
+
+ compositor.listItemsChanged(&listA, 3, 4, &changes);
+ QCOMPARE(changes.count(), 2);
+ QCOMPARE(changes.at(0).index[C::Default], 2); QCOMPARE(changes.at(0).count, 2);
+ QCOMPARE(changes.at(1).index[C::Default], 9); QCOMPARE(changes.at(0).count, 2);
+}
+
+QTEST_MAIN(tst_qdeclarativelistcompositor)
+
+#include "tst_qdeclarativelistcompositor.moc"
+
diff --git a/tests/auto/declarative/qsgvisualdatamodel/data/groups.qml b/tests/auto/declarative/qsgvisualdatamodel/data/groups.qml
new file mode 100644
index 0000000000..0189697a89
--- /dev/null
+++ b/tests/auto/declarative/qsgvisualdatamodel/data/groups.qml
@@ -0,0 +1,32 @@
+import QtQuick 2.0
+
+ListView {
+ width: 100
+ height: 100
+ model: VisualDataModel {
+ groups: [
+ VisualDataGroup { id: visibleItems; objectName: "visibleItems"; name: "visible"; includeByDefault: true },
+ VisualDataGroup { id: selectedItems; objectName: "selectedItems"; name: "selected" }
+ ]
+
+ model: myModel
+ delegate: Item {
+ id: delegate
+ objectName: "delegate"
+ width: 100
+ height: 2
+ property variant test1: name
+ property variant test2: index
+ property variant test3: VisualDataModel.itemsIndex
+ property variant test4: VisualDataModel.inItems
+ property variant test5: VisualDataModel.visibleIndex
+ property variant test6: VisualDataModel.inVisible
+ property variant test7: VisualDataModel.selectedIndex
+ property variant test8: VisualDataModel.inSelected
+ property variant test9: VisualDataModel.groups
+
+ function hide() { VisualDataModel.inVisible = false }
+ function select() { VisualDataModel.inSelected = true }
+ }
+ }
+}
diff --git a/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp b/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp
index 824b623790..e8659bcf16 100644
--- a/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp
+++ b/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp
@@ -55,6 +55,8 @@
#include <math.h>
#include <QtOpenGL/QGLShaderProgram>
+template <typename T, int N> int lengthOf(const T (&)[N]) { return N; }
+
static void initStandardTreeModel(QStandardItemModel *model)
{
QStandardItem *item;
@@ -128,8 +130,22 @@ private slots:
void noDelegate();
void qaimRowsMoved();
void qaimRowsMoved_data();
+ void remove();
+ void move();
+ void groups();
private:
+ template <int N> void groups_verify(
+ const SingleRoleModel &model,
+ QSGItem *contentItem,
+ const int (&mIndex)[N],
+ const int (&iIndex)[N],
+ const int (&vIndex)[N],
+ const int (&sIndex)[N],
+ const bool (&vMember)[N],
+ const bool (&sMember)[N]);
+
+ bool failed;
QDeclarativeEngine engine;
template<typename T>
T *findItem(QSGItem *parent, const QString &objectName, int index);
@@ -184,6 +200,25 @@ private:
QString m_color;
};
+template <typename T> static T evaluate(QObject *scope, const QString &expression)
+{
+ QDeclarativeExpression expr(qmlContext(scope), scope, expression);
+ QVariant result = expr.evaluate();
+ if (expr.hasError())
+ qWarning() << expr.error().toString();
+ return result.value<T>();
+}
+
+
+template <> void evaluate<void>(QObject *scope, const QString &expression)
+{
+ QDeclarativeExpression expr(qmlContext(scope), scope, expression);
+ expr.evaluate();
+ if (expr.hasError())
+ qWarning() << expr.error().toString();
+}
+
+
tst_qsgvisualdatamodel::tst_qsgvisualdatamodel()
{
}
@@ -622,6 +657,531 @@ void tst_qsgvisualdatamodel::qaimRowsMoved_data()
<< 10 << 1 << 5;
}
+void tst_qsgvisualdatamodel::remove()
+{
+ QSGView view;
+
+ SingleRoleModel model;
+ model.list = QStringList()
+ << "one"
+ << "two"
+ << "three"
+ << "four"
+ << "five"
+ << "six"
+ << "seven"
+ << "eight"
+ << "nine"
+ << "ten"
+ << "eleven"
+ << "twelve";
+
+ QDeclarativeContext *ctxt = view.rootContext();
+ ctxt->setContextProperty("myModel", &model);
+
+ view.setSource(QUrl::fromLocalFile(SRCDIR "/data/groups.qml"));
+
+ QSGListView *listview = qobject_cast<QSGListView*>(view.rootObject());
+ QVERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QVERIFY(contentItem != 0);
+
+ QSGVisualDataModel *visualModel = qobject_cast<QSGVisualDataModel *>(qvariant_cast<QObject *>(listview->model()));
+ QVERIFY(visualModel);
+
+ {
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ static const int mIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+
+ for (int i = 0; i < lengthOf(mIndex); ++i) {
+ QSGItem *delegate = findItem<QSGItem>(contentItem, "delegate", mIndex[i]);
+ QVERIFY(delegate);
+ QCOMPARE(delegate->property("test1").toString(), model.list.at(mIndex[i]));
+ QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
+ QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
+ }
+ } {
+ evaluate<void>(visualModel, "items.remove(2)");
+ QCOMPARE(listview->count(), 11);
+ QCOMPARE(visualModel->items()->count(), 11);
+ static const int mIndex[] = { 0, 1, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10 };
+
+ for (int i = 0; i < lengthOf(mIndex); ++i) {
+ QSGItem *delegate = findItem<QSGItem>(contentItem, "delegate", mIndex[i]);
+ QVERIFY(delegate);
+ QCOMPARE(delegate->property("test1").toString(), model.list.at(mIndex[i]));
+ QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
+ QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
+ }
+ } {
+ evaluate<void>(visualModel, "items.remove(1, 4)");
+ QCOMPARE(listview->count(), 7);
+ QCOMPARE(visualModel->items()->count(), 7);
+ static const int mIndex[] = { 0, 6, 7, 8, 9,10,11 };
+ static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6 };
+
+ for (int i = 0; i < lengthOf(mIndex); ++i) {
+ QSGItem *delegate = findItem<QSGItem>(contentItem, "delegate", mIndex[i]);
+ QVERIFY(delegate);
+ QCOMPARE(delegate->property("test1").toString(), model.list.at(mIndex[i]));
+ QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
+ QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
+ }
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: remove: index out of range");
+ evaluate<void>(visualModel, "items.remove(-8, 4)");
+ QCOMPARE(listview->count(), 7);
+ QCOMPARE(visualModel->items()->count(), 7);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: remove: index out of range");
+ evaluate<void>(visualModel, "items.remove(12, 2)");
+ QCOMPARE(listview->count(), 7);
+ QCOMPARE(visualModel->items()->count(), 7);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: remove: index out of range");
+ evaluate<void>(visualModel, "items.remove(5, 3)");
+ QCOMPARE(listview->count(), 7);
+ QCOMPARE(visualModel->items()->count(), 7);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: remove: invalid count");
+ evaluate<void>(visualModel, "items.remove(5, -2)");
+ QCOMPARE(listview->count(), 7);
+ QCOMPARE(visualModel->items()->count(), 7);
+ }
+}
+
+void tst_qsgvisualdatamodel::move()
+{
+ QSGView view;
+
+ SingleRoleModel model;
+ model.list = QStringList()
+ << "one"
+ << "two"
+ << "three"
+ << "four"
+ << "five"
+ << "six"
+ << "seven"
+ << "eight"
+ << "nine"
+ << "ten"
+ << "eleven"
+ << "twelve";
+
+ QDeclarativeContext *ctxt = view.rootContext();
+ ctxt->setContextProperty("myModel", &model);
+
+ view.setSource(QUrl::fromLocalFile(SRCDIR "/data/groups.qml"));
+
+ QSGListView *listview = qobject_cast<QSGListView*>(view.rootObject());
+ QVERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QVERIFY(contentItem != 0);
+
+ QSGVisualDataModel *visualModel = qobject_cast<QSGVisualDataModel *>(qvariant_cast<QObject *>(listview->model()));
+ QVERIFY(visualModel);
+
+ {
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ static const int mIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+
+ for (int i = 0; i < lengthOf(mIndex); ++i) {
+ QSGItem *delegate = findItem<QSGItem>(contentItem, "delegate", mIndex[i]);
+ QVERIFY(delegate);
+ QCOMPARE(delegate->property("test1").toString(), model.list.at(mIndex[i]));
+ QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
+ QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
+ }
+ } {
+ evaluate<void>(visualModel, "items.move(2, 4)");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ static const int mIndex[] = { 0, 1, 3, 4, 2, 5, 6, 7, 8, 9,10,11 };
+ static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+
+ for (int i = 0; i < lengthOf(mIndex); ++i) {
+ QSGItem *delegate = findItem<QSGItem>(contentItem, "delegate", mIndex[i]);
+ QVERIFY(delegate);
+ QCOMPARE(delegate->property("test1").toString(), model.list.at(mIndex[i]));
+ QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
+ QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
+ }
+ } {
+ evaluate<void>(visualModel, "items.move(4, 2)");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ static const int mIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+
+ for (int i = 0; i < lengthOf(mIndex); ++i) {
+ QSGItem *delegate = findItem<QSGItem>(contentItem, "delegate", mIndex[i]);
+ QVERIFY(delegate);
+ QCOMPARE(delegate->property("test1").toString(), model.list.at(mIndex[i]));
+ QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
+ QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
+ }
+ } {
+ evaluate<void>(visualModel, "items.move(8, 0, 4)");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ static const int mIndex[] = { 8, 9,10,11, 0, 1, 2, 3, 4, 5, 6, 7 };
+ static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+
+ for (int i = 0; i < lengthOf(mIndex); ++i) {
+ QSGItem *delegate = findItem<QSGItem>(contentItem, "delegate", mIndex[i]);
+ QVERIFY(delegate);
+ QCOMPARE(delegate->property("test1").toString(), model.list.at(mIndex[i]));
+ QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
+ QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
+ }
+ } {
+ evaluate<void>(visualModel, "items.move(3, 4, 5)");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ static const int mIndex[] = { 8, 9,10,4, 11, 0, 1, 2, 3, 5, 6, 7 };
+ static const int iIndex[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+
+ for (int i = 0; i < lengthOf(mIndex); ++i) {
+ QSGItem *delegate = findItem<QSGItem>(contentItem, "delegate", mIndex[i]);
+ QVERIFY(delegate);
+ QCOMPARE(delegate->property("test1").toString(), model.list.at(mIndex[i]));
+ QCOMPARE(delegate->property("test2").toInt(), mIndex[i]);
+ QCOMPARE(delegate->property("test3").toInt(), iIndex[i]);
+ }
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: move: invalid count");
+ evaluate<void>(visualModel, "items.move(5, 2, -2)");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: move: from index out of range");
+ evaluate<void>(visualModel, "items.move(-6, 2, 1)");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: move: from index out of range");
+ evaluate<void>(visualModel, "items.move(15, 2, 1)");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: move: from index out of range");
+ evaluate<void>(visualModel, "items.move(11, 1, 3)");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: move: to index out of range");
+ evaluate<void>(visualModel, "items.move(2, -5, 1)");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: move: to index out of range");
+ evaluate<void>(visualModel, "items.move(2, 14, 1)");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: move: to index out of range");
+ evaluate<void>(visualModel, "items.move(2, 11, 4)");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ }
+}
+
+
+template <int N> void tst_qsgvisualdatamodel::groups_verify(
+ const SingleRoleModel &model,
+ QSGItem *contentItem,
+ const int (&mIndex)[N],
+ const int (&iIndex)[N],
+ const int (&vIndex)[N],
+ const int (&sIndex)[N],
+ const bool (&vMember)[N],
+ const bool (&sMember)[N])
+{
+ failed = true;
+ for (int i = 0; i < N; ++i) {
+ QSGItem *delegate = findItem<QSGItem>(contentItem, "delegate", mIndex[i]);
+ QVERIFY(delegate);
+ QCOMPARE(delegate->property("test1").toString(), model.list.at(mIndex[i]));
+ QCOMPARE(delegate->property("test2").toInt() , mIndex[i]);
+ QCOMPARE(delegate->property("test3").toInt() , iIndex[i]);
+ QCOMPARE(delegate->property("test4").toBool(), true);
+ QCOMPARE(delegate->property("test5").toInt() , vIndex[i]);
+ QCOMPARE(delegate->property("test6").toBool(), vMember[i]);
+ QCOMPARE(delegate->property("test7").toInt() , sIndex[i]);
+ QCOMPARE(delegate->property("test8").toBool(), sMember[i]);
+ QCOMPARE(delegate->property("test9").toStringList().contains("items") , QBool(true));
+ QCOMPARE(delegate->property("test9").toStringList().contains("visible") , QBool(vMember[i]));
+ QCOMPARE(delegate->property("test9").toStringList().contains("selected"), QBool(sMember[i]));
+ }
+ failed = false;
+}
+
+#define VERIFY_GROUPS \
+ groups_verify(model, contentItem, mIndex, iIndex, vIndex, sIndex, vMember, sMember); \
+ QVERIFY(!failed)
+
+
+void tst_qsgvisualdatamodel::groups()
+{
+ QSGView view;
+
+ SingleRoleModel model;
+ model.list = QStringList()
+ << "one"
+ << "two"
+ << "three"
+ << "four"
+ << "five"
+ << "six"
+ << "seven"
+ << "eight"
+ << "nine"
+ << "ten"
+ << "eleven"
+ << "twelve";
+
+ QDeclarativeContext *ctxt = view.rootContext();
+ ctxt->setContextProperty("myModel", &model);
+
+ view.setSource(QUrl::fromLocalFile(SRCDIR "/data/groups.qml"));
+
+ QSGListView *listview = qobject_cast<QSGListView*>(view.rootObject());
+ QVERIFY(listview != 0);
+
+ QSGItem *contentItem = listview->contentItem();
+ QVERIFY(contentItem != 0);
+
+ QSGVisualDataModel *visualModel = qobject_cast<QSGVisualDataModel *>(qvariant_cast<QObject *>(listview->model()));
+ QVERIFY(visualModel);
+
+ QSGVisualDataGroup *visibleItems = visualModel->findChild<QSGVisualDataGroup *>("visibleItems");
+ QVERIFY(visibleItems);
+
+ QSGVisualDataGroup *selectedItems = visualModel->findChild<QSGVisualDataGroup *>("selectedItems");
+ QVERIFY(selectedItems);
+
+ const bool f = false;
+ const bool t = true;
+
+ {
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 12);
+ QCOMPARE(selectedItems->count(), 0);
+ static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int vIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const bool vMember[] = { t, t, t, t, t, t, t, t, t, t, t, t };
+ static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ static const bool sMember[] = { f, f, f, f, f, f, f, f, f, f, f, f };
+ VERIFY_GROUPS;
+ } {
+ evaluate<void>(visualModel, "items.addGroups(8, \"selected\")");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 12);
+ QCOMPARE(selectedItems->count(), 1);
+ static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int vIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const bool vMember[] = { t, t, t, t, t, t, t, t, t, t, t, t };
+ static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 };
+ static const bool sMember[] = { f, f, f, f, f, f, f, f, t, f, f, f };
+ VERIFY_GROUPS;
+ } {
+ evaluate<void>(visualModel, "items.addGroups(6, 4, [\"visible\", \"selected\"])");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 12);
+ QCOMPARE(selectedItems->count(), 4);
+ static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int vIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const bool vMember[] = { t, t, t, t, t, t, t, t, t, t, t, t };
+ static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 4 };
+ static const bool sMember[] = { f, f, f, f, f, f, t, t, t, t, f, f };
+ VERIFY_GROUPS;
+ } {
+ evaluate<void>(visualModel, "items.setGroups(2, [\"items\", \"selected\"])");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 11);
+ QCOMPARE(selectedItems->count(), 5);
+ static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9,10 };
+ static const bool vMember[] = { t, t, f, t, t, t, t, t, t, t, t, t };
+ static const int sIndex [] = { 0, 0, 0, 1, 1, 1, 1, 2, 3, 4, 5, 5 };
+ static const bool sMember[] = { f, f, t, f, f, f, t, t, t, t, f, f };
+ VERIFY_GROUPS;
+ } {
+ evaluate<void>(selectedItems, "setGroups(0, 3, \"items\")");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 9);
+ QCOMPARE(selectedItems->count(), 2);
+ static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 5, 5, 5, 6, 7, 8 };
+ static const bool vMember[] = { t, t, f, t, t, t, f, f, t, t, t, t };
+ static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2 };
+ static const bool sMember[] = { f, f, f, f, f, f, f, f, t, t, f, f };
+ VERIFY_GROUPS;
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: addGroups: invalid count");
+ evaluate<void>(visualModel, "items.addGroups(11, -4, \"items\")");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 9);
+ QCOMPARE(selectedItems->count(), 2);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: addGroups: index out of range");
+ evaluate<void>(visualModel, "items.addGroups(-1, 3, \"items\")");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 9);
+ QCOMPARE(selectedItems->count(), 2);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: addGroups: index out of range");
+ evaluate<void>(visualModel, "items.addGroups(14, 3, \"items\")");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 9);
+ QCOMPARE(selectedItems->count(), 2);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: addGroups: index out of range");
+ evaluate<void>(visualModel, "items.addGroups(11, 5, \"items\")");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 9);
+ QCOMPARE(selectedItems->count(), 2);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: setGroups: invalid count");
+ evaluate<void>(visualModel, "items.setGroups(11, -4, \"items\")");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 9);
+ QCOMPARE(selectedItems->count(), 2);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: setGroups: index out of range");
+ evaluate<void>(visualModel, "items.setGroups(-1, 3, \"items\")");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 9);
+ QCOMPARE(selectedItems->count(), 2);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: setGroups: index out of range");
+ evaluate<void>(visualModel, "items.setGroups(14, 3, \"items\")");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 9);
+ QCOMPARE(selectedItems->count(), 2);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: setGroups: index out of range");
+ evaluate<void>(visualModel, "items.setGroups(11, 5, \"items\")");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 9);
+ QCOMPARE(selectedItems->count(), 2);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: removeGroups: invalid count");
+ evaluate<void>(visualModel, "items.removeGroups(11, -4, \"items\")");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: removeGroups: index out of range");
+ evaluate<void>(visualModel, "items.removeGroups(-1, 3, \"items\")");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 9);
+ QCOMPARE(selectedItems->count(), 2);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: removeGroups: index out of range");
+ evaluate<void>(visualModel, "items.removeGroups(14, 3, \"items\")");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 9);
+ QCOMPARE(selectedItems->count(), 2);
+ } {
+ QTest::ignoreMessage(QtWarningMsg, "<Unknown File>: QML VisualDataGroup: removeGroups: index out of range");
+ evaluate<void>(visualModel, "items.removeGroups(11, 5, \"items\")");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 9);
+ QCOMPARE(selectedItems->count(), 2);
+ } {
+ evaluate<void>(visualModel, "filterOnGroup = \"visible\"");
+ QCOMPARE(listview->count(), 9);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 9);
+ QCOMPARE(selectedItems->count(), 2);
+ } {
+ evaluate<void>(visualModel, "filterOnGroup = \"selected\"");
+ QCOMPARE(listview->count(), 2);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 9);
+ QCOMPARE(selectedItems->count(), 2);
+ } {
+ evaluate<void>(visualModel, "filterOnGroup = \"items\"");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 9);
+ QCOMPARE(selectedItems->count(), 2);
+ } {
+ QSGItem *delegate = findItem<QSGItem>(contentItem, "delegate", 5);
+ QVERIFY(delegate);
+
+ evaluate<void>(delegate, "hide()");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 8);
+ QCOMPARE(selectedItems->count(), 2);
+ static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 4, 4, 4, 5, 6, 7 };
+ static const bool vMember[] = { t, t, f, t, t, f, f, f, t, t, t, t };
+ static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2 };
+ static const bool sMember[] = { f, f, f, f, f, f, f, f, t, t, f, f };
+ VERIFY_GROUPS;
+ } {
+ QSGItem *delegate = findItem<QSGItem>(contentItem, "delegate", 5);
+ QVERIFY(delegate);
+
+ evaluate<void>(delegate, "select()");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 8);
+ QCOMPARE(selectedItems->count(), 3);
+ static const int mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int vIndex [] = { 0, 1, 2, 2, 3, 4, 4, 4, 4, 5, 6, 7 };
+ static const bool vMember[] = { t, t, f, t, t, f, f, f, t, t, t, t };
+ static const int sIndex [] = { 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 3, 3 };
+ static const bool sMember[] = { f, f, f, f, f, t, f, f, t, t, f, f };
+ VERIFY_GROUPS;
+ } {
+ evaluate<void>(visualModel, "items.move(2, 6, 3)");
+ QCOMPARE(listview->count(), 12);
+ QCOMPARE(visualModel->items()->count(), 12);
+ QCOMPARE(visibleItems->count(), 8);
+ QCOMPARE(selectedItems->count(), 3);
+ static const int mIndex [] = { 0, 1, 5, 6, 7, 8, 2, 3, 4, 9,10,11 };
+ static const int iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+ static const int vIndex [] = { 0, 1, 2, 2, 2, 2, 3, 3, 4, 5, 6, 7 };
+ static const bool vMember[] = { t, t, f, f, f, t, f, t, t, t, t, t };
+ static const int sIndex [] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3 };
+ static const bool sMember[] = { f, f, t, f, f, t, f, f, f, t, f, f };
+ VERIFY_GROUPS;
+ }
+}
template<typename T>
T *tst_qsgvisualdatamodel::findItem(QSGItem *parent, const QString &objectName, int index)