Add a get function to VisualDataGroup.
authorAndrew den Exter <andrew.den-exter@nokia.com>
Fri, 23 Sep 2011 03:43:24 +0000 (13:43 +1000)
committerQt by Nokia <qt-info@nokia.com>
Thu, 6 Oct 2011 08:44:27 +0000 (10:44 +0200)
This returns an object with properties for accessing the model data of
an item, membership in the visual data model's groups and the index
of the item in each group.

Task-number: QTBUG-21512

Change-Id: I878442c8a2e30cc6f9941f7412303581613a6142
Reviewed-on: http://codereview.qt-project.org/5515
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Martin Jones <martin.jones@nokia.com>

examples/declarative/modelviews/visualdatamodel/sortedmodel.qml [new file with mode: 0644]
examples/declarative/modelviews/visualdatamodel/visualdatamodel.qmlproject [new file with mode: 0644]
src/declarative/items/qsgvisualdatamodel.cpp
src/declarative/items/qsgvisualdatamodel_p.h
src/declarative/qml/v8/qv8engine_p.h
tests/auto/declarative/qsgvisualdatamodel/data/groups.qml
tests/auto/declarative/qsgvisualdatamodel/tst_qsgvisualdatamodel.cpp

diff --git a/examples/declarative/modelviews/visualdatamodel/sortedmodel.qml b/examples/declarative/modelviews/visualdatamodel/sortedmodel.qml
new file mode 100644 (file)
index 0000000..2a72060
--- /dev/null
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** 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
+
+Rectangle {
+    width: 480; height: 640
+
+    Component {
+        id: numberDelegate
+
+        Text {
+            id: numberText
+            anchors { left: parent.left; right: parent.right }
+            text: number
+
+            horizontalAlignment: Text.AlignHCenter
+            font.pixelSize: 18
+
+            Text {
+                anchors { left: parent.left; baseline: parent.baseline }
+                text: index
+
+                horizontalAlignment: Text.AlignLeft
+                font.pixelSize: 12
+            }
+            Text {
+                anchors { right: parent.right; baseline: parent.baseline }
+                text: numberText.VisualDataModel.itemsIndex
+
+                horizontalAlignment: Text.AlignRight
+                font.pixelSize: 12
+            }
+        }
+    }
+
+    ListView {
+        anchors {
+            left: parent.left; top: parent.top;
+            right: parent.horizontalCenter; bottom: button.top
+            leftMargin: 2; topMargin: 2; rightMargin: 1; bottomMargin: 2
+        }
+
+        model: ListModel {
+            id: unsortedModel
+        }
+        delegate: numberDelegate
+    }
+    ListView {
+        anchors {
+            left: parent.horizontalCenter; top: parent.top;
+            right: parent.right; bottom: button.top
+            leftMargin: 1; topMargin: 2; rightMargin: 2; bottomMargin: 2
+        }
+        model: VisualDataModel {
+            model: unsortedModel
+            delegate: numberDelegate
+
+            items.onChanged: {
+                for (var i = 0; i < inserted.length; ++i) {
+                    for (var j = inserted[i].index; j < inserted[i].index + inserted[i].count; ++j) {
+                        var number = items.get(j).model.number
+                        for (var l = 0, k = 0; l < unsortedModel.count; ++l) {
+                            if (l == inserted[k].index) {
+                                l += inserted[k].count - 1
+                                ++k
+                            } else if (number < items.get(l).model.number) {
+                                items.move(j, l, 1)
+                                break
+                            }
+                        }
+                        inserted[i].index += 1;
+                        inserted[i].count -= 1;
+                    }
+                }
+            }
+        }
+    }
+
+    Rectangle {
+        id: button
+
+        anchors { left: parent.left; right: parent.right; bottom: parent.bottom; margins: 2 }
+        height: moreText.implicitHeight + 4
+
+        color: "black"
+
+        Text {
+            id: moreText
+
+            anchors.fill: parent
+            text: "More"
+            color: "white"
+            font.pixelSize: 18
+            horizontalAlignment: Text.AlignHCenter
+            verticalAlignment: Text.AlignVCenter
+        }
+        MouseArea {
+            anchors.fill: parent
+
+            onClicked: unsortedModel.append({ "number": Math.floor(Math.random() * 100) })
+        }
+    }
+}
diff --git a/examples/declarative/modelviews/visualdatamodel/visualdatamodel.qmlproject b/examples/declarative/modelviews/visualdatamodel/visualdatamodel.qmlproject
new file mode 100644 (file)
index 0000000..d4909f8
--- /dev/null
@@ -0,0 +1,16 @@
+import QmlProject 1.0
+
+Project {
+    /* Include .qml, .js, and image files from current directory and subdirectories */
+    QmlFiles {
+        directory: "."
+    }
+    JavaScriptFiles {
+        directory: "."
+    }
+    ImageFiles {
+        directory: "."
+    }
+    /* List of plugin directories passed to QML runtime */
+    // importPaths: [ " ../exampleplugin " ]
+}
index 7793230..f5c86b8 100644 (file)
@@ -290,28 +290,42 @@ QSGVisualDataModelParts::QSGVisualDataModelParts(QSGVisualDataModel *parent)
 class QSGVisualDataModelCacheMetaType : public QDeclarativeRefCount
 {
 public:
-    QSGVisualDataModelCacheMetaType(QSGVisualDataModel *model, const QStringList &groupNames);
+    QSGVisualDataModelCacheMetaType(QV8Engine *engine, QSGVisualDataModel *model, const QStringList &groupNames);
     ~QSGVisualDataModelCacheMetaType();
 
     int parseGroups(const QStringList &groupNames) const;
     int parseGroups(QV8Engine *engine, const v8::Local<v8::Value> &groupNames) const;
 
+    static v8::Handle<v8::Value> get_model(v8::Local<v8::String>, const v8::AccessorInfo &info);
+    static v8::Handle<v8::Value> get_groups(v8::Local<v8::String>, const v8::AccessorInfo &info);
+    static void set_groups(
+            v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info);
+    static v8::Handle<v8::Value> get_member(v8::Local<v8::String>, const v8::AccessorInfo &info);
+    static void set_member(
+            v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info);
+    static v8::Handle<v8::Value> get_index(v8::Local<v8::String>, const v8::AccessorInfo &info);
+
     QDeclarativeGuard<QSGVisualDataModel> model;
     const int groupCount;
     const int memberPropertyOffset;
     const int indexPropertyOffset;
+    QV8Engine * const v8Engine;
     QMetaObject *metaObject;
     const QStringList groupNames;
+    v8::Persistent<v8::Function> constructor;
 };
 
-class QSGVisualDataModelCacheItem
+class QSGVisualDataModelCacheItem : public QV8ObjectResource
 {
+    V8_RESOURCE_TYPE(VisualDataItemType)
 public:
     QSGVisualDataModelCacheItem(QSGVisualDataModelCacheMetaType *metaType)
-        : metaType(metaType)
+        : QV8ObjectResource(metaType->v8Engine)
+        , metaType(metaType)
         , object(0)
         , attached(0)
         , objectRef(0)
+        , scriptRef(0)
         , groups(0)
     {
         metaType->addref();
@@ -319,6 +333,7 @@ public:
 
     ~QSGVisualDataModelCacheItem()
     {
+        Q_ASSERT(scriptRef == 0);
         Q_ASSERT(objectRef == 0);
         Q_ASSERT(!object);
 
@@ -328,12 +343,15 @@ public:
     void referenceObject() { ++objectRef; }
     bool releaseObject() { return --objectRef == 0; }
 
-    bool isReferenced() const { return objectRef; }
+    bool isReferenced() const { return objectRef || scriptRef; }
+
+    void Dispose();
 
     QSGVisualDataModelCacheMetaType * const metaType;
     QDeclarativeGuard<QObject> object;
     QSGVisualDataModelAttached *attached;
     int objectRef;
+    int scriptRef;
     int groups;
     int index[Compositor::MaximumGroupCount];
 };
@@ -437,7 +455,8 @@ QSGVisualDataModel::~QSGVisualDataModel()
     foreach (QSGVisualDataModelCacheItem *cacheItem, d->m_cache) {
         cacheItem->object = 0;
         cacheItem->objectRef = 0;
-        delete cacheItem;
+        if (!cacheItem->isReferenced())
+            delete cacheItem;
     }
 
     delete d->m_adaptorModel;
@@ -483,7 +502,8 @@ void QSGVisualDataModel::componentComplete()
     if (!d->m_context)
         d->m_context = qmlContext(this);
 
-    d->m_cacheMetaType = new QSGVisualDataModelCacheMetaType(this, groupNames);
+    d->m_cacheMetaType = new QSGVisualDataModelCacheMetaType(
+            QDeclarativeEnginePrivate::getV8Engine(d->m_context->engine()), this, groupNames);
 
     d->m_compositor.setGroupCount(d->m_groupCount);
     d->m_compositor.setDefaultGroups(defaultGroups);
@@ -681,10 +701,12 @@ QSGVisualDataModel::ReleaseFlags QSGVisualDataModelPrivate::release(QObject *obj
             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));
+            if (!cacheItem->isReferenced()) {
+                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;
         }
@@ -1390,11 +1412,12 @@ QSGVisualDataModelAttached *QSGVisualDataModel::qmlAttachedProperties(QObject *o
 //============================================================================
 
 QSGVisualDataModelCacheMetaType::QSGVisualDataModelCacheMetaType(
-        QSGVisualDataModel *model, const QStringList &groupNames)
+        QV8Engine *engine, QSGVisualDataModel *model, const QStringList &groupNames)
     : model(model)
     , groupCount(groupNames.count() + 1)
     , memberPropertyOffset(QSGVisualDataModelAttached::staticMetaObject.propertyCount())
     , indexPropertyOffset(QSGVisualDataModelAttached::staticMetaObject.propertyCount() + groupNames.count())
+    , v8Engine(engine)
     , metaObject(0)
     , groupNames(groupNames)
 {
@@ -1403,6 +1426,13 @@ QSGVisualDataModelCacheMetaType::QSGVisualDataModelCacheMetaType(
     builder.setClassName(QSGVisualDataModelAttached::staticMetaObject.className());
     builder.setSuperClass(&QSGVisualDataModelAttached::staticMetaObject);
 
+    v8::HandleScope handleScope;
+    v8::Context::Scope contextScope(engine->context());
+    v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
+    ft->InstanceTemplate()->SetHasExternalResource(true);
+    ft->PrototypeTemplate()->SetAccessor(v8::String::New("model"), get_model);
+    ft->PrototypeTemplate()->SetAccessor(v8::String::New("groups"), get_groups, set_groups);
+
     int notifierId = 0;
     for (int i = 0; i < groupNames.count(); ++i, ++notifierId) {
         QString propertyName = QStringLiteral("in") + groupNames.at(i);
@@ -1411,6 +1441,9 @@ QSGVisualDataModelCacheMetaType::QSGVisualDataModelCacheMetaType(
         QMetaPropertyBuilder propertyBuilder = builder.addProperty(
                 propertyName.toUtf8(), "bool", notifierId);
         propertyBuilder.setWritable(true);
+
+        ft->PrototypeTemplate()->SetAccessor(
+                engine->toString(propertyName), get_member, set_member, v8::Int32::New(i + 1));
     }
     for (int i = 0; i < groupNames.count(); ++i, ++notifierId) {
         const QString propertyName = groupNames.at(i) + QStringLiteral("Index");
@@ -1418,14 +1451,20 @@ QSGVisualDataModelCacheMetaType::QSGVisualDataModelCacheMetaType(
         QMetaPropertyBuilder propertyBuilder = builder.addProperty(
                 propertyName.toUtf8(), "int", notifierId);
         propertyBuilder.setWritable(true);
+
+        ft->PrototypeTemplate()->SetAccessor(
+                engine->toString(propertyName), get_index, 0, v8::Int32::New(i + 1));
     }
 
     metaObject = builder.toMetaObject();
+
+    constructor = qPersistentNew<v8::Function>(ft->GetFunction());
 }
 
 QSGVisualDataModelCacheMetaType::~QSGVisualDataModelCacheMetaType()
 {
     qFree(metaObject);
+    qPersistentDispose(constructor);
 }
 
 int QSGVisualDataModelCacheMetaType::parseGroups(const QStringList &groups) const
@@ -1459,6 +1498,135 @@ int QSGVisualDataModelCacheMetaType::parseGroups(QV8Engine *engine, const v8::Lo
     return groupFlags;
 }
 
+v8::Handle<v8::Value> QSGVisualDataModelCacheMetaType::get_model(
+        v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+    QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast<QSGVisualDataModelCacheItem>(info.This());
+    if (!cacheItem)
+        V8THROW_ERROR("Not a valid VisualData object");
+    if (!cacheItem->metaType->model)
+        return v8::Undefined();
+    QObject *data = 0;
+
+    QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(cacheItem->metaType->model);
+    for (int i = 1; i < cacheItem->metaType->groupCount; ++i) {
+        if (cacheItem->groups & (1 << i)) {
+            Compositor::iterator it = model->m_compositor.find(
+                    Compositor::Group(i), cacheItem->index[i]);
+            if (QSGVisualAdaptorModel *list = it.list<QSGVisualAdaptorModel>())
+                data = list->data(it.modelIndex());
+            break;
+        }
+    }
+    if (!data)
+        return v8::Undefined();
+    return cacheItem->engine->newQObject(data);
+}
+
+v8::Handle<v8::Value> QSGVisualDataModelCacheMetaType::get_groups(
+        v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+    QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast<QSGVisualDataModelCacheItem>(info.This());
+    if (!cacheItem)
+        V8THROW_ERROR("Not a valid VisualData object");
+
+    QStringList groups;
+    for (int i = 1; i < cacheItem->metaType->groupCount; ++i) {
+        if (cacheItem->groups & (1 << i))
+            groups.append(cacheItem->metaType->groupNames.at(i - 1));
+    }
+
+    return cacheItem->engine->fromVariant(groups);
+}
+
+void QSGVisualDataModelCacheMetaType::set_groups(
+        v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+    QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast<QSGVisualDataModelCacheItem>(info.This());
+    if (!cacheItem)
+        V8THROW_ERROR_SETTER("Not a valid VisualData object");
+
+    if (!cacheItem->metaType->model)
+        return;
+    QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(cacheItem->metaType->model);
+
+    const int groupFlags = model->m_cacheMetaType->parseGroups(cacheItem->engine, value);
+    for (int i = 1; i < cacheItem->metaType->groupCount; ++i) {
+        if (cacheItem->groups & (1 << i)) {
+            model->setGroups(Compositor::Group(i), cacheItem->index[i], 1, groupFlags);
+            break;
+        }
+    }
+}
+
+v8::Handle<v8::Value> QSGVisualDataModelCacheMetaType::get_member(
+        v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+    QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast<QSGVisualDataModelCacheItem>(info.This());
+    if (!cacheItem)
+        V8THROW_ERROR("Not a valid VisualData object");
+
+    return v8::Boolean::New(cacheItem->groups & (1 << info.Data()->Int32Value()));
+}
+
+void QSGVisualDataModelCacheMetaType::set_member(
+        v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+    QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast<QSGVisualDataModelCacheItem>(info.This());
+    if (!cacheItem)
+        V8THROW_ERROR_SETTER("Not a valid VisualData object");
+
+    if (!cacheItem->metaType->model)
+        return;
+    QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(cacheItem->metaType->model);
+
+    Compositor::Group group = Compositor::Group(info.Data()->Int32Value());
+    const bool member = value->BooleanValue();
+    const int groupFlag = (1 << group);
+    if (member == ((cacheItem->groups & groupFlag) != 0))
+        return;
+
+    for (int i = 1; i < cacheItem->metaType->groupCount; ++i) {
+        if (cacheItem->groups & (1 << i)) {
+            if (member)
+                model->addGroups(Compositor::Group(i), cacheItem->index[i], 1, groupFlag);
+            else
+                model->removeGroups(Compositor::Group(i), cacheItem->index[i], 1, groupFlag);
+            break;
+        }
+    }
+}
+
+v8::Handle<v8::Value> QSGVisualDataModelCacheMetaType::get_index(
+        v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+    QSGVisualDataModelCacheItem *cacheItem = v8_resource_cast<QSGVisualDataModelCacheItem>(info.This());
+    if (!cacheItem)
+        V8THROW_ERROR("Not a valid VisualData object");
+
+    return v8::Integer::New(cacheItem->index[info.Data()->Int32Value()]);
+}
+
+
+//---------------------------------------------------------------------------
+
+void QSGVisualDataModelCacheItem::Dispose()
+{
+    --scriptRef;
+    if (isReferenced())
+        return;
+
+    if (metaType->model) {
+        QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(metaType->model);
+        const int cacheIndex = model->m_cache.indexOf(this);
+        if (cacheIndex != -1) {
+            model->m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag);
+            model->m_cache.removeAt(cacheIndex);
+        }
+    }
+    delete this;
+}
+
 //---------------------------------------------------------------------------
 
 QSGVisualDataModelAttachedMetaObject::QSGVisualDataModelAttachedMetaObject(
@@ -1743,6 +1911,62 @@ void QSGVisualDataGroup::setDefaultInclude(bool include)
 }
 
 /*!
+    \qmlmethod var QtQuick2::VisualDataGroup::get(int index)
+
+    Returns a javascript object describing the item at \a index in the group.
+
+    The returned object contains the same information that is available to a delegate from the
+    VisualDataModel attached as well as the model for that item.  It has the properties:
+
+    \list
+    \o \b model The model data of the item.  This is the same as the model context property in
+    a delegate
+    \o \b groups A list the of names of groups the item is a member of.  This property can be
+    written to change the item's membership.
+    \o \b inItems Whether the item belongs to the \l {VisualDataModel::items}{items} group.
+    Writing to this property will add or remove the item from the group.
+    \o \b itemsIndex The index of the item within the \l {VisualDataModel::items}{items} group.
+    \o \b {in\i{GroupName}} Whether the item belongs to the dynamic group \i groupName.  Writing to
+    this property will add or remove the item from the group.
+    \o \b {\i{groupName}Index} The index of the item within the dynamic group \i groupName.
+    \endlist
+*/
+
+QDeclarativeV8Handle QSGVisualDataGroup::get(int index)
+{
+    Q_D(QSGVisualDataGroup);
+    if (!d->model)
+        return QDeclarativeV8Handle::fromHandle(v8::Undefined());;
+
+    QSGVisualDataModelPrivate *model = QSGVisualDataModelPrivate::get(d->model);
+    if (index < 0 || index >= model->m_compositor.count(d->group)) {
+        qmlInfo(this) << tr("get: index out of range");
+        return QDeclarativeV8Handle::fromHandle(v8::Undefined());
+    }
+
+    Compositor::iterator it = model->m_compositor.find(d->group, index);
+    QSGVisualDataModelCacheItem *cacheItem = it->inCache()
+            ? model->m_cache.at(it.cacheIndex)
+            : 0;
+
+    if (!cacheItem) {
+        cacheItem = new QSGVisualDataModelCacheItem(model->m_cacheMetaType);
+        for (int i = 0; i < model->m_groupCount; ++i)
+            cacheItem->index[i] = it.index[i];
+        cacheItem->groups = it->flags & Compositor::GroupMask;
+
+        model->m_cache.insert(it.cacheIndex, cacheItem);
+        model->m_compositor.setFlags(it, 1, Compositor::CacheFlag);
+    }
+
+    ++cacheItem->scriptRef;
+
+    v8::Local<v8::Object> rv = model->m_cacheMetaType->constructor->NewInstance();
+    rv->SetExternalResource(cacheItem);
+    return QDeclarativeV8Handle::fromHandle(rv);
+}
+
+/*!
     \qmlmethod QtQuick2::VisualDataGroup::remove(int index, int count)
 
     Removes \a count items starting at \a index from the group.
index 94640a6..9b4542d 100644 (file)
@@ -50,6 +50,8 @@
 #include <QtCore/qabstractitemmodel.h>
 #include <QtCore/qstringlist.h>
 
+#include <private/qv8engine_p.h>
+
 QT_BEGIN_HEADER
 
 Q_DECLARE_METATYPE(QModelIndex)
@@ -63,7 +65,6 @@ class QDeclarativeChangeSet;
 class QDeclarativeComponent;
 class QDeclarativePackage;
 class QDeclarativeV8Function;
-class QDeclarativeV8Handle;
 class QSGVisualDataGroup;
 class QSGVisualDataModelAttached;
 class QSGVisualDataModelPrivate;
@@ -161,6 +162,8 @@ public:
     bool defaultInclude() const;
     void setDefaultInclude(bool include);
 
+    Q_INVOKABLE QDeclarativeV8Handle get(int index);
+
 public Q_SLOTS:
     void remove(QDeclarativeV8Function *);
     void addGroups(QDeclarativeV8Function *);
index 114584c..185eb54 100644 (file)
@@ -136,7 +136,7 @@ public:
     enum ResourceType { ContextType, QObjectType, TypeType, ListType, VariantType, 
                         ValueTypeType, XMLHttpRequestType, DOMNodeType, SQLDatabaseType,
                         ListModelType, Context2DType, Context2DStyleType, Context2DPixelArrayType, 
-                        ParticleDataType, SignalHandlerType, IncubatorType };
+                        ParticleDataType, SignalHandlerType, IncubatorType, VisualDataItemType };
     virtual ResourceType resourceType() const = 0;
 
     QV8Engine *engine;
index 0189697..a24e223 100644 (file)
@@ -3,6 +3,14 @@ import QtQuick 2.0
 ListView {
     width: 100
     height: 100
+
+    function contains(array, value) {
+        for (var i = 0; i < array.length; ++i)
+            if (array[i] == value)
+                return true
+        return false
+    }
+
     model: VisualDataModel {
         groups: [
             VisualDataGroup { id: visibleItems; objectName: "visibleItems"; name: "visible"; includeByDefault: true },
index e8659bc..50e1b8f 100644 (file)
@@ -52,6 +52,7 @@
 #include <private/qsgvisualdatamodel_p.h>
 #include <private/qdeclarativevaluetype_p.h>
 #include <private/qdeclarativechangeset_p.h>
+#include <private/qdeclarativeengine_p.h>
 #include <math.h>
 #include <QtOpenGL/QGLShaderProgram>
 
@@ -133,6 +134,7 @@ private slots:
     void remove();
     void move();
     void groups();
+    void get();
 
 private:
     template <int N> void groups_verify(
@@ -145,6 +147,18 @@ private:
             const bool (&vMember)[N],
             const bool (&sMember)[N]);
 
+    template <int N> void get_verify(
+            const SingleRoleModel &model,
+            QSGVisualDataModel *visualModel,
+            QSGVisualDataGroup *visibleItems,
+            QSGVisualDataGroup *selectedItems,
+            const int (&mIndex)[N],
+            const int (&iIndex)[N],
+            const int (&vIndex)[N],
+            const int (&sIndex)[N],
+            const bool (&vMember)[N],
+            const bool (&sMember)[N]);
+
     bool failed;
     QDeclarativeEngine engine;
     template<typename T>
@@ -203,13 +217,12 @@ private:
 template <typename T> static T evaluate(QObject *scope, const QString &expression)
 {
     QDeclarativeExpression expr(qmlContext(scope), scope, expression);
-    QVariant result = expr.evaluate();
+    T result = expr.evaluate().value<T>();
     if (expr.hasError())
         qWarning() << expr.error().toString();
-    return result.value<T>();
+    return result;
 }
 
-
 template <> void evaluate<void>(QObject *scope, const QString &expression)
 {
     QDeclarativeExpression expr(qmlContext(scope), scope, expression);
@@ -218,7 +231,6 @@ template <> void evaluate<void>(QObject *scope, const QString &expression)
         qWarning() << expr.error().toString();
 }
 
-
 tst_qsgvisualdatamodel::tst_qsgvisualdatamodel()
 {
 }
@@ -1183,6 +1195,221 @@ void tst_qsgvisualdatamodel::groups()
     }
 }
 
+template <int N> void tst_qsgvisualdatamodel::get_verify(
+        const SingleRoleModel &model,
+        QSGVisualDataModel *visualModel,
+        QSGVisualDataGroup *visibleItems,
+        QSGVisualDataGroup *selectedItems,
+        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) {
+        QCOMPARE(evaluate<QString>(visualModel, QString("items.get(%1).model.name").arg(i)), model.list.at(mIndex[i]));
+        QCOMPARE(evaluate<QString>(visualModel, QString("items.get(%1).model.modelData").arg(i)), model.list.at(mIndex[i]));
+        QCOMPARE(evaluate<int>(visualModel, QString("items.get(%1).model.index").arg(i)), mIndex[i]);
+        QCOMPARE(evaluate<int>(visualModel, QString("items.get(%1).itemsIndex").arg(i)), iIndex[i]);
+        QCOMPARE(evaluate<bool>(visualModel, QString("items.get(%1).inItems").arg(i)), true);
+        QCOMPARE(evaluate<int>(visualModel, QString("items.get(%1).visibleIndex").arg(i)), vIndex[i]);
+        QCOMPARE(evaluate<bool>(visualModel, QString("items.get(%1).inVisible").arg(i)), vMember[i]);
+        QCOMPARE(evaluate<int>(visualModel, QString("items.get(%1).selectedIndex").arg(i)), sIndex[i]);
+        QCOMPARE(evaluate<bool>(visualModel, QString("items.get(%1).inSelected").arg(i)), sMember[i]);
+        QCOMPARE(evaluate<bool>(visualModel, QString("contains(items.get(%1).groups, \"items\")").arg(i)), true);
+        QCOMPARE(evaluate<bool>(visualModel, QString("contains(items.get(%1).groups, \"visible\")").arg(i)), vMember[i]);
+        QCOMPARE(evaluate<bool>(visualModel, QString("contains(items.get(%1).groups, \"selected\")").arg(i)), sMember[i]);
+
+        if (vMember[i]) {
+            QCOMPARE(evaluate<QString>(visibleItems, QString("get(%1).model.name").arg(vIndex[i])), model.list.at(mIndex[i]));
+            QCOMPARE(evaluate<QString>(visibleItems, QString("get(%1).model.modelData").arg(vIndex[i])), model.list.at(mIndex[i]));
+            QCOMPARE(evaluate<int>(visibleItems, QString("get(%1).model.index").arg(vIndex[i])), mIndex[i]);
+            QCOMPARE(evaluate<int>(visibleItems, QString("get(%1).itemsIndex").arg(vIndex[i])), iIndex[i]);
+            QCOMPARE(evaluate<bool>(visibleItems, QString("get(%1).inItems").arg(vIndex[i])), true);
+            QCOMPARE(evaluate<int>(visibleItems, QString("get(%1).visibleIndex").arg(vIndex[i])), vIndex[i]);
+            QCOMPARE(evaluate<bool>(visibleItems, QString("get(%1).inVisible").arg(vIndex[i])), vMember[i]);
+            QCOMPARE(evaluate<int>(visibleItems, QString("get(%1).selectedIndex").arg(vIndex[i])), sIndex[i]);
+            QCOMPARE(evaluate<bool>(visibleItems, QString("get(%1).inSelected").arg(vIndex[i])), sMember[i]);
+
+            QCOMPARE(evaluate<bool>(visibleItems, QString("contains(get(%1).groups, \"items\")").arg(vIndex[i])), true);
+            QCOMPARE(evaluate<bool>(visibleItems, QString("contains(get(%1).groups, \"visible\")").arg(vIndex[i])), vMember[i]);
+            QCOMPARE(evaluate<bool>(visibleItems, QString("contains(get(%1).groups, \"selected\")").arg(vIndex[i])), sMember[i]);
+        }
+        if (sMember[i]) {
+            QCOMPARE(evaluate<QString>(selectedItems, QString("get(%1).model.name").arg(sIndex[i])), model.list.at(mIndex[i]));
+            QCOMPARE(evaluate<QString>(selectedItems, QString("get(%1).model.modelData").arg(sIndex[i])), model.list.at(mIndex[i]));
+            QCOMPARE(evaluate<int>(selectedItems, QString("get(%1).model.index").arg(sIndex[i])), mIndex[i]);
+            QCOMPARE(evaluate<int>(selectedItems, QString("get(%1).itemsIndex").arg(sIndex[i])), iIndex[i]);
+            QCOMPARE(evaluate<bool>(selectedItems, QString("get(%1).inItems").arg(sIndex[i])), true);
+            QCOMPARE(evaluate<int>(selectedItems, QString("get(%1).visibleIndex").arg(sIndex[i])), vIndex[i]);
+            QCOMPARE(evaluate<bool>(selectedItems, QString("get(%1).inVisible").arg(sIndex[i])), vMember[i]);
+            QCOMPARE(evaluate<int>(selectedItems, QString("get(%1).selectedIndex").arg(sIndex[i])), sIndex[i]);
+            QCOMPARE(evaluate<bool>(selectedItems, QString("get(%1).inSelected").arg(sIndex[i])), sMember[i]);
+            QCOMPARE(evaluate<bool>(selectedItems, QString("contains(get(%1).groups, \"items\")").arg(sIndex[i])), true);
+            QCOMPARE(evaluate<bool>(selectedItems, QString("contains(get(%1).groups, \"visible\")").arg(sIndex[i])), vMember[i]);
+            QCOMPARE(evaluate<bool>(selectedItems, QString("contains(get(%1).groups, \"selected\")").arg(sIndex[i])), sMember[i]);
+        }
+    }
+    failed = false;
+}
+
+#define VERIFY_GET \
+    get_verify(model, visualModel, visibleItems, selectedItems, mIndex, iIndex, vIndex, sIndex, vMember, sMember); \
+    QVERIFY(!failed)
+
+void tst_qsgvisualdatamodel::get()
+{
+    QSGView view;
+
+    SingleRoleModel model;
+    model.list = QStringList()
+            << "one"
+            << "two"
+            << "three"
+            << "four"
+            << "five"
+            << "six"
+            << "seven"
+            << "eight"
+            << "nine"
+            << "ten"
+            << "eleven"
+            << "twelve";
+
+    QDeclarativeContext *ctxt = view.rootContext();
+    ctxt->setContextProperty("myModel", &model);
+
+    view.setSource(QUrl::fromLocalFile(SRCDIR "/data/groups.qml"));
+
+    QSGListView *listview = qobject_cast<QSGListView*>(view.rootObject());
+    QVERIFY(listview != 0);
+
+    QSGItem *contentItem = listview->contentItem();
+    QVERIFY(contentItem != 0);
+
+    QSGVisualDataModel *visualModel = qobject_cast<QSGVisualDataModel *>(qvariant_cast<QObject *>(listview->model()));
+    QVERIFY(visualModel);
+
+    QSGVisualDataGroup *visibleItems = visualModel->findChild<QSGVisualDataGroup *>("visibleItems");
+    QVERIFY(visibleItems);
+
+    QSGVisualDataGroup *selectedItems = visualModel->findChild<QSGVisualDataGroup *>("selectedItems");
+    QVERIFY(selectedItems);
+
+    QV8Engine *v8Engine = QDeclarativeEnginePrivate::getV8Engine(ctxt->engine());
+    QVERIFY(v8Engine);
+
+    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_GET;
+    } {
+        evaluate<void>(visualModel, "items.addGroups(8, \"selected\")");
+        QCOMPARE(listview->count(), 12);
+        QCOMPARE(visualModel->items()->count(), 12);
+        QCOMPARE(visibleItems->count(), 12);
+        QCOMPARE(selectedItems->count(), 1);
+        static const int  mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+        static const int  iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+        static const int  vIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+        static const bool vMember[] = { t, t, t, t, t, t, t, t, t, t, t, t };
+        static const int  sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1 };
+        static const bool sMember[] = { f, f, f, f, f, f, f, f, t, f, f, f };
+        VERIFY_GET;
+    } {
+        evaluate<void>(visualModel, "items.addGroups(6, 4, [\"visible\", \"selected\"])");
+        QCOMPARE(listview->count(), 12);
+        QCOMPARE(visualModel->items()->count(), 12);
+        QCOMPARE(visibleItems->count(), 12);
+        QCOMPARE(selectedItems->count(), 4);
+        static const int  mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+        static const int  iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+        static const int  vIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+        static const bool vMember[] = { t, t, t, t, t, t, t, t, t, t, t, t };
+        static const int  sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 4 };
+        static const bool sMember[] = { f, f, f, f, f, f, t, t, t, t, f, f };
+        VERIFY_GET;
+    } {
+        evaluate<void>(visualModel, "items.setGroups(2, [\"items\", \"selected\"])");
+        QCOMPARE(listview->count(), 12);
+        QCOMPARE(visualModel->items()->count(), 12);
+        QCOMPARE(visibleItems->count(), 11);
+        QCOMPARE(selectedItems->count(), 5);
+        static const int  mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+        static const int  iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+        static const int  vIndex [] = { 0, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9,10 };
+        static const bool vMember[] = { t, t, f, t, t, t, t, t, t, t, t, t };
+        static const int  sIndex [] = { 0, 0, 0, 1, 1, 1, 1, 2, 3, 4, 5, 5 };
+        static const bool sMember[] = { f, f, t, f, f, f, t, t, t, t, f, f };
+        VERIFY_GET;
+    } {
+        evaluate<void>(selectedItems, "setGroups(0, 3, \"items\")");
+        QCOMPARE(listview->count(), 12);
+        QCOMPARE(visualModel->items()->count(), 12);
+        QCOMPARE(visibleItems->count(), 9);
+        QCOMPARE(selectedItems->count(), 2);
+        static const int  mIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+        static const int  iIndex [] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11 };
+        static const int  vIndex [] = { 0, 1, 2, 2, 3, 4, 5, 5, 5, 6, 7, 8 };
+        static const bool vMember[] = { t, t, f, t, t, t, f, f, t, t, t, t };
+        static const int  sIndex [] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2 };
+        static const bool sMember[] = { f, f, f, f, f, f, f, f, t, t, f, f };
+        VERIFY_GET;
+    } {
+        evaluate<void>(visualModel, "items.get(5).inVisible = false");
+        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_GET;
+    } {
+        evaluate<void>(visualModel, "items.get(5).inSelected = true");
+        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_GET;
+    } {
+        evaluate<void>(visualModel, "items.get(5).groups = [\"visible\", \"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_GET;
+    }
+}
+
 template<typename T>
 T *tst_qsgvisualdatamodel::findItem(QSGItem *parent, const QString &objectName, int index)
 {