Add support for filtering VisualDataModels.
authorAndrew den Exter <andrew.den-exter@nokia.com>
Thu, 1 Sep 2011 08:06:11 +0000 (18:06 +1000)
committerQt by Nokia <qt-info@nokia.com>
Thu, 6 Oct 2011 03:29:00 +0000 (05:29 +0200)
Add a VisualDataGroup element which items within a VisualDataModel
can be assigned to. Setting the group property of a VisualDataModel
or one of its parts models will filter the items visible within a
view to just items belonging to that group.  By default all items
belong to an 'items' group.

The VisualDataModel attached object includes properties indicating
whether a item is a member of a group and its index in the group.

Task-number: QTBUG-21513 QTBUG-21515
Change-Id: If3df6a359a888a6f79923775d2f78076d5e7d2cf
Reviewed-on: http://codereview.qt-project.org/4115
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Martin Jones <martin.jones@nokia.com>
15 files changed:
doc/src/snippets/declarative/visualdatagroup.qml [new file with mode: 0644]
examples/declarative/modelviews/visualdatamodel/dragselection.qml [new file with mode: 0644]
src/declarative/items/qsgitemsmodule.cpp
src/declarative/items/qsgvisualdatamodel.cpp
src/declarative/items/qsgvisualdatamodel_p.h
src/declarative/util/qdeclarativechangeset.cpp
src/declarative/util/qdeclarativechangeset_p.h
src/declarative/util/qdeclarativelistcompositor.cpp [new file with mode: 0644]
src/declarative/util/qdeclarativelistcompositor_p.h [new file with mode: 0644]
src/declarative/util/util.pri
tests/auto/declarative/declarative.pro
tests/auto/declarative/qdeclarativelistcompositor/qdeclarativelistcompositor.pro [new file with mode: 0644]
tests/auto/declarative/qdeclarativelistcompositor/tst_qdeclarativelistcompositor.cpp [new file with mode: 0644]
tests/auto/declarative/qsgvisualdatamodel/data/groups.qml [new file with mode: 0644]
tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp

diff --git a/doc/src/snippets/declarative/visualdatagroup.qml b/doc/src/snippets/declarative/visualdatagroup.qml
new file mode 100644 (file)
index 0000000..feb41e2
--- /dev/null
@@ -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 (file)
index 0000000..d4412f9
--- /dev/null
@@ -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
+    }
+}
index 41823c1..db98dc4 100644 (file)
@@ -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>();
index 09f5d5d..72cebd7 100644 (file)
 #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>
 
 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 QSGVisualDataModel::_q_itemsChanged(int index, int count)
-{
-    Q_D(QSGVisualDataModel);
-    if (!d->m_delegate)
-        return;
-    d->m_absoluteChangeSet.change(index, count);
-    d->emitChanges();
-}
-
-void QSGVisualDataModel::_q_itemsInserted(int index, int count)
+void QSGVisualDataModelPrivate::itemsChanged(const QVector<Compositor::Change> &changes)
 {
-    Q_D(QSGVisualDataModel);
-    if (!d->m_delegate)
+    if (!m_delegate)
         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) {
-            QSGVisualDataModelPrivate::ObjectRef objRef = *iter;
-            int index = iter.key() + count;
-            iter = d->m_cache.erase(iter);
+    QVarLengthArray<QVector<QDeclarativeChangeSet::Change>, Compositor::MaximumGroupCount> translatedChanges(m_groupCount);
 
-            items.insert(index, objRef);
-        } else {
-            ++iter;
+    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));
+            }
         }
     }
-    d->m_cache.unite(items);
 
-    d->m_absoluteChangeSet.insert(index, count);
-    d->emitChanges();
-    emit countChanged();
+    for (int i = 1; i < m_groupCount; ++i)
+        QSGVisualDataGroupPrivate::get(m_groups[i])->changeSet.apply(translatedChanges.at(i));
 }
 
-void QSGVisualDataModel::_q_itemsRemoved(int index, int count)
+void QSGVisualDataModel::_q_itemsChanged(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;
-        }
+    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];
     }
-    d->m_cache.unite(items);
+}
 
-    d->m_absoluteChangeSet.remove(index, count);
-    d->emitChanges();
-    emit countChanged();
+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_itemsMoved(int from, int to, int count)
+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(); ) {
-        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;
+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 (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);
+        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 (!remove.inCache())
+            continue;
+
+        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.move(from, to, 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] -= 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 (count <= 0)
+        return;
+
+    QVector<Compositor::Remove> removes;
+    d->m_compositor.listItemsRemoved(d->m_adaptorModel, index, count, &removes);
+    d->itemsRemoved(removes);
     d->emitChanges();
 }
 
-void QSGVisualDataModelPrivate::emitChanges()
+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 (count <= 0)
+        return;
+
+    QVector<Compositor::Remove> removes;
+    QVector<Compositor::Insert> inserts;
+    d->m_compositor.listItemsMoved(d->m_adaptorModel, from, to, count, &removes, &inserts);
+    d->itemsMoved(removes, inserts);
+    d->emitChanges();
+}
+
+template <typename T> v8::Local<v8::Array>
+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>
index d3f1d18..94640a6 100644 (file)
@@ -1,3 +1,4 @@
+// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350
 /****************************************************************************
 **
 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
 #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
 
index 3375706..81cbe3e 100644 (file)
@@ -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)
index 67533b0..b7554da 100644 (file)
@@ -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 (file)
index 0000000..8fd4ec4
--- /dev/null
@@ -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 (file)
index 0000000..cd2d9b3
--- /dev/null
@@ -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
index ddd7026..ae67a86 100644 (file)
@@ -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
index f02bc51..3248801 100644 (file)
@@ -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 (file)
index 0000000..e3988b3
--- /dev/null
@@ -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 (file)
index 0000000..030d2e2
--- /dev/null
@@ -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 (file)
index 0000000..0189697
--- /dev/null
@@ -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 }
+        }
+    }
+}
index 824b623..e8659bc 100644 (file)
@@ -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)