From: Andrew den Exter Date: Thu, 1 Sep 2011 08:06:11 +0000 (+1000) Subject: Add support for filtering VisualDataModels. X-Git-Url: http://git.silmor.de/gitweb/?a=commitdiff_plain;h=6bd1704c42f564980677682e1d47e91129d94e5c;p=konrad%2Fqtdeclarative.git Add support for filtering VisualDataModels. 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 Reviewed-by: Martin Jones --- diff --git a/doc/src/snippets/declarative/visualdatagroup.qml b/doc/src/snippets/declarative/visualdatagroup.qml new file mode 100644 index 0000000..feb41e2 --- /dev/null +++ b/doc/src/snippets/declarative/visualdatagroup.qml @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ +//![0] +import QtQuick 2.0 + +Rectangle { + width: 200; height: 100 + + VisualDataModel { + id: visualModel + model: ListModel { + ListElement { name: "Apple" } + ListElement { name: "Orange" } + } + + groups: [ + VisualDataGroup { name: "selected" } + ] + + delegate: Rectangle { + id: item + height: 25 + width: 200 + Text { + text: { + var text = "Name: " + name + if (item.VisualDataModel.inSelected) + text += " (" + item.VisualDataModel.selectedIndex + ")" + return text; + } + } + MouseArea { + anchors.fill: parent + onClicked: item.VisualDataModel.inSelected = !item.VisualDataModel.inSelected + } + } + } + + ListView { + anchors.fill: parent + model: visualModel + } +} +//![0] diff --git a/examples/declarative/modelviews/visualdatamodel/dragselection.qml b/examples/declarative/modelviews/visualdatamodel/dragselection.qml new file mode 100644 index 0000000..d4412f9 --- /dev/null +++ b/examples/declarative/modelviews/visualdatamodel/dragselection.qml @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + id: root + + width: 320 + height: 480 + + property bool dragging: false + + Component { + id: packageDelegate + Package { + id: packageRoot + + MouseArea { + id: visibleContainer + Package.name: "visible" + + width: 64 + height: 64 + enabled: packageRoot.VisualDataModel.inSelected + + drag.target: draggable + + Item { + id: draggable + + width: 64 + height: 64 + + anchors { horizontalCenter: parent.horizontalCenter; verticalCenter: parent.verticalCenter } + + states: State { + when: visibleContainer.drag.active + AnchorChanges { target: draggable; anchors { horizontalCenter: undefined; verticalCenter: undefined} } + ParentChange { target: selectionView; parent: draggable; x: 0; y: 0 } + PropertyChanges { target: root; dragging: true } + ParentChange { target: draggable; parent: root } + } + } + DragTarget { + anchors.fill: parent + onEntered: visualModel.items.move(selectedItems, 0, packageRoot.VisualDataModel.itemsIndex, selectedItems.count) + } + } + Item { + id: selectionContainer + Package.name: "selection" + + width: 64 + height: 64 + + visible: PathView.onPath + } + Rectangle { + id: content + parent: visibleContainer + + width: 58 + height: 58 + + radius: 8 + + gradient: Gradient { + GradientStop { id: gradientStart; position: 0.0; color: "#8AC953" } + GradientStop { id: gradientEnd; position: 1.0; color: "#8BC953" } + } + + border.width: 2 + border.color: "#007423" + + state: root.dragging && packageRoot.VisualDataModel.inSelected ? "selected" : "visible" + + Text { + anchors.fill: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + color: "white" + text: modelData + font.pixelSize: 18 + } + + Rectangle { + anchors { right: parent.right; top: parent.top; margins: 3 } + width: 12; height: 12 + color: packageRoot.VisualDataModel.inSelected ? "black" : "white" + radius: 6 + + border.color: "white" + border.width: 2 + + MouseArea { + anchors.fill: parent + onClicked: packageRoot.VisualDataModel.inSelected = !packageRoot.VisualDataModel.inSelected + } + } + + states: [ + State { + name: "selected" + ParentChange { target: content; parent: selectionContainer; x: 3; y: 3 } + PropertyChanges { target: packageRoot; VisualDataModel.inItems: visibleContainer.drag.active } + PropertyChanges { target: gradientStart; color: "#017423" } + PropertyChanges { target: gradientStart; color: "#007423" } + }, State { + name: "visible" + PropertyChanges { target: packageRoot; VisualDataModel.inItems: true } + ParentChange { target: content; parent: visibleContainer; x: 3; y: 3 } + PropertyChanges { target: gradientStart; color: "#8AC953" } + PropertyChanges { target: gradientStart; color: "#8BC953" } + } + ] + transitions: Transition { + PropertyAction { target: packageRoot; properties: "VisualDataModel.inItems" } + ParentAnimation { + target: content + NumberAnimation { target: content; properties: "x,y"; duration: 500 } + } + ColorAnimation { targets: [gradientStart, gradientEnd]; duration: 500 } + } + } + } + } + + VisualDataModel { + id: visualModel + model: 35 + delegate: packageDelegate + + groups: VisualDataGroup { id: selectedItems; name: "selected" } + + Component.onCompleted: parts.selection.filterOnGroup = "selected" + } + + PathView { + id: selectionView + + height: 64 + width: 64 + + model: visualModel.parts.selection + + path: Path { + startX: 0 + startY: 0 + PathLine { x: 64; y: 64 } + } + } + + GridView { + id: itemsView + anchors { fill: parent } + cellWidth: 64 + cellHeight: 64 + model: visualModel.parts.visible + } +} diff --git a/src/declarative/items/qsgitemsmodule.cpp b/src/declarative/items/qsgitemsmodule.cpp index 41823c1..db98dc4 100644 --- a/src/declarative/items/qsgitemsmodule.cpp +++ b/src/declarative/items/qsgitemsmodule.cpp @@ -149,6 +149,7 @@ static void qt_sgitems_defineModule(const char *uri, int major, int minor) qmlRegisterType(uri,major,minor,"TextInput"); qmlRegisterType(uri,major,minor,"ViewSection"); qmlRegisterType(uri,major,minor,"VisualDataModel"); + qmlRegisterType(uri,major,minor,"VisualDataGroup"); qmlRegisterType(uri,major,minor,"VisualItemModel"); qmlRegisterType(); diff --git a/src/declarative/items/qsgvisualdatamodel.cpp b/src/declarative/items/qsgvisualdatamodel.cpp index 09f5d5d..72cebd7 100644 --- a/src/declarative/items/qsgvisualdatamodel.cpp +++ b/src/declarative/items/qsgvisualdatamodel.cpp @@ -58,8 +58,10 @@ #include #include #include -#include #include +#include +#include +#include #include #include @@ -67,10 +69,56 @@ QT_BEGIN_NAMESPACE +typedef QDeclarativeListCompositor Compositor; + +class QSGVisualDataGroupEmitter +{ +public: + virtual void emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset) = 0; + virtual void createdPackage(int, QDeclarativePackage *) {} + virtual void destroyingPackage(QDeclarativePackage *) {} + + QIntrusiveListNode emitterNode; +}; + +typedef QIntrusiveList QSGVisualDataGroupEmitterList; + +//--------------------------------------------------------------------------- + +class QSGVisualDataGroupPrivate : public QObjectPrivate +{ +public: + Q_DECLARE_PUBLIC(QSGVisualDataGroup) + + QSGVisualDataGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {} + + static QSGVisualDataGroupPrivate *get(QSGVisualDataGroup *group) { + return static_cast(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 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 &inserts, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, + QHash > *movedItems = 0); + void itemsInserted(const QVector &inserts); + void itemsRemoved( + const QVector &removes, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, + QHash > *movedItems = 0); + void itemsRemoved(const QVector &removes); + void itemsMoved( + const QVector &removes, const QVector &inserts); + void itemsChanged(const QVector &changes); + template static v8::Local buildChangeList(const QVector &changes); void emitChanges(); + void emitModelUpdated(const QDeclarativeChangeSet &changeSet, bool reset); + + + static void group_append(QDeclarativeListProperty *property, QSGVisualDataGroup *group); + static int group_count(QDeclarativeListProperty *property); + static QSGVisualDataGroup *group_at(QDeclarativeListProperty *property, int index); QSGVisualAdaptorModel *m_adaptorModel; QDeclarativeComponent *m_delegate; + QSGVisualDataModelCacheMetaType *m_cacheMetaType; QDeclarativeGuard m_context; - struct ObjectRef { - ObjectRef(QObject *object=0) : obj(object), ref(1) {} - QObject *obj; - int ref; - }; - class Cache : public QHash { - public: - QObject *getItem(int index) { - QObject *item = 0; - QHash::iterator it = find(index); - if (it != end()) { - (*it).ref++; - item = (*it).obj; - } - return item; - } - QObject *item(int index) { - QObject *item = 0; - QHash::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::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 m_packaged; - + QList 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 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 m_packaged; QString m_part; + QString m_filterGroup; QList 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 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(object()); QSGVisualPartsModel *m = new QSGVisualPartsModel( - static_cast(object())->model, - QString::fromUtf8(name(id)), - object()); + parts->model, QString::fromUtf8(name(id)), parts); + parts->models.append(m); return QVariant::fromValue(static_cast(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 &groupNames) const; + + QDeclarativeGuard 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 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 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(d->m_pendingParts.first())->updateFilterGroup(); + + d->connectModel(d->m_adaptorModel); + QVector 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(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(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 *property, QSGVisualDataGroup *group) +{ + QSGVisualDataModelPrivate *d = static_cast(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 *property) +{ + QSGVisualDataModelPrivate *d = static_cast(property->data); + return d->m_groupCount - 1; +} + +QSGVisualDataGroup *QSGVisualDataModelPrivate::group_at( + QDeclarativeListProperty *property, int index) +{ + QSGVisualDataModelPrivate *d = static_cast(property->data); + return index >= 0 && index < d->m_groupCount - 1 + ? d->m_groups[index - 1] + : 0; +} + +/*! + \qmlproperty list 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 QSGVisualDataModel::groups() +{ + Q_D(QSGVisualDataModel); + return QDeclarativeListProperty( + 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 removes; + QVector 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(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(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(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(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()) { + 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 roles) @@ -610,6 +1035,36 @@ void QSGVisualDataModel::setWatchedRoles(QList roles) d->watchedRoles = roles; } +void QSGVisualDataModelPrivate::addGroups(Compositor::Group group, int index, int count, int groupFlags) +{ + QVector 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 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 inserts; + m_compositor.setFlags(group, index, count, groupFlags, &inserts); + itemsInserted(inserts); + + const int removeFlags = ~groupFlags & Compositor::GroupMask; + QVector 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 &changes) { - Q_D(QSGVisualDataModel); - if (!d->m_delegate) + if (!m_delegate) return; - // XXX - highly inefficient - QHash items; - for (QHash::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, 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 items; - for (QHash::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 changes; + d->m_compositor.listItemsChanged(d->m_adaptorModel, index, count, &changes); + d->itemsChanged(changes); + d->emitChanges(); +} + +void QSGVisualDataModelPrivate::itemsInserted( + const QVector &inserts, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedInserts, + QHash > *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 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 &inserts) +{ + QVarLengthArray, 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 inserts; + d->m_compositor.listItemsInserted(d->m_adaptorModel, index, count, &inserts); + d->itemsInserted(inserts); + d->emitChanges(); +} - // XXX - highly inefficient - QHash items; - for (QHash::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 &removes, + QVarLengthArray, Compositor::MaximumGroupCount> *translatedRemoves, + QHash > *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::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::iterator begin = m_cache.begin() + remove.cacheIndex; + QList::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 &removes) +{ + QVarLengthArray, 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 removes; + d->m_compositor.listItemsRemoved(d->m_adaptorModel, index, count, &removes); + d->itemsRemoved(removes); d->emitChanges(); } -void QSGVisualDataModelPrivate::emitChanges() +void QSGVisualDataModelPrivate::itemsMoved( + const QVector &removes, const QVector &inserts) +{ + QHash > movedItems; + + QVarLengthArray, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); + itemsRemoved(removes, &translatedRemoves, &movedItems); + + QVarLengthArray, 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 removes; + QVector inserts; + d->m_compositor.listItemsMoved(d->m_adaptorModel, from, to, count, &removes, &inserts); + d->itemsMoved(removes, inserts); + d->emitChanges(); +} + +template v8::Local +QSGVisualDataModelPrivate::buildChangeList(const QVector &changes) +{ + v8::Local indexes = v8::Array::New(changes.count()); + v8::Local indexKey = v8::String::New("index"); + v8::Local countKey = v8::String::New("count"); + v8::Local moveIdKey = v8::String::New("moveId"); + + for (int i = 0; i < changes.count(); ++i) { + v8::Local 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 removes; + QVector 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 &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 array = v8::Local::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(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(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(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(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 removed = QSGVisualDataModelPrivate::buildChangeList(changeSet.removes()); + v8::Local 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 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 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 v = (*args)[i]; + if (QSGVisualDataGroup *group = qobject_cast(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(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 removes; + QVector 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 removes; + QVector 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(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 roles) @@ -840,11 +2170,15 @@ void QSGVisualPartsModel::setWatchedRoles(QList roles) int QSGVisualPartsModel::indexOf(QSGItem *item, QObject *) const { - const QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(m_model); QHash::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 diff --git a/src/declarative/items/qsgvisualdatamodel_p.h b/src/declarative/items/qsgvisualdatamodel_p.h index d3f1d18..94640a6 100644 --- a/src/declarative/items/qsgvisualdatamodel_p.h +++ b/src/declarative/items/qsgvisualdatamodel_p.h @@ -1,3 +1,4 @@ +// Commit: ac5c099cc3c5b8c7eec7a49fdeb8a21037230350 /**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). @@ -42,8 +43,12 @@ #ifndef QSGVISUALDATAMODEL_P_H #define QSGVISUALDATAMODEL_P_H +#include #include + + #include +#include 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 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 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 attachedProperties; + + friend class QSGVisualDataModelAttachedMetaObject; +}; + QT_END_NAMESPACE QML_DECLARE_TYPE(QSGVisualDataModel) +QML_DECLARE_TYPEINFO(QSGVisualDataModel, QML_HAS_ATTACHED_PROPERTIES) +QML_DECLARE_TYPE(QSGVisualDataGroup) QT_END_HEADER diff --git a/src/declarative/util/qdeclarativechangeset.cpp b/src/declarative/util/qdeclarativechangeset.cpp index 3375706..81cbe3e 100644 --- a/src/declarative/util/qdeclarativechangeset.cpp +++ b/src/declarative/util/qdeclarativechangeset.cpp @@ -45,6 +45,7 @@ QT_BEGIN_NAMESPACE QDeclarativeChangeSet::QDeclarativeChangeSet() : m_moveCounter(0) + , m_difference(0) { } @@ -53,6 +54,7 @@ QDeclarativeChangeSet::QDeclarativeChangeSet(const QDeclarativeChangeSet &change , m_inserts(changeSet.m_inserts) , m_changes(changeSet.m_changes) , m_moveCounter(changeSet.m_moveCounter) + , m_difference(0) { } @@ -66,6 +68,7 @@ QDeclarativeChangeSet &QDeclarativeChangeSet::operator =(const QDeclarativeChang m_inserts = changeSet.m_inserts; m_changes = changeSet.m_changes; m_moveCounter = changeSet.m_moveCounter; + m_difference = changeSet.m_difference; return *this; } @@ -330,6 +333,7 @@ void QDeclarativeChangeSet::applyRemovals(QVector &removals, QVectorindex -= removeCount; + m_difference -= removeCount; } void QDeclarativeChangeSet::applyInsertions(QVector &insertions) @@ -354,6 +358,7 @@ void QDeclarativeChangeSet::applyInsertions(QVector &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 &insertions) change->index += insertCount; for (; insert != m_inserts.end(); ++insert) insert->index += insertCount; + m_difference += insertCount; } void QDeclarativeChangeSet::applyChanges(QVector &changes) diff --git a/src/declarative/util/qdeclarativechangeset_p.h b/src/declarative/util/qdeclarativechangeset_p.h index 67533b0..b7554da 100644 --- a/src/declarative/util/qdeclarativechangeset_p.h +++ b/src/declarative/util/qdeclarativechangeset_p.h @@ -103,10 +103,6 @@ public: }; QDeclarativeChangeSet(); - QDeclarativeChangeSet( - const QVector &removals, - const QVector &insertions, - const QVector &changes = QVector()); 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 &removals, QVector &insertions); void applyInsertions(QVector &insertions); @@ -149,6 +149,7 @@ private: QVector m_inserts; QVector m_changes; int m_moveCounter; + int m_difference; }; inline uint qHash(const QDeclarativeChangeSet::MoveKey &key) { return qHash(qMakePair(key.moveId, key.offset)); } diff --git a/src/declarative/util/qdeclarativelistcompositor.cpp b/src/declarative/util/qdeclarativelistcompositor.cpp new file mode 100644 index 0000000..8fd4ec4 --- /dev/null +++ b/src/declarative/util/qdeclarativelistcompositor.cpp @@ -0,0 +1,1198 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativelistcompositor_p.h" + +#include + +//#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(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 *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 *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 *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 *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 *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 *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 *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 *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 *removes, + QVector *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 *translatedInsertions, + void *list, + const QVector &insertions, + const QVector *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::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 *translatedInsertions) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << index << count) + Q_ASSERT(count > 0); + + QVector insertions; + insertions.append(QDeclarativeChangeSet::Insert(index, count)); + + listItemsInserted(translatedInsertions, list, insertions); +} + +void QDeclarativeListCompositor::listItemsRemoved( + QVector *translatedRemovals, + void *list, + QVector *removals, + QVector *insertions, + QVector *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::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::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 *translatedRemovals) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << index << count) + Q_ASSERT(count >= 0); + + QVector 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 *translatedRemovals, + QVector *translatedInsertions) +{ + + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << from << to << count) + Q_ASSERT(count >= 0); + + QVector removals; + QVector insertions; + QVector 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 *translatedChanges, + void *list, + const QVector &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 *translatedChanges) +{ + QT_DECLARATIVE_TRACE_LISTCOMPOSITOR(<< list << index << count) + Q_ASSERT(count >= 0); + QVector changes; + changes.append(QDeclarativeChangeSet::Change(index, count)); + listItemsChanged(translatedChanges, list, changes); +} + +void QDeclarativeListCompositor::listChanged( + void *list, + const QDeclarativeChangeSet &changeSet, + QVector *translatedRemovals, + QVector *translatedInsertions, + QVector *translatedChanges) +{ + QVector removals = changeSet.removes(); + QVector insertions = changeSet.inserts(); + QVector 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 *removes, + QVector *inserts) +{ + int removeCount = 0; + for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { + if (it == from && it != to) { + removes->append(QDeclarativeChangeSet::Remove(it.index[from]- removeCount, it->count)); + removeCount += it->count; + } else if (it != from && it == to) { + inserts->append(QDeclarativeChangeSet::Insert(it.index[to], it->count)); + } + it.incrementIndexes(it->count); + } +} + +QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Group &group) +{ + switch (group) { + case QDeclarativeListCompositor::Cache: return debug << "Cache"; + case QDeclarativeListCompositor::Default: return debug << "Default"; + default: return (debug.nospace() << "Group" << int(group)).space(); + } + +} + +QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Range &range) +{ + (debug.nospace() + << "Range(" + << range.list) << " " + << range.index << " " + << range.count << " " + << (range.append() ? "A" : "0") + << (range.prepend() ? "P" : "0"); + for (int i = QDeclarativeListCompositor::MaximumGroupCount - 1; i >= 2; --i) + debug << (range.inGroup(i) ? "1" : "0"); + return (debug + << (range.inGroup(QDeclarativeListCompositor::Default) ? "D" : "0") + << (range.inGroup(QDeclarativeListCompositor::Cache) ? "C" : "0")); +} + +static void qt_print_indexes(QDebug &debug, int count, const int *indexes) +{ + for (int i = count - 1; i >= 0; --i) + debug << indexes[i]; +} + +QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::iterator &it) +{ + (debug.nospace() << "iterator(" << it.group).space() << "offset:" << it.offset; + qt_print_indexes(debug, it.groupCount, it.index); + return ((debug << **it).nospace() << ")").space(); +} + +static QDebug qt_print_change(QDebug debug, const char *name, const QDeclarativeListCompositor::Change &change) +{ + debug.nospace() << name << "(" << change.moveId << " " << change.count << " "; + for (int i = QDeclarativeListCompositor::MaximumGroupCount - 1; i >= 2; --i) + debug << (change.inGroup(i) ? "1" : "0"); + debug << (change.inGroup(QDeclarativeListCompositor::Default) ? "D" : "0") + << (change.inGroup(QDeclarativeListCompositor::Cache) ? "C" : "0"); + int i = QDeclarativeListCompositor::MaximumGroupCount - 1; + for (; i >= 0 && !change.inGroup(i); --i) {} + for (; i >= 0; --i) + debug << " " << change.index[i]; + return (debug << ")").maybeSpace(); +} + +QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Change &change) +{ + return qt_print_change(debug, "Change", change); +} + +QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Remove &remove) +{ + return qt_print_change(debug, "Remove", remove); +} + +QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Insert &insert) +{ + return qt_print_change(debug, "Insert", insert); +} + +QDebug operator <<(QDebug debug, const QDeclarativeListCompositor &list) +{ + int indexes[QDeclarativeListCompositor::MaximumGroupCount]; + for (int i = 0; i < QDeclarativeListCompositor::MaximumGroupCount; ++i) + indexes[i] = 0; + debug.nospace() << "QDeclarativeListCompositor("; + qt_print_indexes(debug, list.m_groupCount, list.m_end.index); + for (QDeclarativeListCompositor::Range *range = list.m_ranges.next; range != &list.m_ranges; range = range->next) { + (debug << "\n").space(); + qt_print_indexes(debug, list.m_groupCount, indexes); + debug << " " << *range; + + for (int i = 0; i < list.m_groupCount; ++i) { + if (range->inGroup(i)) + indexes[i] += range->count; + } + } + return (debug << ")").maybeSpace(); +} diff --git a/src/declarative/util/qdeclarativelistcompositor_p.h b/src/declarative/util/qdeclarativelistcompositor_p.h new file mode 100644 index 0000000..cd2d9b3 --- /dev/null +++ b/src/declarative/util/qdeclarativelistcompositor_p.h @@ -0,0 +1,370 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELISTCOMPOSITOR_P_H +#define QDECLARATIVELISTCOMPOSITOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include + +#include + +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 T *list() const { return static_cast(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 *inserts = 0); + void insert(Group group, int before, void *list, int index, int count, int flags, QVector *inserts = 0); + iterator insert(iterator before, void *list, int index, int count, int flags, QVector *inserts = 0); + + void setFlags(Group group, int index, int count, int flags, QVector *inserts = 0); + void setFlags(iterator from, int count, int flags, QVector *inserts = 0); + + void clearFlags(Group group, int index, int count, int flags, QVector *removals = 0); + void clearFlags(iterator from, int count, int flags, QVector *removals = 0); + + void removeList(void *list, QVector *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 *removals = 0, + QVector *inserts = 0); + void clear(); + + void listItemsInserted(void *list, int index, int count, QVector *inserts); + void listItemsRemoved(void *list, int index, int count, QVector *removals); + void listItemsMoved(void *list, int from, int to, int count, QVector *removals, QVector *inserts); + void listItemsChanged(void *list, int index, int count, QVector *changes); + void listChanged( + void *list, + const QDeclarativeChangeSet &changeSet, + QVector *removals, + QVector *inserts, + QVector *changes); + + void transition( + Group from, + Group to, + QVector *removes, + QVector *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 *translatedRemovals, + void *list, + QVector *removals, + QVector *insertions = 0, + QVector *movedFlags = 0, + int moveId = 0); + void listItemsInserted( + QVector *translatedInsertions, + void *list, + const QVector &insertions, + const QVector *movedFlags = 0); + void listItemsChanged( + QVector *translatedChanges, + void *list, + const QVector &changes); + + friend Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor &list); +}; + +inline QDeclarativeListCompositor::iterator::iterator() + : range(0), offset(0), group(Default), groupCount(0) {} +inline QDeclarativeListCompositor::iterator::iterator(const iterator &it) + : range(it.range) + , offset(it.offset) + , group(it.group) + , groupFlag(it.groupFlag) + , groupCount(it.groupCount) +{ + for (int i = 0; i < groupCount; ++i) + index[i] = it.index[i]; +} + +inline QDeclarativeListCompositor::iterator::iterator( + Range *range, int offset, Group group, int groupCount) + : range(range) + , offset(offset) + , group(group) + , groupFlag(1 << group) + , groupCount(groupCount) +{ + for (int i = 0; i < groupCount; ++i) + index[i] = 0; +} + +inline void QDeclarativeListCompositor::iterator::incrementIndexes(int difference, int flags) +{ + for (int i = 0; i < groupCount; ++i) { + if (flags & (1 << i)) + index[i] += difference; + } +} + +inline void QDeclarativeListCompositor::iterator::decrementIndexes(int difference, int flags) +{ + for (int i = 0; i < groupCount; ++i) { + if (flags & (1 << i)) + index[i] -= difference; + } +} + +inline QDeclarativeListCompositor::insert_iterator::insert_iterator( + Range *range, int offset, Group group, int groupCount) + : iterator(range, offset, group, groupCount) {} + +inline QDeclarativeListCompositor::Change::Change(iterator it, int count, int flags, int moveId) + : count(count), flags(flags), moveId(moveId) +{ + for (int i = 0; i < MaximumGroupCount; ++i) + index[i] = it.index[i]; +} + +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Group &group); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Range &range); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::iterator &it); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Change &change); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Remove &remove); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor::Insert &insert); +Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QDeclarativeListCompositor &list); + +QT_END_NAMESPACE + +#endif diff --git a/src/declarative/util/util.pri b/src/declarative/util/util.pri index ddd7026..ae67a86 100644 --- a/src/declarative/util/util.pri +++ b/src/declarative/util/util.pri @@ -29,6 +29,7 @@ SOURCES += \ $$PWD/qdeclarativelistmodelworkeragent.cpp \ $$PWD/qdeclarativepath.cpp \ $$PWD/qdeclarativechangeset.cpp \ + $$PWD/qdeclarativelistcompositor.cpp \ $$PWD/qlistmodelinterface.cpp \ $$PWD/qdeclarativepathinterpolator.cpp \ $$PWD/qdeclarativesvgparser.cpp @@ -67,6 +68,7 @@ HEADERS += \ $$PWD/qdeclarativepath_p.h \ $$PWD/qdeclarativepath_p_p.h \ $$PWD/qdeclarativechangeset_p.h \ + $$PWD/qdeclarativelistcompositor_p.h \ $$PWD/qlistmodelinterface_p.h \ $$PWD/qdeclarativepathinterpolator_p.h \ $$PWD/qdeclarativesvgparser_p.h diff --git a/tests/auto/declarative/declarative.pro b/tests/auto/declarative/declarative.pro index f02bc51..3248801 100644 --- a/tests/auto/declarative/declarative.pro +++ b/tests/auto/declarative/declarative.pro @@ -42,6 +42,7 @@ PRIVATETESTS += \ qdeclarativeimageprovider \ qdeclarativeinstruction \ qdeclarativelanguage \ + qdeclarativelistcompositor \ qdeclarativelistmodel \ qdeclarativeproperty \ qdeclarativepropertymap \ diff --git a/tests/auto/declarative/qdeclarativelistcompositor/qdeclarativelistcompositor.pro b/tests/auto/declarative/qdeclarativelistcompositor/qdeclarativelistcompositor.pro new file mode 100644 index 0000000..e3988b3 --- /dev/null +++ b/tests/auto/declarative/qdeclarativelistcompositor/qdeclarativelistcompositor.pro @@ -0,0 +1,17 @@ +load(qttest_p4) +contains(QT_CONFIG,declarative): QT += declarative gui +macx:CONFIG -= app_bundle + +SOURCES += tst_qdeclarativelistcompositor.cpp + +symbian: { + importFiles.files = data + importFiles.path = . + DEPLOYMENT += importFiles +} else { + DEFINES += SRCDIR=\\\"$$PWD\\\" +} + +CONFIG += parallel_test + +QT += core-private gui-private declarative-private diff --git a/tests/auto/declarative/qdeclarativelistcompositor/tst_qdeclarativelistcompositor.cpp b/tests/auto/declarative/qdeclarativelistcompositor/tst_qdeclarativelistcompositor.cpp new file mode 100644 index 0000000..030d2e2 --- /dev/null +++ b/tests/auto/declarative/qdeclarativelistcompositor/tst_qdeclarativelistcompositor.cpp @@ -0,0 +1,666 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include + +template 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(), 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(), 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(), 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(), 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(), 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(), 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(), 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 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 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(), 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(), 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(), 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(), 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(), 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(), 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 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 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 removes; + QVector 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 changes; + + compositor.append(&listA, 0, 7, C::AppendFlag | C::PrependFlag | C::DefaultFlag); + compositor.append(&listB, 0, 4, C::DefaultFlag); + compositor.move(C::Default, 2, C::Default, 8, 3); + + QCOMPARE(compositor.count(C::Default), 11); + { const int indexes[] = {/*A*/0,1,5,6,/*B*/0,1,2,3,/*A*/2,3,4}; + for (int i = 0; i < lengthOf(indexes); ++i) { + QCOMPARE(compositor.find(C::Default, i).modelIndex(), indexes[i]); + } + } + + compositor.listItemsChanged(&listA, 3, 4, &changes); + QCOMPARE(changes.count(), 2); + QCOMPARE(changes.at(0).index[C::Default], 2); QCOMPARE(changes.at(0).count, 2); + QCOMPARE(changes.at(1).index[C::Default], 9); QCOMPARE(changes.at(0).count, 2); +} + +QTEST_MAIN(tst_qdeclarativelistcompositor) + +#include "tst_qdeclarativelistcompositor.moc" + diff --git a/tests/auto/declarative/qsgvisualdatamodel/data/groups.qml b/tests/auto/declarative/qsgvisualdatamodel/data/groups.qml new file mode 100644 index 0000000..0189697 --- /dev/null +++ b/tests/auto/declarative/qsgvisualdatamodel/data/groups.qml @@ -0,0 +1,32 @@ +import QtQuick 2.0 + +ListView { + width: 100 + height: 100 + model: VisualDataModel { + groups: [ + VisualDataGroup { id: visibleItems; objectName: "visibleItems"; name: "visible"; includeByDefault: true }, + VisualDataGroup { id: selectedItems; objectName: "selectedItems"; name: "selected" } + ] + + model: myModel + delegate: Item { + id: delegate + objectName: "delegate" + width: 100 + height: 2 + property variant test1: name + property variant test2: index + property variant test3: VisualDataModel.itemsIndex + property variant test4: VisualDataModel.inItems + property variant test5: VisualDataModel.visibleIndex + property variant test6: VisualDataModel.inVisible + property variant test7: VisualDataModel.selectedIndex + property variant test8: VisualDataModel.inSelected + property variant test9: VisualDataModel.groups + + function hide() { VisualDataModel.inVisible = false } + function select() { VisualDataModel.inSelected = true } + } + } +} diff --git a/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp b/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp index 824b623..e8659bc 100644 --- a/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp +++ b/tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp @@ -55,6 +55,8 @@ #include #include +template 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 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 T *findItem(QSGItem *parent, const QString &objectName, int index); @@ -184,6 +200,25 @@ private: QString m_color; }; +template 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(); +} + + +template <> void evaluate(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(view.rootObject()); + QVERIFY(listview != 0); + + QSGItem *contentItem = listview->contentItem(); + QVERIFY(contentItem != 0); + + QSGVisualDataModel *visualModel = qobject_cast(qvariant_cast(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(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(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(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(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(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, ": QML VisualDataGroup: remove: index out of range"); + evaluate(visualModel, "items.remove(-8, 4)"); + QCOMPARE(listview->count(), 7); + QCOMPARE(visualModel->items()->count(), 7); + } { + QTest::ignoreMessage(QtWarningMsg, ": QML VisualDataGroup: remove: index out of range"); + evaluate(visualModel, "items.remove(12, 2)"); + QCOMPARE(listview->count(), 7); + QCOMPARE(visualModel->items()->count(), 7); + } { + QTest::ignoreMessage(QtWarningMsg, ": QML VisualDataGroup: remove: index out of range"); + evaluate(visualModel, "items.remove(5, 3)"); + QCOMPARE(listview->count(), 7); + QCOMPARE(visualModel->items()->count(), 7); + } { + QTest::ignoreMessage(QtWarningMsg, ": QML VisualDataGroup: remove: invalid count"); + evaluate(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(view.rootObject()); + QVERIFY(listview != 0); + + QSGItem *contentItem = listview->contentItem(); + QVERIFY(contentItem != 0); + + QSGVisualDataModel *visualModel = qobject_cast(qvariant_cast(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(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(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(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(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(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(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(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(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(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, ": QML VisualDataGroup: move: invalid count"); + evaluate(visualModel, "items.move(5, 2, -2)"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + } { + QTest::ignoreMessage(QtWarningMsg, ": QML VisualDataGroup: move: from index out of range"); + evaluate(visualModel, "items.move(-6, 2, 1)"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + } { + QTest::ignoreMessage(QtWarningMsg, ": QML VisualDataGroup: move: from index out of range"); + evaluate(visualModel, "items.move(15, 2, 1)"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + } { + QTest::ignoreMessage(QtWarningMsg, ": QML VisualDataGroup: move: from index out of range"); + evaluate(visualModel, "items.move(11, 1, 3)"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + } { + QTest::ignoreMessage(QtWarningMsg, ": QML VisualDataGroup: move: to index out of range"); + evaluate(visualModel, "items.move(2, -5, 1)"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + } { + QTest::ignoreMessage(QtWarningMsg, ": QML VisualDataGroup: move: to index out of range"); + evaluate(visualModel, "items.move(2, 14, 1)"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + } { + QTest::ignoreMessage(QtWarningMsg, ": QML VisualDataGroup: move: to index out of range"); + evaluate(visualModel, "items.move(2, 11, 4)"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + } +} + + +template 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(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(view.rootObject()); + QVERIFY(listview != 0); + + QSGItem *contentItem = listview->contentItem(); + QVERIFY(contentItem != 0); + + QSGVisualDataModel *visualModel = qobject_cast(qvariant_cast(listview->model())); + QVERIFY(visualModel); + + QSGVisualDataGroup *visibleItems = visualModel->findChild("visibleItems"); + QVERIFY(visibleItems); + + QSGVisualDataGroup *selectedItems = visualModel->findChild("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(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(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(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(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, ": QML VisualDataGroup: addGroups: invalid count"); + evaluate(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, ": QML VisualDataGroup: addGroups: index out of range"); + evaluate(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, ": QML VisualDataGroup: addGroups: index out of range"); + evaluate(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, ": QML VisualDataGroup: addGroups: index out of range"); + evaluate(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, ": QML VisualDataGroup: setGroups: invalid count"); + evaluate(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, ": QML VisualDataGroup: setGroups: index out of range"); + evaluate(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, ": QML VisualDataGroup: setGroups: index out of range"); + evaluate(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, ": QML VisualDataGroup: setGroups: index out of range"); + evaluate(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, ": QML VisualDataGroup: removeGroups: invalid count"); + evaluate(visualModel, "items.removeGroups(11, -4, \"items\")"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + } { + QTest::ignoreMessage(QtWarningMsg, ": QML VisualDataGroup: removeGroups: index out of range"); + evaluate(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, ": QML VisualDataGroup: removeGroups: index out of range"); + evaluate(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, ": QML VisualDataGroup: removeGroups: index out of range"); + evaluate(visualModel, "items.removeGroups(11, 5, \"items\")"); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + QCOMPARE(visibleItems->count(), 9); + QCOMPARE(selectedItems->count(), 2); + } { + evaluate(visualModel, "filterOnGroup = \"visible\""); + QCOMPARE(listview->count(), 9); + QCOMPARE(visualModel->items()->count(), 12); + QCOMPARE(visibleItems->count(), 9); + QCOMPARE(selectedItems->count(), 2); + } { + evaluate(visualModel, "filterOnGroup = \"selected\""); + QCOMPARE(listview->count(), 2); + QCOMPARE(visualModel->items()->count(), 12); + QCOMPARE(visibleItems->count(), 9); + QCOMPARE(selectedItems->count(), 2); + } { + evaluate(visualModel, "filterOnGroup = \"items\""); + QCOMPARE(listview->count(), 12); + QCOMPARE(visualModel->items()->count(), 12); + QCOMPARE(visibleItems->count(), 9); + QCOMPARE(selectedItems->count(), 2); + } { + QSGItem *delegate = findItem(contentItem, "delegate", 5); + QVERIFY(delegate); + + evaluate(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(contentItem, "delegate", 5); + QVERIFY(delegate); + + evaluate(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(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 T *tst_qsgvisualdatamodel::findItem(QSGItem *parent, const QString &objectName, int index)