aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/types
diff options
context:
space:
mode:
authorAlan Alpert <aalpert@rim.com>2013-01-23 15:17:27 -0800
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-03-14 22:44:11 +0100
commit78b3aed3264439cd95ca23a47e53e15b769a58ae (patch)
tree4228a117f0b48b8e62fc03c03ca7e3bf82efaff3 /src/qml/types
parent2050601125eeb72950a7d718b5136d16c898f8e6 (diff)
Move QML types to types folder
The QtQml module has gain a bunch of QML types cluttering up the qml folder. Moving them to types organizes them a bit better. Change-Id: I570884c00f4abc48f4f1aea048bf002bc70223f3 Reviewed-by: Gabriel de Dietrich <gabriel.dedietrich@digia.com> Reviewed-by: Lars Knoll <lars.knoll@digia.com>
Diffstat (limited to 'src/qml/types')
-rw-r--r--src/qml/types/qqmlbind.cpp310
-rw-r--r--src/qml/types/qqmlbind_p.h92
-rw-r--r--src/qml/types/qqmlconnections.cpp321
-rw-r--r--src/qml/types/qqmlconnections_p.h96
-rw-r--r--src/qml/types/qqmldelegatemodel.cpp3189
-rw-r--r--src/qml/types/qqmldelegatemodel_p.h234
-rw-r--r--src/qml/types/qqmldelegatemodel_p_p.h411
-rw-r--r--src/qml/types/qqmllistmodel.cpp2588
-rw-r--r--src/qml/types/qqmllistmodel_p.h198
-rw-r--r--src/qml/types/qqmllistmodel_p_p.h378
-rw-r--r--src/qml/types/qqmllistmodelworkeragent.cpp259
-rw-r--r--src/qml/types/qqmllistmodelworkeragent_p.h156
-rw-r--r--src/qml/types/qqmlmodelsmodule.cpp60
-rw-r--r--src/qml/types/qqmlmodelsmodule_p.h57
-rw-r--r--src/qml/types/qqmlobjectmodel.cpp269
-rw-r--r--src/qml/types/qqmlobjectmodel_p.h170
-rw-r--r--src/qml/types/qqmltimer.cpp328
-rw-r--r--src/qml/types/qqmltimer_p.h107
-rw-r--r--src/qml/types/qquickpackage.cpp198
-rw-r--r--src/qml/types/qquickpackage_p.h92
-rw-r--r--src/qml/types/qquickworkerscript.cpp740
-rw-r--r--src/qml/types/qquickworkerscript_p.h126
-rw-r--r--src/qml/types/types.pri26
23 files changed, 10405 insertions, 0 deletions
diff --git a/src/qml/types/qqmlbind.cpp b/src/qml/types/qqmlbind.cpp
new file mode 100644
index 0000000000..fcb3079891
--- /dev/null
+++ b/src/qml/types/qqmlbind.cpp
@@ -0,0 +1,310 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlbind_p.h"
+
+#include <private/qqmlnullablevalue_p_p.h>
+#include <private/qqmlproperty_p.h>
+#include <private/qqmlbinding_p.h>
+#include <private/qqmlguard_p.h>
+
+#include <qqmlengine.h>
+#include <qqmlcontext.h>
+#include <qqmlproperty.h>
+#include <qqmlinfo.h>
+
+#include <QtCore/qfile.h>
+#include <QtCore/qdebug.h>
+
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlBindPrivate : public QObjectPrivate
+{
+public:
+ QQmlBindPrivate() : componentComplete(true), obj(0), prevBind(0) {}
+ ~QQmlBindPrivate() { if (prevBind) prevBind->destroy(); }
+
+ QQmlNullableValue<bool> when;
+ bool componentComplete;
+ QQmlGuard<QObject> obj;
+ QString propName;
+ QQmlNullableValue<QVariant> value;
+ QQmlProperty prop;
+ QQmlAbstractBinding *prevBind;
+};
+
+
+/*!
+ \qmltype Binding
+ \instantiates QQmlBind
+ \inqmlmodule QtQml 2
+ \ingroup qtquick-interceptors
+ \brief Enables the arbitrary creation of property bindings
+
+ \section1 Binding to an inaccessible property
+
+ Sometimes it is necessary to bind to a property of an object that wasn't
+ directly instantiated by QML - generally a property of a class exported
+ to QML by C++. In these cases, regular property binding doesn't work. Binding
+ allows you to bind any value to any property.
+
+ For example, imagine a C++ application that maps an "app.enteredText"
+ property into QML. You could use Binding to update the enteredText property
+ like this.
+ \code
+ TextEdit { id: myTextField; text: "Please type here..." }
+ Binding { target: app; property: "enteredText"; value: myTextField.text }
+ \endcode
+ Whenever the text in the TextEdit is updated, the C++ property will be
+ updated also.
+
+ \section1 "Single-branch" conditional binding
+
+ In some circumstances you may want to control the value of a property
+ only when a certain condition is true (and relinquish control in all
+ other circumstances). This often isn't possible to accomplish with a direct
+ binding, as you need to supply values for all possible branches.
+
+ \code
+ // produces warning: "Unable to assign [undefined] to double value"
+ value: if (mouse.pressed) mouse.mouseX
+ \endcode
+
+ The above example will produce a warning whenever we release the mouse, as the value
+ of the binding is undefined when the mouse isn't pressed. We can use the Binding
+ type to rewrite the above code and avoid the warning.
+
+ \qml
+ Binding on value {
+ when: mouse.pressed
+ value: mouse.mouseX
+ }
+ \endqml
+
+ The Binding type will also restore any previously set direct bindings on
+ the property. In that sense, it functions much like a simplified State.
+
+ \qml
+ // this is equivalent to the above Binding
+ State {
+ name: "pressed"
+ when: mouse.pressed
+ PropertyChanges {
+ target: obj
+ value: mouse.mouseX
+ }
+ }
+ \endqml
+
+ If the binding target or binding property is changed, the bound value is
+ immediately pushed onto the new target.
+
+ \sa QtQml
+*/
+QQmlBind::QQmlBind(QObject *parent)
+ : QObject(*(new QQmlBindPrivate), parent)
+{
+}
+
+QQmlBind::~QQmlBind()
+{
+}
+
+/*!
+ \qmlproperty bool QtQml2::Binding::when
+
+ This property holds when the binding is active.
+ This should be set to an expression that evaluates to true when you want the binding to be active.
+
+ \code
+ Binding {
+ target: contactName; property: 'text'
+ value: name; when: list.ListView.isCurrentItem
+ }
+ \endcode
+
+ When the binding becomes inactive again, any direct bindings that were previously
+ set on the property will be restored.
+*/
+bool QQmlBind::when() const
+{
+ Q_D(const QQmlBind);
+ return d->when;
+}
+
+void QQmlBind::setWhen(bool v)
+{
+ Q_D(QQmlBind);
+ if (!d->when.isNull && d->when == v)
+ return;
+
+ d->when = v;
+ eval();
+}
+
+/*!
+ \qmlproperty Object QtQml2::Binding::target
+
+ The object to be updated.
+*/
+QObject *QQmlBind::object()
+{
+ Q_D(const QQmlBind);
+ return d->obj;
+}
+
+void QQmlBind::setObject(QObject *obj)
+{
+ Q_D(QQmlBind);
+ if (d->obj && d->when.isValid() && d->when) {
+ /* if we switch the object at runtime, we need to restore the
+ previous binding on the old object before continuing */
+ d->when = false;
+ eval();
+ d->when = true;
+ }
+ d->obj = obj;
+ if (d->componentComplete)
+ d->prop = QQmlProperty(d->obj, d->propName);
+ eval();
+}
+
+/*!
+ \qmlproperty string QtQml2::Binding::property
+
+ The property to be updated.
+*/
+QString QQmlBind::property() const
+{
+ Q_D(const QQmlBind);
+ return d->propName;
+}
+
+void QQmlBind::setProperty(const QString &p)
+{
+ Q_D(QQmlBind);
+ if (!d->propName.isEmpty() && d->when.isValid() && d->when) {
+ /* if we switch the property name at runtime, we need to restore the
+ previous binding on the old object before continuing */
+ d->when = false;
+ eval();
+ d->when = true;
+ }
+ d->propName = p;
+ if (d->componentComplete)
+ d->prop = QQmlProperty(d->obj, d->propName);
+ eval();
+}
+
+/*!
+ \qmlproperty any QtQml2::Binding::value
+
+ The value to be set on the target object and property. This can be a
+ constant (which isn't very useful), or a bound expression.
+*/
+QVariant QQmlBind::value() const
+{
+ Q_D(const QQmlBind);
+ return d->value.value;
+}
+
+void QQmlBind::setValue(const QVariant &v)
+{
+ Q_D(QQmlBind);
+ d->value = v;
+ eval();
+}
+
+void QQmlBind::setTarget(const QQmlProperty &p)
+{
+ Q_D(QQmlBind);
+ d->prop = p;
+}
+
+void QQmlBind::classBegin()
+{
+ Q_D(QQmlBind);
+ d->componentComplete = false;
+}
+
+void QQmlBind::componentComplete()
+{
+ Q_D(QQmlBind);
+ d->componentComplete = true;
+ if (!d->prop.isValid())
+ d->prop = QQmlProperty(d->obj, d->propName);
+ eval();
+}
+
+void QQmlBind::eval()
+{
+ Q_D(QQmlBind);
+ if (!d->prop.isValid() || d->value.isNull || !d->componentComplete)
+ return;
+
+ if (d->when.isValid()) {
+ if (!d->when) {
+ //restore any previous binding
+ if (d->prevBind) {
+ QQmlAbstractBinding *tmp = d->prevBind;
+ d->prevBind = 0;
+ tmp = QQmlPropertyPrivate::setBinding(d->prop, tmp);
+ if (tmp) //should this ever be true?
+ tmp->destroy();
+ }
+ return;
+ }
+
+ //save any set binding for restoration
+ QQmlAbstractBinding *tmp;
+ tmp = QQmlPropertyPrivate::setBinding(d->prop, 0);
+ if (tmp && d->prevBind)
+ tmp->destroy();
+ else if (!d->prevBind)
+ d->prevBind = tmp;
+ }
+
+ d->prop.write(d->value.value);
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/types/qqmlbind_p.h b/src/qml/types/qqmlbind_p.h
new file mode 100644
index 0000000000..1e29c257f0
--- /dev/null
+++ b/src/qml/types/qqmlbind_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLBIND_H
+#define QQMLBIND_H
+
+#include <qqml.h>
+
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlBindPrivate;
+class Q_AUTOTEST_EXPORT QQmlBind : public QObject, public QQmlPropertyValueSource, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QQmlBind)
+ Q_INTERFACES(QQmlParserStatus)
+ Q_INTERFACES(QQmlPropertyValueSource)
+ Q_PROPERTY(QObject *target READ object WRITE setObject)
+ Q_PROPERTY(QString property READ property WRITE setProperty)
+ Q_PROPERTY(QVariant value READ value WRITE setValue)
+ Q_PROPERTY(bool when READ when WRITE setWhen)
+
+public:
+ QQmlBind(QObject *parent=0);
+ ~QQmlBind();
+
+ bool when() const;
+ void setWhen(bool);
+
+ QObject *object();
+ void setObject(QObject *);
+
+ QString property() const;
+ void setProperty(const QString &);
+
+ QVariant value() const;
+ void setValue(const QVariant &);
+
+protected:
+ virtual void setTarget(const QQmlProperty &);
+ virtual void classBegin();
+ virtual void componentComplete();
+
+private:
+ void eval();
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQmlBind)
+
+#endif
diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp
new file mode 100644
index 0000000000..286933e557
--- /dev/null
+++ b/src/qml/types/qqmlconnections.cpp
@@ -0,0 +1,321 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlconnections_p.h"
+
+#include <private/qqmlexpression_p.h>
+#include <private/qqmlproperty_p.h>
+#include <private/qqmlboundsignal_p.h>
+#include <qqmlcontext.h>
+#include <private/qqmlcontext_p.h>
+#include <qqmlinfo.h>
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qstringlist.h>
+
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlConnectionsPrivate : public QObjectPrivate
+{
+public:
+ QQmlConnectionsPrivate() : target(0), targetSet(false), ignoreUnknownSignals(false), componentcomplete(true) {}
+
+ QList<QQmlBoundSignal*> boundsignals;
+ QObject *target;
+
+ bool targetSet;
+ bool ignoreUnknownSignals;
+ bool componentcomplete;
+
+ QByteArray data;
+};
+
+/*!
+ \qmltype Connections
+ \instantiates QQmlConnections
+ \inqmlmodule QtQml 2
+ \ingroup qtquick-interceptors
+ \brief Describes generalized connections to signals
+
+ A Connections object creates a connection to a QML signal.
+
+ When connecting to signals in QML, the usual way is to create an
+ "on<Signal>" handler that reacts when a signal is received, like this:
+
+ \qml
+ MouseArea {
+ onClicked: { foo(parameters) }
+ }
+ \endqml
+
+ However, it is not possible to connect to a signal in this way in some
+ cases, such as when:
+
+ \list
+ \li Multiple connections to the same signal are required
+ \li Creating connections outside the scope of the signal sender
+ \li Connecting to targets not defined in QML
+ \endlist
+
+ When any of these are needed, the Connections type can be used instead.
+
+ For example, the above code can be changed to use a Connections object,
+ like this:
+
+ \qml
+ MouseArea {
+ Connections {
+ onClicked: foo(parameters)
+ }
+ }
+ \endqml
+
+ More generally, the Connections object can be a child of some object other than
+ the sender of the signal:
+
+ \qml
+ MouseArea {
+ id: area
+ }
+ // ...
+ \endqml
+ \qml
+ Connections {
+ target: area
+ onClicked: foo(parameters)
+ }
+ \endqml
+
+ \sa QtQml
+*/
+QQmlConnections::QQmlConnections(QObject *parent) :
+ QObject(*(new QQmlConnectionsPrivate), parent)
+{
+}
+
+QQmlConnections::~QQmlConnections()
+{
+}
+
+/*!
+ \qmlproperty Object QtQml2::Connections::target
+ This property holds the object that sends the signal.
+
+ If this property is not set, the \c target defaults to the parent of the Connection.
+
+ If set to null, no connection is made and any signal handlers are ignored
+ until the target is not null.
+*/
+QObject *QQmlConnections::target() const
+{
+ Q_D(const QQmlConnections);
+ return d->targetSet ? d->target : parent();
+}
+
+class QQmlBoundSignalDeleter : public QObject
+{
+public:
+ QQmlBoundSignalDeleter(QQmlBoundSignal *signal) : m_signal(signal) { m_signal->removeFromObject(); }
+ ~QQmlBoundSignalDeleter() { delete m_signal; }
+
+private:
+ QQmlBoundSignal *m_signal;
+};
+
+void QQmlConnections::setTarget(QObject *obj)
+{
+ Q_D(QQmlConnections);
+ d->targetSet = true; // even if setting to 0, it is *set*
+ if (d->target == obj)
+ return;
+ foreach (QQmlBoundSignal *s, d->boundsignals) {
+ // It is possible that target is being changed due to one of our signal
+ // handlers -> use deleteLater().
+ if (s->isEvaluating())
+ (new QQmlBoundSignalDeleter(s))->deleteLater();
+ else
+ delete s;
+ }
+ d->boundsignals.clear();
+ d->target = obj;
+ connectSignals();
+ emit targetChanged();
+}
+
+/*!
+ \qmlproperty bool QtQml2::Connections::ignoreUnknownSignals
+
+ Normally, a connection to a non-existent signal produces runtime errors.
+
+ If this property is set to \c true, such errors are ignored.
+ This is useful if you intend to connect to different types of objects, handling
+ a different set of signals for each object.
+*/
+bool QQmlConnections::ignoreUnknownSignals() const
+{
+ Q_D(const QQmlConnections);
+ return d->ignoreUnknownSignals;
+}
+
+void QQmlConnections::setIgnoreUnknownSignals(bool ignore)
+{
+ Q_D(QQmlConnections);
+ d->ignoreUnknownSignals = ignore;
+}
+
+
+
+QByteArray
+QQmlConnectionsParser::compile(const QList<QQmlCustomParserProperty> &props)
+{
+ QByteArray rv;
+ QDataStream ds(&rv, QIODevice::WriteOnly);
+
+ for(int ii = 0; ii < props.count(); ++ii)
+ {
+ QString propName = props.at(ii).name();
+ int propLine = props.at(ii).location().line;
+ int propColumn = props.at(ii).location().column;
+
+ if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) {
+ error(props.at(ii), QQmlConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName));
+ return QByteArray();
+ }
+
+ QList<QVariant> values = props.at(ii).assignedValues();
+
+ for (int i = 0; i < values.count(); ++i) {
+ const QVariant &value = values.at(i);
+
+ if (value.userType() == qMetaTypeId<QQmlCustomParserNode>()) {
+ error(props.at(ii), QQmlConnections::tr("Connections: nested objects not allowed"));
+ return QByteArray();
+ } else if (value.userType() == qMetaTypeId<QQmlCustomParserProperty>()) {
+ error(props.at(ii), QQmlConnections::tr("Connections: syntax error"));
+ return QByteArray();
+ } else {
+ QQmlScript::Variant v = qvariant_cast<QQmlScript::Variant>(value);
+ if (v.isScript()) {
+ ds << propName;
+ ds << rewriteSignalHandler(v, propName).toUtf8();
+ ds << propLine;
+ ds << propColumn;
+ } else {
+ error(props.at(ii), QQmlConnections::tr("Connections: script expected"));
+ return QByteArray();
+ }
+ }
+ }
+ }
+
+ return rv;
+}
+
+void QQmlConnectionsParser::setCustomData(QObject *object,
+ const QByteArray &data)
+{
+ QQmlConnectionsPrivate *p =
+ static_cast<QQmlConnectionsPrivate *>(QObjectPrivate::get(object));
+ p->data = data;
+}
+
+
+void QQmlConnections::connectSignals()
+{
+ Q_D(QQmlConnections);
+ if (!d->componentcomplete || (d->targetSet && !target()))
+ return;
+
+ QDataStream ds(d->data);
+ while (!ds.atEnd()) {
+ QString propName;
+ ds >> propName;
+ QByteArray script;
+ ds >> script;
+ int line;
+ ds >> line;
+ int column;
+ ds >> column;
+
+ QQmlProperty prop(target(), propName);
+ if (prop.isValid() && (prop.type() & QQmlProperty::SignalProperty)) {
+ int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex();
+ QQmlBoundSignal *signal =
+ new QQmlBoundSignal(target(), signalIndex, this, qmlEngine(this));
+
+ QString location;
+ QQmlContextData *ctxtdata = 0;
+ QQmlData *ddata = QQmlData::get(this);
+ if (ddata) {
+ ctxtdata = ddata->outerContext;
+ if (ctxtdata && !ctxtdata->url.isEmpty())
+ location = ddata->outerContext->urlString;
+ }
+
+ QQmlBoundSignalExpression *expression = ctxtdata ?
+ new QQmlBoundSignalExpression(target(), signalIndex,
+ ctxtdata, this, script,
+ true, location, line, column) : 0;
+ signal->takeExpression(expression);
+ d->boundsignals += signal;
+ } else {
+ if (!d->ignoreUnknownSignals)
+ qmlInfo(this) << tr("Cannot assign to non-existent property \"%1\"").arg(propName);
+ }
+ }
+}
+
+void QQmlConnections::classBegin()
+{
+ Q_D(QQmlConnections);
+ d->componentcomplete=false;
+}
+
+void QQmlConnections::componentComplete()
+{
+ Q_D(QQmlConnections);
+ d->componentcomplete=true;
+ connectSignals();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/types/qqmlconnections_p.h b/src/qml/types/qqmlconnections_p.h
new file mode 100644
index 0000000000..9bc668e5f4
--- /dev/null
+++ b/src/qml/types/qqmlconnections_p.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLCONNECTIONS_H
+#define QQMLCONNECTIONS_H
+
+#include <qqml.h>
+#include <private/qqmlcustomparser_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlBoundSignal;
+class QQmlContext;
+class QQmlConnectionsPrivate;
+class Q_AUTOTEST_EXPORT QQmlConnections : public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QQmlConnections)
+
+ Q_INTERFACES(QQmlParserStatus)
+ Q_PROPERTY(QObject *target READ target WRITE setTarget NOTIFY targetChanged)
+ Q_PROPERTY(bool ignoreUnknownSignals READ ignoreUnknownSignals WRITE setIgnoreUnknownSignals)
+
+public:
+ QQmlConnections(QObject *parent=0);
+ ~QQmlConnections();
+
+ QObject *target() const;
+ void setTarget(QObject *);
+
+ bool ignoreUnknownSignals() const;
+ void setIgnoreUnknownSignals(bool ignore);
+
+Q_SIGNALS:
+ void targetChanged();
+
+private:
+ void connectSignals();
+ void classBegin();
+ void componentComplete();
+};
+
+class QQmlConnectionsParser : public QQmlCustomParser
+{
+public:
+ virtual QByteArray compile(const QList<QQmlCustomParserProperty> &);
+ virtual void setCustomData(QObject *, const QByteArray &);
+};
+
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQmlConnections)
+
+#endif
diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp
new file mode 100644
index 0000000000..efbd98bdbc
--- /dev/null
+++ b/src/qml/types/qqmldelegatemodel.cpp
@@ -0,0 +1,3189 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmldelegatemodel_p_p.h"
+
+#include <QtQml/qqmlinfo.h>
+
+#include <private/qquickpackage_p.h>
+#include <private/qmetaobjectbuilder_p.h>
+#include <private/qqmladaptormodel_p.h>
+#include <private/qqmlchangeset_p.h>
+#include <private/qqmlengine_p.h>
+#include <private/qqmlcomponent_p.h>
+#include <private/qqmlincubator_p.h>
+#include <private/qqmlcompiler_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlDelegateModelEngineData : public QV8Engine::Deletable
+{
+public:
+ enum
+ {
+ Model,
+ Groups,
+ IsUnresolved,
+ ItemsIndex,
+ PersistedItemsIndex,
+ InItems,
+ InPersistedItems,
+ StringCount
+ };
+
+ QQmlDelegateModelEngineData(QV8Engine *engine);
+ ~QQmlDelegateModelEngineData();
+
+ v8::Local<v8::Object> array(
+ QV8Engine *engine, const QVector<QQmlChangeSet::Remove> &changes);
+ v8::Local<v8::Object> array(
+ QV8Engine *engine, const QVector<QQmlChangeSet::Insert> &changes);
+ v8::Local<v8::Object> array(
+ QV8Engine *engine, const QVector<QQmlChangeSet::Change> &changes);
+
+
+ inline v8::Local<v8::String> model() { return strings->Get(Model)->ToString(); }
+ inline v8::Local<v8::String> groups() { return strings->Get(Groups)->ToString(); }
+ inline v8::Local<v8::String> isUnresolved() { return strings->Get(IsUnresolved)->ToString(); }
+ inline v8::Local<v8::String> itemsIndex() { return strings->Get(ItemsIndex)->ToString(); }
+ inline v8::Local<v8::String> persistedItemsIndex() { return strings->Get(PersistedItemsIndex)->ToString(); }
+ inline v8::Local<v8::String> inItems() { return strings->Get(InItems)->ToString(); }
+ inline v8::Local<v8::String> inPersistedItems() { return strings->Get(InPersistedItems)->ToString(); }
+
+ v8::Persistent<v8::Array> strings;
+ v8::Persistent<v8::Function> constructorChange;
+ v8::Persistent<v8::Function> constructorChangeArray;
+};
+
+V8_DEFINE_EXTENSION(QQmlDelegateModelEngineData, engineData)
+
+
+void QQmlDelegateModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop)
+{
+ prop.setWritable(false);
+}
+
+QVariant QQmlDelegateModelPartsMetaObject::initialValue(int id)
+{
+ QQmlDelegateModelParts *parts = static_cast<QQmlDelegateModelParts *>(object());
+ QQmlPartsModel *m = new QQmlPartsModel(
+ parts->model, QString::fromUtf8(name(id)), parts);
+ parts->models.append(m);
+ return QVariant::fromValue(static_cast<QObject *>(m));
+}
+
+QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent)
+: QObject(parent), model(parent)
+{
+ new QQmlDelegateModelPartsMetaObject(this);
+}
+
+//---------------------------------------------------------------------------
+
+/*!
+ \qmltype VisualDataModel
+ \instantiates QQmlDelegateModel
+ \inqmlmodule QtQuick 2
+ \ingroup qtquick-models
+ \brief Encapsulates a model and delegate
+
+ The VisualDataModel type encapsulates a model and the delegate that will
+ be instantiated for items in a model.
+
+ This type is provided by QtQuick 2 for compatibility reasons. The same implementation
+ is now primarily available as DelegateModel in the QtQml.Models module.
+
+ \sa {QtQml.Models2::DelegateModel}
+*/
+/*!
+ \qmltype DelegateModel
+ \instantiates QQmlDelegateModel
+ \inqmlmodule QtQml.Models 2
+ \brief Encapsulates a model and delegate
+
+ The DelegateModel type encapsulates a model and the delegate that will
+ be instantiated for items in the model.
+
+ This element is also available as DelegateModel in the QtQuick module. For full details,
+ see the \l DelegateModel documentation.
+
+ The DelegateModel type encapsulates a model and the delegate that will
+ be instantiated for items in the model.
+
+ It is usually not necessary to create a DelegateModel.
+ However, it can be useful for manipulating and accessing the \l modelIndex
+ when a QAbstractItemModel subclass is used as the
+ model. Also, DelegateModel is used together with \l Package to
+ provide delegates to multiple views, and with DelegateModelGroup to sort and filter
+ delegate items.
+
+ The example below illustrates using a DelegateModel with a ListView.
+
+ \snippet qml/visualdatamodel.qml 0
+*/
+
+QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt)
+ : m_delegate(0)
+ , m_cacheMetaType(0)
+ , m_context(ctxt)
+ , m_parts(0)
+ , m_filterGroup(QStringLiteral("items"))
+ , m_count(0)
+ , m_groupCount(Compositor::MinimumGroupCount)
+ , m_compositorGroup(Compositor::Cache)
+ , m_complete(false)
+ , m_delegateValidated(false)
+ , m_reset(false)
+ , m_transaction(false)
+ , m_incubatorCleanupScheduled(false)
+ , m_cacheItems(0)
+ , m_items(0)
+ , m_persistedItems(0)
+{
+}
+
+QQmlDelegateModelPrivate::~QQmlDelegateModelPrivate()
+{
+ qDeleteAll(m_finishedIncubating);
+
+ if (m_cacheMetaType)
+ m_cacheMetaType->release();
+}
+
+void QQmlDelegateModelPrivate::init()
+{
+ Q_Q(QQmlDelegateModel);
+ m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag);
+
+ m_items = new QQmlDelegateModelGroup(QStringLiteral("items"), q, Compositor::Default, q);
+ m_items->setDefaultInclude(true);
+ m_persistedItems = new QQmlDelegateModelGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q);
+ QQmlDelegateModelGroupPrivate::get(m_items)->emitters.insert(this);
+}
+
+QQmlDelegateModel::QQmlDelegateModel()
+: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(0)))
+{
+ Q_D(QQmlDelegateModel);
+ d->init();
+}
+
+QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent)
+: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(ctxt)), parent)
+{
+ Q_D(QQmlDelegateModel);
+ d->init();
+}
+
+QQmlDelegateModel::~QQmlDelegateModel()
+{
+ Q_D(QQmlDelegateModel);
+
+ foreach (QQmlDelegateModelItem *cacheItem, d->m_cache) {
+ if (cacheItem->object) {
+ delete cacheItem->object;
+
+ cacheItem->object = 0;
+ cacheItem->contextData->destroy();
+ cacheItem->contextData = 0;
+ cacheItem->scriptRef -= 1;
+ }
+ cacheItem->groups &= ~Compositor::UnresolvedFlag;
+ cacheItem->objectRef = 0;
+ if (!cacheItem->isReferenced())
+ delete cacheItem;
+ }
+}
+
+
+void QQmlDelegateModel::classBegin()
+{
+ Q_D(QQmlDelegateModel);
+ if (!d->m_context)
+ d->m_context = qmlContext(this);
+}
+
+void QQmlDelegateModel::componentComplete()
+{
+ Q_D(QQmlDelegateModel);
+ d->m_complete = true;
+
+ int defaultGroups = 0;
+ QStringList groupNames;
+ groupNames.append(QStringLiteral("items"));
+ groupNames.append(QStringLiteral("persistedItems"));
+ if (QQmlDelegateModelGroupPrivate::get(d->m_items)->defaultInclude)
+ defaultGroups |= Compositor::DefaultFlag;
+ if (QQmlDelegateModelGroupPrivate::get(d->m_persistedItems)->defaultInclude)
+ defaultGroups |= Compositor::PersistedFlag;
+ for (int i = Compositor::MinimumGroupCount; i < d->m_groupCount; ++i) {
+ QString name = d->m_groups[i]->name();
+ if (name.isEmpty()) {
+ d->m_groups[i] = d->m_groups[d->m_groupCount - 1];
+ --d->m_groupCount;
+ --i;
+ } else if (name.at(0).isUpper()) {
+ qmlInfo(d->m_groups[i]) << QQmlDelegateModelGroup::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);
+
+ QQmlDelegateModelGroupPrivate *group = QQmlDelegateModelGroupPrivate::get(d->m_groups[i]);
+ group->setModel(this, Compositor::Group(i));
+ if (group->defaultInclude)
+ defaultGroups |= (1 << i);
+ }
+ }
+
+ d->m_cacheMetaType = new QQmlDelegateModelItemMetaType(
+ QQmlEnginePrivate::getV8Engine(d->m_context->engine()), this, groupNames);
+
+ d->m_compositor.setGroupCount(d->m_groupCount);
+ d->m_compositor.setDefaultGroups(defaultGroups);
+ d->updateFilterGroup();
+
+ while (!d->m_pendingParts.isEmpty())
+ static_cast<QQmlPartsModel *>(d->m_pendingParts.first())->updateFilterGroup();
+
+ QVector<Compositor::Insert> inserts;
+ d->m_count = d->m_adaptorModel.count();
+ d->m_compositor.append(
+ &d->m_adaptorModel,
+ 0,
+ d->m_count,
+ defaultGroups | Compositor::AppendFlag | Compositor::PrependFlag,
+ &inserts);
+ d->itemsInserted(inserts);
+ d->emitChanges();
+
+ if (d->m_adaptorModel.canFetchMore())
+ QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
+}
+
+/*!
+ \qmlproperty model QtQml.Models2::DelegateModel::model
+ This property holds the model providing data for the DelegateModel.
+
+ The model provides a set of data that is used to create the items
+ for a view. For large or dynamic datasets the model is usually
+ provided by a C++ model object. The C++ model object must be a \l
+ {QAbstractItemModel} subclass or a simple list.
+
+ Models can also be created directly in QML, using a \l{ListModel} or
+ \l{XmlListModel}.
+
+ \sa {qml-data-models}{Data Models}
+*/
+QVariant QQmlDelegateModel::model() const
+{
+ Q_D(const QQmlDelegateModel);
+ return d->m_adaptorModel.model();
+}
+
+void QQmlDelegateModel::setModel(const QVariant &model)
+{
+ Q_D(QQmlDelegateModel);
+
+ if (d->m_complete)
+ _q_itemsRemoved(0, d->m_count);
+
+ d->m_adaptorModel.setModel(model, this, d->m_context->engine());
+ d->m_adaptorModel.replaceWatchedRoles(QList<QByteArray>(), d->m_watchedRoles);
+ for (int i = 0; d->m_parts && i < d->m_parts->models.count(); ++i) {
+ d->m_adaptorModel.replaceWatchedRoles(
+ QList<QByteArray>(), d->m_parts->models.at(i)->watchedRoles());
+ }
+
+ if (d->m_complete) {
+ _q_itemsInserted(0, d->m_adaptorModel.count());
+ if (d->m_adaptorModel.canFetchMore())
+ QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
+ }
+}
+
+/*!
+ \qmlproperty Component QtQml.Models2::DelegateModel::delegate
+
+ The delegate provides a template defining each item instantiated by a view.
+ The index is exposed as an accessible \c index property. Properties of the
+ model are also available depending upon the type of \l {qml-data-models}{Data Model}.
+*/
+QQmlComponent *QQmlDelegateModel::delegate() const
+{
+ Q_D(const QQmlDelegateModel);
+ return d->m_delegate;
+}
+
+void QQmlDelegateModel::setDelegate(QQmlComponent *delegate)
+{
+ Q_D(QQmlDelegateModel);
+ if (d->m_transaction) {
+ qmlInfo(this) << tr("The delegate of a DelegateModel cannot be changed within onUpdated.");
+ return;
+ }
+ bool wasValid = d->m_delegate != 0;
+ d->m_delegate = delegate;
+ d->m_delegateValidated = false;
+ if (wasValid && d->m_complete) {
+ for (int i = 1; i < d->m_groupCount; ++i) {
+ QQmlDelegateModelGroupPrivate::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) {
+ QQmlDelegateModelGroupPrivate::get(d->m_groups[i])->changeSet.insert(
+ 0, d->m_compositor.count(Compositor::Group(i)));
+ }
+ }
+ d->emitChanges();
+}
+
+/*!
+ \qmlproperty QModelIndex QtQml.Models2::DelegateModel::rootIndex
+
+ QAbstractItemModel provides a hierarchical tree of data, whereas
+ QML only operates on list data. \c rootIndex allows the children of
+ any node in a QAbstractItemModel to be provided by this model.
+
+ This property only affects models of type QAbstractItemModel that
+ are hierarchical (e.g, a tree model).
+
+ For example, here is a simple interactive file system browser.
+ When a directory name is clicked, the view's \c rootIndex is set to the
+ QModelIndex node of the clicked directory, thus updating the view to show
+ the new directory's contents.
+
+ \c main.cpp:
+ \snippet qml/visualdatamodel_rootindex/main.cpp 0
+
+ \c view.qml:
+ \snippet qml/visualdatamodel_rootindex/view.qml 0
+
+ If the \l model is a QAbstractItemModel subclass, the delegate can also
+ reference a \c hasModelChildren property (optionally qualified by a
+ \e model. prefix) that indicates whether the delegate's model item has
+ any child nodes.
+
+
+ \sa modelIndex(), parentModelIndex()
+*/
+QVariant QQmlDelegateModel::rootIndex() const
+{
+ Q_D(const QQmlDelegateModel);
+ return QVariant::fromValue(QModelIndex(d->m_adaptorModel.rootIndex));
+}
+
+void QQmlDelegateModel::setRootIndex(const QVariant &root)
+{
+ Q_D(QQmlDelegateModel);
+
+ QModelIndex modelIndex = qvariant_cast<QModelIndex>(root);
+ const bool changed = d->m_adaptorModel.rootIndex != modelIndex;
+ if (changed || !d->m_adaptorModel.isValid()) {
+ const int oldCount = d->m_count;
+ d->m_adaptorModel.rootIndex = modelIndex;
+ if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) // The previous root index was invalidated, so we need to reconnect the model.
+ d->m_adaptorModel.setModel(d->m_adaptorModel.list.list(), this, d->m_context->engine());
+ if (d->m_adaptorModel.canFetchMore())
+ d->m_adaptorModel.fetchMore();
+ if (d->m_complete) {
+ const int newCount = d->m_adaptorModel.count();
+ if (oldCount)
+ _q_itemsRemoved(0, oldCount);
+ if (newCount)
+ _q_itemsInserted(0, newCount);
+ }
+ if (changed)
+ emit rootIndexChanged();
+ }
+}
+
+/*!
+ \qmlmethod QModelIndex QtQml.Models2::DelegateModel::modelIndex(int index)
+
+ QAbstractItemModel provides a hierarchical tree of data, whereas
+ QML only operates on list data. This function assists in using
+ tree models in QML.
+
+ Returns a QModelIndex for the specified index.
+ This value can be assigned to rootIndex.
+
+ \sa rootIndex
+*/
+QVariant QQmlDelegateModel::modelIndex(int idx) const
+{
+ Q_D(const QQmlDelegateModel);
+ return d->m_adaptorModel.modelIndex(idx);
+}
+
+/*!
+ \qmlmethod QModelIndex QtQml.Models2::DelegateModel::parentModelIndex()
+
+ QAbstractItemModel provides a hierarchical tree of data, whereas
+ QML only operates on list data. This function assists in using
+ tree models in QML.
+
+ Returns a QModelIndex for the parent of the current rootIndex.
+ This value can be assigned to rootIndex.
+
+ \sa rootIndex
+*/
+QVariant QQmlDelegateModel::parentModelIndex() const
+{
+ Q_D(const QQmlDelegateModel);
+ return d->m_adaptorModel.parentModelIndex();
+}
+
+/*!
+ \qmlproperty int QtQml.Models2::DelegateModel::count
+*/
+
+int QQmlDelegateModel::count() const
+{
+ Q_D(const QQmlDelegateModel);
+ if (!d->m_delegate)
+ return 0;
+ return d->m_compositor.count(d->m_compositorGroup);
+}
+
+QQmlDelegateModel::ReleaseFlags QQmlDelegateModelPrivate::release(QObject *object)
+{
+ QQmlDelegateModel::ReleaseFlags stat = 0;
+ if (!object)
+ return stat;
+
+ if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object)) {
+ if (cacheItem->releaseObject()) {
+ cacheItem->destroyObject();
+ emitDestroyingItem(object);
+ if (cacheItem->incubationTask) {
+ releaseIncubator(cacheItem->incubationTask);
+ cacheItem->incubationTask = 0;
+ }
+ cacheItem->Dispose();
+ stat |= QQmlInstanceModel::Destroyed;
+ } else {
+ stat |= QQmlDelegateModel::Referenced;
+ }
+ }
+ return stat;
+}
+
+/*
+ Returns ReleaseStatus flags.
+*/
+
+QQmlDelegateModel::ReleaseFlags QQmlDelegateModel::release(QObject *item)
+{
+ Q_D(QQmlDelegateModel);
+ QQmlInstanceModel::ReleaseFlags stat = d->release(item);
+ return stat;
+}
+
+// Cancel a requested async item
+void QQmlDelegateModel::cancel(int index)
+{
+ Q_D(QQmlDelegateModel);
+ if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) {
+ qWarning() << "DelegateModel::cancel: index out range" << index << d->m_compositor.count(d->m_compositorGroup);
+ return;
+ }
+
+ Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index);
+ QQmlDelegateModelItem *cacheItem = it->inCache() ? d->m_cache.at(it.cacheIndex) : 0;
+ if (cacheItem) {
+ if (cacheItem->incubationTask && !cacheItem->isObjectReferenced()) {
+ d->releaseIncubator(cacheItem->incubationTask);
+ cacheItem->incubationTask = 0;
+
+ if (cacheItem->object) {
+ QObject *object = cacheItem->object;
+ cacheItem->destroyObject();
+ if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object))
+ d->emitDestroyingPackage(package);
+ else
+ d->emitDestroyingItem(object);
+ }
+
+ cacheItem->scriptRef -= 1;
+ }
+ if (!cacheItem->isReferenced()) {
+ d->m_compositor.clearFlags(Compositor::Cache, it.cacheIndex, 1, Compositor::CacheFlag);
+ d->m_cache.removeAt(it.cacheIndex);
+ delete cacheItem;
+ Q_ASSERT(d->m_cache.count() == d->m_compositor.count(Compositor::Cache));
+ }
+ }
+}
+
+void QQmlDelegateModelPrivate::group_append(
+ QQmlListProperty<QQmlDelegateModelGroup> *property, QQmlDelegateModelGroup *group)
+{
+ QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data);
+ if (d->m_complete)
+ return;
+ if (d->m_groupCount == Compositor::MaximumGroupCount) {
+ qmlInfo(d->q_func()) << QQmlDelegateModel::tr("The maximum number of supported DelegateModelGroups is 8");
+ return;
+ }
+ d->m_groups[d->m_groupCount] = group;
+ d->m_groupCount += 1;
+}
+
+int QQmlDelegateModelPrivate::group_count(
+ QQmlListProperty<QQmlDelegateModelGroup> *property)
+{
+ QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data);
+ return d->m_groupCount - 1;
+}
+
+QQmlDelegateModelGroup *QQmlDelegateModelPrivate::group_at(
+ QQmlListProperty<QQmlDelegateModelGroup> *property, int index)
+{
+ QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data);
+ return index >= 0 && index < d->m_groupCount - 1
+ ? d->m_groups[index + 1]
+ : 0;
+}
+
+/*!
+ \qmlproperty list<DelegateModelGroup> QtQml.Models2::DelegateModel::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 DelegateModel two attached properties are added to each
+ delegate item. The first of the form DelegateModel.in\e{GroupName} holds whether the
+ item belongs to the group and the second DelegateModel.\e{groupName}Index holds the
+ index of the item in that group.
+
+ The following example illustrates using groups to select items in a model.
+
+ \snippet qml/visualdatagroup.qml 0
+*/
+
+QQmlListProperty<QQmlDelegateModelGroup> QQmlDelegateModel::groups()
+{
+ Q_D(QQmlDelegateModel);
+ return QQmlListProperty<QQmlDelegateModelGroup>(
+ this,
+ d,
+ QQmlDelegateModelPrivate::group_append,
+ QQmlDelegateModelPrivate::group_count,
+ QQmlDelegateModelPrivate::group_at,
+ 0);
+}
+
+/*!
+ \qmlproperty DelegateModelGroup QtQml.Models2::DelegateModel::items
+
+ This property holds visual data model's default group to which all new items are added.
+*/
+
+QQmlDelegateModelGroup *QQmlDelegateModel::items()
+{
+ Q_D(QQmlDelegateModel);
+ return d->m_items;
+}
+
+/*!
+ \qmlproperty DelegateModelGroup QtQml.Models2::DelegateModel::persistedItems
+
+ This property holds visual data model's persisted items group.
+
+ Items in this group are not destroyed when released by a view, instead they are persisted
+ until removed from the group.
+
+ An item can be removed from the persistedItems group by setting the
+ DelegateModel.inPersistedItems property to false. If the item is not referenced by a view
+ at that time it will be destroyed. Adding an item to this group will not create a new
+ instance.
+
+ Items returned by the \l QtQml.Models2::DelegateModelGroup::create() function are automatically added
+ to this group.
+*/
+
+QQmlDelegateModelGroup *QQmlDelegateModel::persistedItems()
+{
+ Q_D(QQmlDelegateModel);
+ return d->m_persistedItems;
+}
+
+/*!
+ \qmlproperty string QtQml.Models2::DelegateModel::filterOnGroup
+
+ This property holds the name of the group used to filter the visual data model.
+
+ Only items which belong to this group are visible to a view.
+
+ By default this is the \l items group.
+*/
+
+QString QQmlDelegateModel::filterGroup() const
+{
+ Q_D(const QQmlDelegateModel);
+ return d->m_filterGroup;
+}
+
+void QQmlDelegateModel::setFilterGroup(const QString &group)
+{
+ Q_D(QQmlDelegateModel);
+
+ if (d->m_transaction) {
+ qmlInfo(this) << tr("The group of a DelegateModel cannot be changed within onChanged");
+ return;
+ }
+
+ if (d->m_filterGroup != group) {
+ d->m_filterGroup = group;
+ d->updateFilterGroup();
+ emit filterGroupChanged();
+ }
+}
+
+void QQmlDelegateModel::resetFilterGroup()
+{
+ setFilterGroup(QStringLiteral("items"));
+}
+
+void QQmlDelegateModelPrivate::updateFilterGroup()
+{
+ Q_Q(QQmlDelegateModel);
+ if (!m_cacheMetaType)
+ return;
+
+ QQmlListCompositor::Group previousGroup = m_compositorGroup;
+ m_compositorGroup = Compositor::Default;
+ for (int i = 1; i < m_groupCount; ++i) {
+ if (m_filterGroup == m_cacheMetaType->groupNames.at(i - 1)) {
+ m_compositorGroup = Compositor::Group(i);
+ break;
+ }
+ }
+
+ QQmlDelegateModelGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this);
+ if (m_compositorGroup != previousGroup) {
+ QVector<QQmlChangeSet::Remove> removes;
+ QVector<QQmlChangeSet::Insert> inserts;
+ m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts);
+
+ QQmlChangeSet changeSet;
+ changeSet.move(removes, inserts);
+ emit q->modelUpdated(changeSet, false);
+
+ if (changeSet.difference() != 0)
+ emit q->countChanged();
+
+ if (m_parts) {
+ foreach (QQmlPartsModel *model, m_parts->models)
+ model->updateFilterGroup(m_compositorGroup, changeSet);
+ }
+ }
+}
+
+/*!
+ \qmlproperty object QtQml.Models2::DelegateModel::parts
+
+ The \a parts property selects a DelegateModel which creates
+ delegates from the part named. This is used in conjunction with
+ the \l Package type.
+
+ For example, the code below selects a model which creates
+ delegates named \e list from a \l Package:
+
+ \code
+ DelegateModel {
+ id: visualModel
+ delegate: Package {
+ Item { Package.name: "list" }
+ }
+ model: myModel
+ }
+
+ ListView {
+ width: 200; height:200
+ model: visualModel.parts.list
+ }
+ \endcode
+
+ \sa Package
+*/
+
+QObject *QQmlDelegateModel::parts()
+{
+ Q_D(QQmlDelegateModel);
+ if (!d->m_parts)
+ d->m_parts = new QQmlDelegateModelParts(this);
+ return d->m_parts;
+}
+
+void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package)
+{
+ for (int i = 1; i < m_groupCount; ++i)
+ QQmlDelegateModelGroupPrivate::get(m_groups[i])->createdPackage(incubationTask->index[i], package);
+}
+
+void QQmlDelegateModelPrivate::emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package)
+{
+ for (int i = 1; i < m_groupCount; ++i)
+ QQmlDelegateModelGroupPrivate::get(m_groups[i])->initPackage(incubationTask->index[i], package);
+}
+
+void QQmlDelegateModelPrivate::emitDestroyingPackage(QQuickPackage *package)
+{
+ for (int i = 1; i < m_groupCount; ++i)
+ QQmlDelegateModelGroupPrivate::get(m_groups[i])->destroyingPackage(package);
+}
+
+void QQDMIncubationTask::statusChanged(Status status)
+{
+ vdm->incubatorStatusChanged(this, status);
+}
+
+void QQmlDelegateModelPrivate::releaseIncubator(QQDMIncubationTask *incubationTask)
+{
+ Q_Q(QQmlDelegateModel);
+ if (!incubationTask->isError())
+ incubationTask->clear();
+ m_finishedIncubating.append(incubationTask);
+ if (!m_incubatorCleanupScheduled) {
+ m_incubatorCleanupScheduled = true;
+ QCoreApplication::postEvent(q, new QEvent(QEvent::User));
+ }
+}
+
+void QQmlDelegateModelPrivate::removeCacheItem(QQmlDelegateModelItem *cacheItem)
+{
+ int cidx = m_cache.indexOf(cacheItem);
+ if (cidx >= 0) {
+ m_compositor.clearFlags(Compositor::Cache, cidx, 1, Compositor::CacheFlag);
+ m_cache.removeAt(cidx);
+ }
+ Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache));
+}
+
+void QQmlDelegateModelPrivate::incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status)
+{
+ Q_Q(QQmlDelegateModel);
+ if (status != QQmlIncubator::Ready && status != QQmlIncubator::Error)
+ return;
+
+ QQmlDelegateModelItem *cacheItem = incubationTask->incubating;
+ cacheItem->incubationTask = 0;
+ incubationTask->incubating = 0;
+ releaseIncubator(incubationTask);
+
+ if (status == QQmlIncubator::Ready) {
+ if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object))
+ emitCreatedPackage(incubationTask, package);
+ else
+ emitCreatedItem(incubationTask, cacheItem->object);
+ } else if (status == QQmlIncubator::Error) {
+ qmlInfo(q, m_delegate->errors()) << "Error creating delegate";
+ }
+
+ if (!cacheItem->isObjectReferenced()) {
+ if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object))
+ emitDestroyingPackage(package);
+ else
+ emitDestroyingItem(cacheItem->object);
+ delete cacheItem->object;
+ cacheItem->object = 0;
+ cacheItem->scriptRef -= 1;
+ cacheItem->contextData->destroy();
+ cacheItem->contextData = 0;
+ if (!cacheItem->isReferenced()) {
+ removeCacheItem(cacheItem);
+ delete cacheItem;
+ }
+ }
+}
+
+void QQDMIncubationTask::setInitialState(QObject *o)
+{
+ vdm->setInitialState(this, o);
+}
+
+void QQmlDelegateModelPrivate::setInitialState(QQDMIncubationTask *incubationTask, QObject *o)
+{
+ QQmlDelegateModelItem *cacheItem = incubationTask->incubating;
+ cacheItem->object = o;
+
+ if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object))
+ emitInitPackage(incubationTask, package);
+ else
+ emitInitItem(incubationTask, cacheItem->object);
+}
+
+QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, bool asynchronous)
+{
+ Q_Q(QQmlDelegateModel);
+ if (!m_delegate || index < 0 || index >= m_compositor.count(group)) {
+ qWarning() << "DelegateModel::item: index out range" << index << m_compositor.count(group);
+ return 0;
+ } else if (!m_context->isValid()) {
+ return 0;
+ }
+
+ Compositor::iterator it = m_compositor.find(group, index);
+
+ QQmlDelegateModelItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0;
+
+ if (!cacheItem) {
+ cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), it.modelIndex());
+ if (!cacheItem)
+ return 0;
+
+ cacheItem->groups = it->flags;
+
+ m_cache.insert(it.cacheIndex, cacheItem);
+ m_compositor.setFlags(it, 1, Compositor::CacheFlag);
+ Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache));
+ }
+
+ // Bump the reference counts temporarily so neither the content data or the delegate object
+ // are deleted if incubatorStatusChanged() is called synchronously.
+ cacheItem->scriptRef += 1;
+ cacheItem->referenceObject();
+
+ if (cacheItem->incubationTask) {
+ if (!asynchronous && cacheItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) {
+ // previously requested async - now needed immediately
+ cacheItem->incubationTask->forceCompletion();
+ }
+ } else if (!cacheItem->object) {
+ QQmlContext *creationContext = m_delegate->creationContext();
+
+ cacheItem->scriptRef += 1;
+
+ cacheItem->incubationTask = new QQDMIncubationTask(this, asynchronous ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested);
+ cacheItem->incubationTask->incubating = cacheItem;
+ cacheItem->incubationTask->clear();
+
+ for (int i = 1; i < m_groupCount; ++i)
+ cacheItem->incubationTask->index[i] = it.index[i];
+
+ QQmlContextData *ctxt = new QQmlContextData;
+ ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_context));
+ ctxt->contextObject = cacheItem;
+ cacheItem->contextData = ctxt;
+
+ if (m_adaptorModel.hasProxyObject()) {
+ if (QQmlAdaptorModelProxyInterface *proxy
+ = qobject_cast<QQmlAdaptorModelProxyInterface *>(cacheItem)) {
+ ctxt = new QQmlContextData;
+ ctxt->setParent(cacheItem->contextData, true);
+ ctxt->contextObject = proxy->proxiedObject();
+ }
+ }
+
+ cacheItem->incubateObject(
+ m_delegate,
+ m_context->engine(),
+ ctxt,
+ QQmlContextData::get(m_context));
+ }
+
+ if (index == m_compositor.count(group) - 1 && m_adaptorModel.canFetchMore())
+ QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest));
+
+ // Remove the temporary reference count.
+ cacheItem->scriptRef -= 1;
+ if (cacheItem->object)
+ return cacheItem->object;
+
+ cacheItem->releaseObject();
+ if (!cacheItem->isReferenced()) {
+ removeCacheItem(cacheItem);
+ delete cacheItem;
+ }
+
+ return 0;
+}
+
+/*
+ If asynchronous is true or the component is being loaded asynchronously due
+ to an ancestor being loaded asynchronously, item() may return 0. In this
+ case itemCreated() will be emitted when the item is available. The item
+ at this stage does not have any references, so item() must be called again
+ to ensure a reference is held. Any call to item() which returns a valid item
+ must be matched by a call to release() in order to destroy the item.
+*/
+QObject *QQmlDelegateModel::object(int index, bool asynchronous)
+{
+ Q_D(QQmlDelegateModel);
+ if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) {
+ qWarning() << "DelegateModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup);
+ return 0;
+ }
+
+ QObject *object = d->object(d->m_compositorGroup, index, asynchronous);
+ if (!object)
+ return 0;
+
+ return object;
+}
+
+QString QQmlDelegateModelPrivate::stringValue(Compositor::Group group, int index, const QString &name)
+{
+ Compositor::iterator it = m_compositor.find(group, index);
+ if (QQmlAdaptorModel *model = it.list<QQmlAdaptorModel>()) {
+ QString role = name;
+ int dot = name.indexOf(QLatin1Char('.'));
+ if (dot > 0)
+ role = name.left(dot);
+ QVariant value = model->value(it.modelIndex(), role);
+ while (dot > 0) {
+ QObject *obj = qvariant_cast<QObject*>(value);
+ if (!obj)
+ return QString();
+ int from = dot+1;
+ dot = name.indexOf(QLatin1Char('.'), from);
+ value = obj->property(name.mid(from, dot-from).toUtf8());
+ }
+ return value.toString();
+ }
+ return QString();
+}
+
+QString QQmlDelegateModel::stringValue(int index, const QString &name)
+{
+ Q_D(QQmlDelegateModel);
+ return d->stringValue(d->m_compositorGroup, index, name);
+}
+
+int QQmlDelegateModel::indexOf(QObject *item, QObject *) const
+{
+ Q_D(const QQmlDelegateModel);
+ if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(item))
+ return cacheItem->groupIndex(d->m_compositorGroup);
+ return -1;
+}
+
+void QQmlDelegateModel::setWatchedRoles(QList<QByteArray> roles)
+{
+ Q_D(QQmlDelegateModel);
+ d->m_adaptorModel.replaceWatchedRoles(d->m_watchedRoles, roles);
+ d->m_watchedRoles = roles;
+}
+
+void QQmlDelegateModelPrivate::addGroups(
+ Compositor::iterator from, int count, Compositor::Group group, int groupFlags)
+{
+ QVector<Compositor::Insert> inserts;
+ m_compositor.setFlags(from, count, group, groupFlags, &inserts);
+ itemsInserted(inserts);
+ emitChanges();
+}
+
+void QQmlDelegateModelPrivate::removeGroups(
+ Compositor::iterator from, int count, Compositor::Group group, int groupFlags)
+{
+ QVector<Compositor::Remove> removes;
+ m_compositor.clearFlags(from, count, group, groupFlags, &removes);
+ itemsRemoved(removes);
+ emitChanges();
+}
+
+void QQmlDelegateModelPrivate::setGroups(
+ Compositor::iterator from, int count, Compositor::Group group, int groupFlags)
+{
+ QVector<Compositor::Remove> removes;
+ QVector<Compositor::Insert> inserts;
+
+ m_compositor.setFlags(from, count, group, groupFlags, &inserts);
+ itemsInserted(inserts);
+ const int removeFlags = ~groupFlags & Compositor::GroupMask;
+
+ from = m_compositor.find(from.group, from.index[from.group]);
+ m_compositor.clearFlags(from, count, group, removeFlags, &removes);
+ itemsRemoved(removes);
+ emitChanges();
+}
+
+bool QQmlDelegateModel::event(QEvent *e)
+{
+ Q_D(QQmlDelegateModel);
+ if (e->type() == QEvent::UpdateRequest) {
+ d->m_adaptorModel.fetchMore();
+ } else if (e->type() == QEvent::User) {
+ d->m_incubatorCleanupScheduled = false;
+ qDeleteAll(d->m_finishedIncubating);
+ d->m_finishedIncubating.clear();
+ }
+ return QQmlInstanceModel::event(e);
+}
+
+void QQmlDelegateModelPrivate::itemsChanged(const QVector<Compositor::Change> &changes)
+{
+ if (!m_delegate)
+ return;
+
+ QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedChanges(m_groupCount);
+
+ foreach (const Compositor::Change &change, changes) {
+ for (int i = 1; i < m_groupCount; ++i) {
+ if (change.inGroup(i)) {
+ translatedChanges[i].append(QQmlChangeSet::Change(change.index[i], change.count));
+ }
+ }
+ }
+
+ for (int i = 1; i < m_groupCount; ++i)
+ QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.change(translatedChanges.at(i));
+}
+
+void QQmlDelegateModel::_q_itemsChanged(int index, int count, const QVector<int> &roles)
+{
+ Q_D(QQmlDelegateModel);
+ if (count <= 0 || !d->m_complete)
+ return;
+
+ if (d->m_adaptorModel.notify(d->m_cache, index, count, roles)) {
+ QVector<Compositor::Change> changes;
+ d->m_compositor.listItemsChanged(&d->m_adaptorModel, index, count, &changes);
+ d->itemsChanged(changes);
+ d->emitChanges();
+ }
+}
+
+static void incrementIndexes(QQmlDelegateModelItem *cacheItem, int count, const int *deltas)
+{
+ if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) {
+ for (int i = 1; i < count; ++i)
+ incubationTask->index[i] += deltas[i];
+ }
+ if (QQmlDelegateModelAttached *attached = cacheItem->attached) {
+ for (int i = 1; i < count; ++i)
+ attached->m_currentIndex[i] += deltas[i];
+ }
+}
+
+void QQmlDelegateModelPrivate::itemsInserted(
+ const QVector<Compositor::Insert> &inserts,
+ QVarLengthArray<QVector<QQmlChangeSet::Insert>, Compositor::MaximumGroupCount> *translatedInserts,
+ QHash<int, QList<QQmlDelegateModelItem *> > *movedItems)
+{
+ int cacheIndex = 0;
+
+ int inserted[Compositor::MaximumGroupCount];
+ for (int i = 1; i < m_groupCount; ++i)
+ inserted[i] = 0;
+
+ foreach (const Compositor::Insert &insert, inserts) {
+ for (; cacheIndex < insert.cacheIndex; ++cacheIndex)
+ incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted);
+
+ for (int i = 1; i < m_groupCount; ++i) {
+ if (insert.inGroup(i)) {
+ (*translatedInserts)[i].append(
+ QQmlChangeSet::Insert(insert.index[i], insert.count, insert.moveId));
+ inserted[i] += insert.count;
+ }
+ }
+
+ if (!insert.inCache())
+ continue;
+
+ if (movedItems && insert.isMove()) {
+ QList<QQmlDelegateModelItem *> items = movedItems->take(insert.moveId);
+ Q_ASSERT(items.count() == insert.count);
+ m_cache = m_cache.mid(0, insert.cacheIndex) + items + m_cache.mid(insert.cacheIndex);
+ }
+ if (insert.inGroup()) {
+ for (int offset = 0; cacheIndex < insert.cacheIndex + insert.count; ++cacheIndex, ++offset) {
+ QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex);
+ cacheItem->groups |= insert.flags & Compositor::GroupMask;
+
+ if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) {
+ for (int i = 1; i < m_groupCount; ++i)
+ incubationTask->index[i] = cacheItem->groups & (1 << i)
+ ? insert.index[i] + offset
+ : insert.index[i];
+ }
+ if (QQmlDelegateModelAttached *attached = cacheItem->attached) {
+ for (int i = 1; i < m_groupCount; ++i)
+ attached->m_currentIndex[i] = cacheItem->groups & (1 << i)
+ ? insert.index[i] + offset
+ : insert.index[i];
+ }
+ }
+ } else {
+ cacheIndex = insert.cacheIndex + insert.count;
+ }
+ }
+ for (; cacheIndex < m_cache.count(); ++cacheIndex)
+ incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted);
+}
+
+void QQmlDelegateModelPrivate::itemsInserted(const QVector<Compositor::Insert> &inserts)
+{
+ QVarLengthArray<QVector<QQmlChangeSet::Insert>, Compositor::MaximumGroupCount> translatedInserts(m_groupCount);
+ itemsInserted(inserts, &translatedInserts);
+ Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache));
+ if (!m_delegate)
+ return;
+
+ for (int i = 1; i < m_groupCount; ++i)
+ QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert(translatedInserts.at(i));
+}
+
+void QQmlDelegateModel::_q_itemsInserted(int index, int count)
+{
+
+ Q_D(QQmlDelegateModel);
+ if (count <= 0 || !d->m_complete)
+ return;
+
+ d->m_count += count;
+
+ for (int i = 0, c = d->m_cache.count(); i < c; ++i) {
+ QQmlDelegateModelItem *item = d->m_cache.at(i);
+ if (item->modelIndex() >= index)
+ item->setModelIndex(item->modelIndex() + count);
+ }
+
+ QVector<Compositor::Insert> inserts;
+ d->m_compositor.listItemsInserted(&d->m_adaptorModel, index, count, &inserts);
+ d->itemsInserted(inserts);
+ d->emitChanges();
+}
+
+void QQmlDelegateModelPrivate::itemsRemoved(
+ const QVector<Compositor::Remove> &removes,
+ QVarLengthArray<QVector<QQmlChangeSet::Remove>, Compositor::MaximumGroupCount> *translatedRemoves,
+ QHash<int, QList<QQmlDelegateModelItem *> > *movedItems)
+{
+ int cacheIndex = 0;
+ int removedCache = 0;
+
+ int removed[Compositor::MaximumGroupCount];
+ for (int i = 1; i < m_groupCount; ++i)
+ removed[i] = 0;
+
+ foreach (const Compositor::Remove &remove, removes) {
+ for (; cacheIndex < remove.cacheIndex; ++cacheIndex)
+ incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed);
+
+ for (int i = 1; i < m_groupCount; ++i) {
+ if (remove.inGroup(i)) {
+ (*translatedRemoves)[i].append(
+ QQmlChangeSet::Remove(remove.index[i], remove.count, remove.moveId));
+ removed[i] -= remove.count;
+ }
+ }
+
+ if (!remove.inCache())
+ continue;
+
+ if (movedItems && remove.isMove()) {
+ movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex, remove.count));
+ QList<QQmlDelegateModelItem *>::iterator begin = m_cache.begin() + remove.cacheIndex;
+ QList<QQmlDelegateModelItem *>::iterator end = begin + remove.count;
+ m_cache.erase(begin, end);
+ } else {
+ for (; cacheIndex < remove.cacheIndex + remove.count - removedCache; ++cacheIndex) {
+ QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex);
+ if (remove.inGroup(Compositor::Persisted) && cacheItem->objectRef == 0 && cacheItem->object) {
+ QObject *object = cacheItem->object;
+ cacheItem->destroyObject();
+ if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object))
+ emitDestroyingPackage(package);
+ else
+ emitDestroyingItem(object);
+ cacheItem->scriptRef -= 1;
+ }
+ if (!cacheItem->isReferenced()) {
+ m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag);
+ m_cache.removeAt(cacheIndex);
+ delete cacheItem;
+ --cacheIndex;
+ ++removedCache;
+ Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache));
+ } else if (remove.groups() == cacheItem->groups) {
+ cacheItem->groups = 0;
+ if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) {
+ for (int i = 1; i < m_groupCount; ++i)
+ incubationTask->index[i] = -1;
+ }
+ if (QQmlDelegateModelAttached *attached = cacheItem->attached) {
+ for (int i = 1; i < m_groupCount; ++i)
+ attached->m_currentIndex[i] = -1;
+ }
+ } else {
+ if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) {
+ for (int i = 1; i < m_groupCount; ++i) {
+ if (remove.inGroup(i))
+ incubationTask->index[i] = remove.index[i];
+ }
+ }
+ if (QQmlDelegateModelAttached *attached = cacheItem->attached) {
+ for (int i = 1; i < m_groupCount; ++i) {
+ if (remove.inGroup(i))
+ attached->m_currentIndex[i] = remove.index[i];
+ }
+ }
+ cacheItem->groups &= ~remove.flags;
+ }
+ }
+ }
+ }
+
+ for (; cacheIndex < m_cache.count(); ++cacheIndex)
+ incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed);
+}
+
+void QQmlDelegateModelPrivate::itemsRemoved(const QVector<Compositor::Remove> &removes)
+{
+ QVarLengthArray<QVector<QQmlChangeSet::Remove>, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount);
+ itemsRemoved(removes, &translatedRemoves);
+ Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache));
+ if (!m_delegate)
+ return;
+
+ for (int i = 1; i < m_groupCount; ++i)
+ QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove(translatedRemoves.at(i));
+}
+
+void QQmlDelegateModel::_q_itemsRemoved(int index, int count)
+{
+ Q_D(QQmlDelegateModel);
+ if (count <= 0|| !d->m_complete)
+ return;
+
+ d->m_count -= count;
+
+ for (int i = 0, c = d->m_cache.count(); i < c; ++i) {
+ QQmlDelegateModelItem *item = d->m_cache.at(i);
+ if (item->modelIndex() >= index + count)
+ item->setModelIndex(item->modelIndex() - count);
+ else if (item->modelIndex() >= index)
+ item->setModelIndex(-1);
+ }
+
+ QVector<Compositor::Remove> removes;
+ d->m_compositor.listItemsRemoved(&d->m_adaptorModel, index, count, &removes);
+ d->itemsRemoved(removes);
+
+ d->emitChanges();
+}
+
+void QQmlDelegateModelPrivate::itemsMoved(
+ const QVector<Compositor::Remove> &removes, const QVector<Compositor::Insert> &inserts)
+{
+ QHash<int, QList<QQmlDelegateModelItem *> > movedItems;
+
+ QVarLengthArray<QVector<QQmlChangeSet::Remove>, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount);
+ itemsRemoved(removes, &translatedRemoves, &movedItems);
+
+ QVarLengthArray<QVector<QQmlChangeSet::Insert>, Compositor::MaximumGroupCount> translatedInserts(m_groupCount);
+ itemsInserted(inserts, &translatedInserts, &movedItems);
+ Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache));
+ Q_ASSERT(movedItems.isEmpty());
+ if (!m_delegate)
+ return;
+
+ for (int i = 1; i < m_groupCount; ++i) {
+ QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.move(
+ translatedRemoves.at(i),
+ translatedInserts.at(i));
+ }
+}
+
+void QQmlDelegateModel::_q_itemsMoved(int from, int to, int count)
+{
+ Q_D(QQmlDelegateModel);
+ if (count <= 0 || !d->m_complete)
+ return;
+
+ const int minimum = qMin(from, to);
+ const int maximum = qMax(from, to) + count;
+ const int difference = from > to ? count : -count;
+
+ for (int i = 0, c = d->m_cache.count(); i < c; ++i) {
+ QQmlDelegateModelItem *item = d->m_cache.at(i);
+ if (item->modelIndex() >= from && item->modelIndex() < from + count)
+ item->setModelIndex(item->modelIndex() - from + to);
+ else if (item->modelIndex() >= minimum && item->modelIndex() < maximum)
+ item->setModelIndex(item->modelIndex() + difference);
+ }
+
+ QVector<Compositor::Remove> removes;
+ QVector<Compositor::Insert> inserts;
+ d->m_compositor.listItemsMoved(&d->m_adaptorModel, from, to, count, &removes, &inserts);
+ d->itemsMoved(removes, inserts);
+ d->emitChanges();
+}
+
+template <typename T> v8::Local<v8::Array>
+QQmlDelegateModelPrivate::buildChangeList(const QVector<T> &changes)
+{
+ v8::Local<v8::Array> indexes = v8::Array::New(changes.count());
+ v8::Local<v8::String> indexKey = v8::String::New("index");
+ v8::Local<v8::String> countKey = v8::String::New("count");
+ v8::Local<v8::String> moveIdKey = v8::String::New("moveId");
+
+ for (int i = 0; i < changes.count(); ++i) {
+ v8::Local<v8::Object> object = v8::Object::New();
+ object->Set(indexKey, v8::Integer::New(changes.at(i).index));
+ object->Set(countKey, v8::Integer::New(changes.at(i).count));
+ object->Set(moveIdKey, changes.at(i).moveId != -1 ? v8::Integer::New(changes.at(i).count) : v8::Undefined());
+ indexes->Set(i, object);
+ }
+ return indexes;
+}
+
+void QQmlDelegateModelPrivate::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset)
+{
+ Q_Q(QQmlDelegateModel);
+ emit q->modelUpdated(changeSet, reset);
+ if (changeSet.difference() != 0)
+ emit q->countChanged();
+}
+
+void QQmlDelegateModelPrivate::emitChanges()
+{
+ if (m_transaction || !m_complete || !m_context->isValid())
+ return;
+
+ m_transaction = true;
+ QV8Engine *engine = QQmlEnginePrivate::getV8Engine(m_context->engine());
+ for (int i = 1; i < m_groupCount; ++i)
+ QQmlDelegateModelGroupPrivate::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)
+ QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitModelUpdated(reset);
+
+ foreach (QQmlDelegateModelItem *cacheItem, m_cache) {
+ if (cacheItem->attached)
+ cacheItem->attached->emitChanges();
+ }
+}
+
+void QQmlDelegateModel::_q_modelReset()
+{
+ Q_D(QQmlDelegateModel);
+ if (!d->m_delegate)
+ return;
+
+ int oldCount = d->m_count;
+ d->m_adaptorModel.rootIndex = QModelIndex();
+
+ if (d->m_complete) {
+ d->m_count = d->m_adaptorModel.count();
+
+ for (int i = 0, c = d->m_cache.count(); i < c; ++i) {
+ QQmlDelegateModelItem *item = d->m_cache.at(i);
+ if (item->modelIndex() != -1)
+ item->setModelIndex(-1);
+ }
+
+ QVector<Compositor::Remove> removes;
+ QVector<Compositor::Insert> inserts;
+ if (oldCount)
+ d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes);
+ if (d->m_count)
+ d->m_compositor.listItemsInserted(&d->m_adaptorModel, 0, d->m_count, &inserts);
+ d->itemsMoved(removes, inserts);
+ d->m_reset = true;
+
+ if (d->m_adaptorModel.canFetchMore())
+ d->m_adaptorModel.fetchMore();
+
+ d->emitChanges();
+ }
+ emit rootIndexChanged();
+}
+
+void QQmlDelegateModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end)
+{
+ Q_D(QQmlDelegateModel);
+ if (parent == d->m_adaptorModel.rootIndex)
+ _q_itemsInserted(begin, end - begin + 1);
+}
+
+void QQmlDelegateModel::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end)
+{
+ Q_D(QQmlDelegateModel);
+ if (!d->m_adaptorModel.rootIndex.isValid())
+ return;
+ const QModelIndex index = d->m_adaptorModel.rootIndex;
+ if (index.parent() == parent && index.row() >= begin && index.row() <= end) {
+ const int oldCount = d->m_count;
+ d->m_count = 0;
+ d->m_adaptorModel.invalidateModel(this);
+
+ if (d->m_complete && oldCount > 0) {
+ QVector<Compositor::Remove> removes;
+ d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes);
+ d->itemsRemoved(removes);
+ d->emitChanges();
+ }
+ }
+}
+
+void QQmlDelegateModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end)
+{
+ Q_D(QQmlDelegateModel);
+ if (parent == d->m_adaptorModel.rootIndex)
+ _q_itemsRemoved(begin, end - begin + 1);
+}
+
+void QQmlDelegateModel::_q_rowsMoved(
+ const QModelIndex &sourceParent, int sourceStart, int sourceEnd,
+ const QModelIndex &destinationParent, int destinationRow)
+{
+ Q_D(QQmlDelegateModel);
+ const int count = sourceEnd - sourceStart + 1;
+ if (destinationParent == d->m_adaptorModel.rootIndex && sourceParent == d->m_adaptorModel.rootIndex) {
+ _q_itemsMoved(sourceStart, sourceStart > destinationRow ? destinationRow : destinationRow - count, count);
+ } else if (sourceParent == d->m_adaptorModel.rootIndex) {
+ _q_itemsRemoved(sourceStart, count);
+ } else if (destinationParent == d->m_adaptorModel.rootIndex) {
+ _q_itemsInserted(destinationRow, count);
+ }
+}
+
+void QQmlDelegateModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles)
+{
+ Q_D(QQmlDelegateModel);
+ if (begin.parent() == d->m_adaptorModel.rootIndex)
+ _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, roles);
+}
+
+void QQmlDelegateModel::_q_layoutChanged()
+{
+ Q_D(QQmlDelegateModel);
+ _q_itemsChanged(0, d->m_count, QVector<int>());
+}
+
+QQmlDelegateModelAttached *QQmlDelegateModel::qmlAttachedProperties(QObject *obj)
+{
+ if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(obj)) {
+ if (cacheItem->object == obj) { // Don't create attached item for child objects.
+ cacheItem->attached = new QQmlDelegateModelAttached(cacheItem, obj);
+ return cacheItem->attached;
+ }
+ }
+ return new QQmlDelegateModelAttached(obj);
+}
+
+bool QQmlDelegateModelPrivate::insert(
+ Compositor::insert_iterator &before, const v8::Local<v8::Object> &object, int groups)
+{
+ if (!m_context->isValid())
+ return false;
+
+ QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, m_context->engine(), -1);
+ if (!cacheItem)
+ return false;
+
+ v8::Local<v8::Array> propertyNames = object->GetPropertyNames();
+ for (uint i = 0; i < propertyNames->Length(); ++i) {
+ v8::Local<v8::String> propertyName = propertyNames->Get(i)->ToString();
+ cacheItem->setValue(
+ m_cacheMetaType->v8Engine->toString(propertyName),
+ m_cacheMetaType->v8Engine->toVariant(object->Get(propertyName), QVariant::Invalid));
+ }
+
+ cacheItem->groups = groups | Compositor::UnresolvedFlag | Compositor::CacheFlag;
+
+ // Must be before the new object is inserted into the cache or its indexes will be adjusted too.
+ itemsInserted(QVector<Compositor::Insert>() << Compositor::Insert(before, 1, cacheItem->groups & ~Compositor::CacheFlag));
+
+ before = m_compositor.insert(before, 0, 0, 1, cacheItem->groups);
+ m_cache.insert(before.cacheIndex, cacheItem);
+
+ return true;
+}
+
+//============================================================================
+
+QQmlDelegateModelItemMetaType::QQmlDelegateModelItemMetaType(
+ QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames)
+ : model(model)
+ , groupCount(groupNames.count() + 1)
+ , v8Engine(engine)
+ , metaObject(0)
+ , groupNames(groupNames)
+{
+}
+
+QQmlDelegateModelItemMetaType::~QQmlDelegateModelItemMetaType()
+{
+ if (metaObject)
+ metaObject->release();
+ qPersistentDispose(constructor);
+}
+
+void QQmlDelegateModelItemMetaType::initializeMetaObject()
+{
+ QMetaObjectBuilder builder;
+ builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
+ builder.setClassName(QQmlDelegateModelAttached::staticMetaObject.className());
+ builder.setSuperClass(&QQmlDelegateModelAttached::staticMetaObject);
+
+ int notifierId = 0;
+ for (int i = 0; i < groupNames.count(); ++i, ++notifierId) {
+ QString propertyName = QStringLiteral("in") + groupNames.at(i);
+ propertyName.replace(2, 1, propertyName.at(2).toUpper());
+ builder.addSignal("__" + propertyName.toUtf8() + "Changed()");
+ QMetaPropertyBuilder propertyBuilder = builder.addProperty(
+ propertyName.toUtf8(), "bool", notifierId);
+ propertyBuilder.setWritable(true);
+ }
+ for (int i = 0; i < groupNames.count(); ++i, ++notifierId) {
+ const QString propertyName = groupNames.at(i) + QStringLiteral("Index");
+ builder.addSignal("__" + propertyName.toUtf8() + "Changed()");
+ QMetaPropertyBuilder propertyBuilder = builder.addProperty(
+ propertyName.toUtf8(), "int", notifierId);
+ propertyBuilder.setWritable(true);
+ }
+
+ metaObject = new QQmlDelegateModelAttachedMetaObject(this, builder.toMetaObject());
+}
+
+void QQmlDelegateModelItemMetaType::initializeConstructor()
+{
+ v8::HandleScope handleScope;
+ v8::Context::Scope contextScope(v8Engine->context());
+
+ QQmlDelegateModelEngineData *data = engineData(v8Engine);
+
+ constructor = qPersistentNew(v8::ObjectTemplate::New());
+
+ constructor->SetHasExternalResource(true);
+ constructor->SetAccessor(data->model(), get_model);
+ constructor->SetAccessor(data->groups(), get_groups, set_groups);
+ constructor->SetAccessor(data->isUnresolved(), get_member, 0, v8::Int32::New(30));
+ constructor->SetAccessor(data->inItems(), get_member, set_member, v8::Int32::New(1));
+ constructor->SetAccessor(data->inPersistedItems(), get_member, set_member, v8::Int32::New(2));
+ constructor->SetAccessor(data->itemsIndex(), get_index, 0, v8::Int32::New(1));
+ constructor->SetAccessor(data->persistedItemsIndex(), get_index, 0, v8::Int32::New(2));
+
+ for (int i = 2; i < groupNames.count(); ++i) {
+ QString propertyName = QStringLiteral("in") + groupNames.at(i);
+ propertyName.replace(2, 1, propertyName.at(2).toUpper());
+ constructor->SetAccessor(
+ v8Engine->toString(propertyName), get_member, set_member, v8::Int32::New(i + 1));
+ }
+ for (int i = 2; i < groupNames.count(); ++i) {
+ const QString propertyName = groupNames.at(i) + QStringLiteral("Index");
+ constructor->SetAccessor(
+ v8Engine->toString(propertyName), get_index, 0, v8::Int32::New(i + 1));
+ }
+}
+
+int QQmlDelegateModelItemMetaType::parseGroups(const QStringList &groups) const
+{
+ int groupFlags = 0;
+ foreach (const QString &groupName, groups) {
+ int index = groupNames.indexOf(groupName);
+ if (index != -1)
+ groupFlags |= 2 << index;
+ }
+ return groupFlags;
+}
+
+int QQmlDelegateModelItemMetaType::parseGroups(const v8::Local<v8::Value> &groups) const
+{
+ int groupFlags = 0;
+ if (groups->IsString()) {
+ const QString groupName = v8Engine->toString(groups);
+ int index = groupNames.indexOf(groupName);
+ if (index != -1)
+ groupFlags |= 2 << index;
+ } else if (groups->IsArray()) {
+ v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(groups);
+ for (uint i = 0; i < array->Length(); ++i) {
+ const QString groupName = v8Engine->toString(array->Get(i));
+ int index = groupNames.indexOf(groupName);
+ if (index != -1)
+ groupFlags |= 2 << index;
+ }
+ }
+ return groupFlags;
+}
+
+v8::Handle<v8::Value> QQmlDelegateModelItemMetaType::get_model(
+ v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QQmlDelegateModelItem *cacheItem = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object");
+ if (!cacheItem->metaType->model)
+ return v8::Undefined();
+
+ return cacheItem->get();
+}
+
+v8::Handle<v8::Value> QQmlDelegateModelItemMetaType::get_groups(
+ v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QQmlDelegateModelItem *cacheItem = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object");
+
+ QStringList groups;
+ for (int i = 1; i < cacheItem->metaType->groupCount; ++i) {
+ if (cacheItem->groups & (1 << i))
+ groups.append(cacheItem->metaType->groupNames.at(i - 1));
+ }
+
+ return cacheItem->engine->fromVariant(groups);
+}
+
+void QQmlDelegateModelItemMetaType::set_groups(
+ v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QQmlDelegateModelItem *cacheItem = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object");
+
+ if (!cacheItem->metaType->model)
+ return;
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model);
+
+ const int groupFlags = model->m_cacheMetaType->parseGroups(value);
+ const int cacheIndex = model->m_cache.indexOf(cacheItem);
+ Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex);
+ model->setGroups(it, 1, Compositor::Cache, groupFlags);
+}
+
+v8::Handle<v8::Value> QQmlDelegateModelItemMetaType::get_member(
+ v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QQmlDelegateModelItem *cacheItem = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object");
+
+ return v8::Boolean::New(cacheItem->groups & (1 << info.Data()->Int32Value()));
+}
+
+void QQmlDelegateModelItemMetaType::set_member(
+ v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QQmlDelegateModelItem *cacheItem = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE_SETTER(cacheItem, "Not a valid VisualData object");
+
+ if (!cacheItem->metaType->model)
+ return;
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model);
+
+ Compositor::Group group = Compositor::Group(info.Data()->Int32Value());
+ const bool member = value->BooleanValue();
+ const int groupFlag = (1 << group);
+ if (member == ((cacheItem->groups & groupFlag) != 0))
+ return;
+
+ const int cacheIndex = model->m_cache.indexOf(cacheItem);
+ Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex);
+ if (member)
+ model->addGroups(it, 1, Compositor::Cache, groupFlag);
+ else
+ model->removeGroups(it, 1, Compositor::Cache, groupFlag);
+}
+
+v8::Handle<v8::Value> QQmlDelegateModelItemMetaType::get_index(
+ v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QQmlDelegateModelItem *cacheItem = v8_resource_cast<QQmlDelegateModelItem>(info.This());
+ V8ASSERT_TYPE(cacheItem, "Not a valid VisualData object");
+
+ return v8::Integer::New(cacheItem->groupIndex(Compositor::Group(info.Data()->Int32Value())));
+}
+
+
+//---------------------------------------------------------------------------
+
+QQmlDelegateModelItem::QQmlDelegateModelItem(
+ QQmlDelegateModelItemMetaType *metaType, int modelIndex)
+ : QV8ObjectResource(metaType->v8Engine)
+ , metaType(metaType)
+ , contextData(0)
+ , object(0)
+ , attached(0)
+ , incubationTask(0)
+ , objectRef(0)
+ , scriptRef(0)
+ , groups(0)
+ , index(modelIndex)
+{
+ metaType->addref();
+}
+
+QQmlDelegateModelItem::~QQmlDelegateModelItem()
+{
+ Q_ASSERT(scriptRef == 0);
+ Q_ASSERT(objectRef == 0);
+ Q_ASSERT(!object);
+
+ if (incubationTask && metaType->model)
+ QQmlDelegateModelPrivate::get(metaType->model)->releaseIncubator(incubationTask);
+
+ metaType->release();
+
+}
+
+void QQmlDelegateModelItem::Dispose()
+{
+ --scriptRef;
+ if (isReferenced())
+ return;
+
+ if (metaType->model) {
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model);
+ model->removeCacheItem(this);
+ }
+ delete this;
+}
+
+/*
+ This is essentially a copy of QQmlComponent::create(); except it takes the QQmlContextData
+ arguments instead of QQmlContext which means we don't have to construct the rather weighty
+ wrapper class for every delegate item.
+*/
+void QQmlDelegateModelItem::incubateObject(
+ QQmlComponent *component,
+ QQmlEngine *engine,
+ QQmlContextData *context,
+ QQmlContextData *forContext)
+{
+ QQmlIncubatorPrivate *incubatorPriv = QQmlIncubatorPrivate::get(incubationTask);
+ QQmlEnginePrivate *enginePriv = QQmlEnginePrivate::get(engine);
+ QQmlComponentPrivate *componentPriv = QQmlComponentPrivate::get(component);
+
+ incubatorPriv->compiledData = componentPriv->cc;
+ incubatorPriv->compiledData->addref();
+ incubatorPriv->vme.init(
+ context,
+ componentPriv->cc,
+ componentPriv->start,
+ componentPriv->creationContext);
+
+ enginePriv->incubate(*incubationTask, forContext);
+}
+
+void QQmlDelegateModelItem::destroyObject()
+{
+ Q_ASSERT(object);
+ Q_ASSERT(contextData);
+
+ QObjectPrivate *p = QObjectPrivate::get(object);
+ Q_ASSERT(p->declarativeData);
+ QQmlData *data = static_cast<QQmlData*>(p->declarativeData);
+ if (data->ownContext && data->context)
+ data->context->clearContext();
+ object->deleteLater();
+
+ if (attached) {
+ attached->m_cacheItem = 0;
+ attached = 0;
+ }
+
+ contextData->destroy();
+ contextData = 0;
+ object = 0;
+}
+
+QQmlDelegateModelItem *QQmlDelegateModelItem::dataForObject(QObject *object)
+{
+ QObjectPrivate *p = QObjectPrivate::get(object);
+ QQmlContextData *context = p->declarativeData
+ ? static_cast<QQmlData *>(p->declarativeData)->context
+ : 0;
+ for (context = context ? context->parent : 0; context; context = context->parent) {
+ if (QQmlDelegateModelItem *cacheItem = qobject_cast<QQmlDelegateModelItem *>(
+ context->contextObject)) {
+ return cacheItem;
+ }
+ }
+ return 0;
+}
+
+int QQmlDelegateModelItem::groupIndex(Compositor::Group group)
+{
+ if (QQmlDelegateModelPrivate * const model = metaType->model
+ ? QQmlDelegateModelPrivate::get(metaType->model)
+ : 0) {
+ return model->m_compositor.find(Compositor::Cache, model->m_cache.indexOf(this)).index[group];
+ }
+ return -1;
+}
+
+//---------------------------------------------------------------------------
+
+QQmlDelegateModelAttachedMetaObject::QQmlDelegateModelAttachedMetaObject(
+ QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject)
+ : metaType(metaType)
+ , metaObject(metaObject)
+ , memberPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount())
+ , indexPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount() + metaType->groupNames.count())
+{
+ // Don't reference count the meta-type here as that would create a circular reference.
+ // Instead we rely the fact that the meta-type's reference count can't reach 0 without first
+ // destroying all delegates with attached objects.
+ *static_cast<QMetaObject *>(this) = *metaObject;
+}
+
+QQmlDelegateModelAttachedMetaObject::~QQmlDelegateModelAttachedMetaObject()
+{
+ ::free(metaObject);
+}
+
+void QQmlDelegateModelAttachedMetaObject::objectDestroyed(QObject *)
+{
+ release();
+}
+
+int QQmlDelegateModelAttachedMetaObject::metaCall(QObject *object, QMetaObject::Call call, int _id, void **arguments)
+{
+ QQmlDelegateModelAttached *attached = static_cast<QQmlDelegateModelAttached *>(object);
+ if (call == QMetaObject::ReadProperty) {
+ if (_id >= indexPropertyOffset) {
+ Compositor::Group group = Compositor::Group(_id - indexPropertyOffset + 1);
+ *static_cast<int *>(arguments[0]) = attached->m_currentIndex[group];
+ return -1;
+ } else if (_id >= memberPropertyOffset) {
+ Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1);
+ *static_cast<bool *>(arguments[0]) = attached->m_cacheItem->groups & (1 << group);
+ return -1;
+ }
+ } else if (call == QMetaObject::WriteProperty) {
+ if (_id >= memberPropertyOffset) {
+ if (!metaType->model)
+ return -1;
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model);
+ Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1);
+ const int groupFlag = 1 << group;
+ const bool member = attached->m_cacheItem->groups & groupFlag;
+ if (member && !*static_cast<bool *>(arguments[0])) {
+ Compositor::iterator it = model->m_compositor.find(
+ group, attached->m_currentIndex[group]);
+ model->removeGroups(it, 1, group, groupFlag);
+ } else if (!member && *static_cast<bool *>(arguments[0])) {
+ for (int i = 1; i < metaType->groupCount; ++i) {
+ if (attached->m_cacheItem->groups & (1 << i)) {
+ Compositor::iterator it = model->m_compositor.find(
+ Compositor::Group(i), attached->m_currentIndex[i]);
+ model->addGroups(it, 1, Compositor::Group(i), groupFlag);
+ break;
+ }
+ }
+ }
+ return -1;
+ }
+ }
+ return attached->qt_metacall(call, _id, arguments);
+}
+
+QQmlDelegateModelAttached::QQmlDelegateModelAttached(QObject *parent)
+ : m_cacheItem(0)
+ , m_previousGroups(0)
+{
+ QQml_setParent_noEvent(this, parent);
+}
+
+QQmlDelegateModelAttached::QQmlDelegateModelAttached(
+ QQmlDelegateModelItem *cacheItem, QObject *parent)
+ : m_cacheItem(cacheItem)
+ , m_previousGroups(cacheItem->groups)
+{
+ QQml_setParent_noEvent(this, parent);
+ if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) {
+ for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i)
+ m_currentIndex[i] = m_previousIndex[i] = incubationTask->index[i];
+ } else {
+ QQmlDelegateModelPrivate * const model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model);
+ Compositor::iterator it = model->m_compositor.find(
+ Compositor::Cache, model->m_cache.indexOf(m_cacheItem));
+ for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i)
+ m_currentIndex[i] = m_previousIndex[i] = it.index[i];
+ }
+
+ if (!cacheItem->metaType->metaObject)
+ cacheItem->metaType->initializeMetaObject();
+
+ QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject;
+ cacheItem->metaType->metaObject->addref();
+}
+
+/*!
+ \qmlattachedproperty int QtQml.Models2::DelegateModel::model
+
+ This attached property holds the visual data model this delegate instance belongs to.
+
+ It is attached to each instance of the delegate.
+*/
+
+QQmlDelegateModel *QQmlDelegateModelAttached::model() const
+{
+ return m_cacheItem ? m_cacheItem->metaType->model : 0;
+}
+
+/*!
+ \qmlattachedproperty stringlist QtQml.Models2::DelegateModel::groups
+
+ This attached property holds the name of DelegateModelGroups the item belongs to.
+
+ It is attached to each instance of the delegate.
+*/
+
+QStringList QQmlDelegateModelAttached::groups() const
+{
+ QStringList groups;
+
+ if (!m_cacheItem)
+ return groups;
+ for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) {
+ if (m_cacheItem->groups & (1 << i))
+ groups.append(m_cacheItem->metaType->groupNames.at(i - 1));
+ }
+ return groups;
+}
+
+void QQmlDelegateModelAttached::setGroups(const QStringList &groups)
+{
+ if (!m_cacheItem)
+ return;
+
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model);
+
+ const int groupFlags = model->m_cacheMetaType->parseGroups(groups);
+ const int cacheIndex = model->m_cache.indexOf(m_cacheItem);
+ Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex);
+ model->setGroups(it, 1, Compositor::Cache, groupFlags);
+}
+
+/*!
+ \qmlattachedproperty bool QtQml.Models2::DelegateModel::isUnresolved
+
+ This attached property holds whether the visual item is bound to a data model index.
+ Returns true if the item is not bound to the model, and false if it is.
+
+ An unresolved item can be bound to the data model using the DelegateModelGroup::resolve()
+ function.
+
+ It is attached to each instance of the delegate.
+*/
+
+bool QQmlDelegateModelAttached::isUnresolved() const
+{
+ if (!m_cacheItem)
+ return false;
+
+ return m_cacheItem->groups & Compositor::UnresolvedFlag;
+}
+
+/*!
+ \qmlattachedproperty int QtQml.Models2::DelegateModel::inItems
+
+ This attached property holds whether the item belongs to the default \l items DelegateModelGroup.
+
+ Changing this property will add or remove the item from the items group.
+
+ It is attached to each instance of the delegate.
+*/
+
+/*!
+ \qmlattachedproperty int QtQml.Models2::DelegateModel::itemsIndex
+
+ This attached property holds the index of the item in the default \l items DelegateModelGroup.
+
+ It is attached to each instance of the delegate.
+*/
+
+/*!
+ \qmlattachedproperty int QtQml.Models2::DelegateModel::inPersistedItems
+
+ This attached property holds whether the item belongs to the \l persistedItems DelegateModelGroup.
+
+ Changing this property will add or remove the item from the items group. Change with caution
+ as removing an item from the persistedItems group will destroy the current instance if it is
+ not referenced by a model.
+
+ It is attached to each instance of the delegate.
+*/
+
+/*!
+ \qmlattachedproperty int QtQml.Models2::DelegateModel::persistedItemsIndex
+
+ This attached property holds the index of the item in the \l persistedItems DelegateModelGroup.
+
+ It is attached to each instance of the delegate.
+*/
+
+void QQmlDelegateModelAttached::emitChanges()
+{
+ const int groupChanges = m_previousGroups ^ m_cacheItem->groups;
+ m_previousGroups = m_cacheItem->groups;
+
+ int indexChanges = 0;
+ for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) {
+ if (m_previousIndex[i] != m_currentIndex[i]) {
+ m_previousIndex[i] = m_currentIndex[i];
+ indexChanges |= (1 << i);
+ }
+ }
+
+ int notifierId = 0;
+ const QMetaObject *meta = metaObject();
+ for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) {
+ if (groupChanges & (1 << i))
+ QMetaObject::activate(this, meta, notifierId, 0);
+ }
+ for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) {
+ if (indexChanges & (1 << i))
+ QMetaObject::activate(this, meta, notifierId, 0);
+ }
+
+ if (groupChanges)
+ emit groupsChanged();
+}
+
+//============================================================================
+
+void QQmlDelegateModelGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::Group g)
+{
+ Q_ASSERT(!model);
+ model = m;
+ group = g;
+}
+
+bool QQmlDelegateModelGroupPrivate::isChangedConnected()
+{
+ Q_Q(QQmlDelegateModelGroup);
+ IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QQmlV8Handle &,const QQmlV8Handle &));
+}
+
+void QQmlDelegateModelGroupPrivate::emitChanges(QV8Engine *engine)
+{
+ Q_Q(QQmlDelegateModelGroup);
+ if (isChangedConnected() && !changeSet.isEmpty()) {
+ v8::HandleScope handleScope;
+ v8::Context::Scope contextScope(engine->context());
+ v8::Local<v8::Object> removed = engineData(engine)->array(engine, changeSet.removes());
+ v8::Local<v8::Object> inserted = engineData(engine)->array(engine, changeSet.inserts());
+ emit q->changed(QQmlV8Handle::fromHandle(removed), QQmlV8Handle::fromHandle(inserted));
+ }
+ if (changeSet.difference() != 0)
+ emit q->countChanged();
+}
+
+void QQmlDelegateModelGroupPrivate::emitModelUpdated(bool reset)
+{
+ for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it)
+ it->emitModelUpdated(changeSet, reset);
+ changeSet.clear();
+}
+
+void QQmlDelegateModelGroupPrivate::createdPackage(int index, QQuickPackage *package)
+{
+ for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it)
+ it->createdPackage(index, package);
+}
+
+void QQmlDelegateModelGroupPrivate::initPackage(int index, QQuickPackage *package)
+{
+ for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it)
+ it->initPackage(index, package);
+}
+
+void QQmlDelegateModelGroupPrivate::destroyingPackage(QQuickPackage *package)
+{
+ for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it)
+ it->destroyingPackage(package);
+}
+
+/*!
+ \qmltype DelegateModelGroup
+ \instantiates QQmlDelegateModelGroup
+ \inqmlmodule QtQuick 2
+ \ingroup qtquick-models
+ \brief Encapsulates a filtered set of visual data items
+
+ The DelegateModelGroup type provides a means to address the model data of a DelegateModel's
+ delegate items, as well as sort and filter these delegate items.
+
+ The initial set of instantiable delegate items in a DelegateModel is represented
+ by its \l {QtQml.Models2::DelegateModel::items}{items} group, which normally directly reflects
+ the contents of the model assigned to DelegateModel::model. This set can be changed to
+ the contents of any other member of DelegateModel::groups by assigning the \l name of that
+ DelegateModelGroup to the DelegateModel::filterOnGroup property.
+
+ The data of an item in a DelegateModelGroup can be accessed using the get() function, which returns
+ information about group membership and indexes as well as model data. In combination
+ with the move() function this can be used to implement view sorting, with remove() to filter
+ items out of a view, or with setGroups() and \l Package delegates to categorize items into
+ different views.
+
+ Data from models can be supplemented by inserting data directly into a DelegateModelGroup
+ with the insert() function. This can be used to introduce mock items into a view, or
+ placeholder items that are later \l {resolve()}{resolved} to real model data when it becomes
+ available.
+
+ Delegate items can also be be instantiated directly from a DelegateModelGroup using the
+ create() function, making it possible to use DelegateModel without an accompanying view
+ type or to cherry-pick specific items that should be instantiated irregardless of whether
+ they're currently within a view's visible area.
+
+ \sa {QML Dynamic View Ordering Tutorial}
+*/
+/*!
+ \qmltype DelegateModelGroup
+ \instantiates QQmlDelegateModelGroup
+ \inqmlmodule QtQml.Models 2
+ \brief Encapsulates a filtered set of visual data items
+
+ The DelegateModelGroup type provides a means to address the model data of a DelegateModel's
+ delegate items, as well as sort and filter these delegate items.
+
+ This element is also available as DelegateModelGroup in the QtQuick module. For full details,
+ see the \l DelegateModelGroup documentation.
+
+ \sa {QtQuick::DelegateModelGroup}
+*/
+
+
+QQmlDelegateModelGroup::QQmlDelegateModelGroup(QObject *parent)
+ : QObject(*new QQmlDelegateModelGroupPrivate, parent)
+{
+}
+
+QQmlDelegateModelGroup::QQmlDelegateModelGroup(
+ const QString &name, QQmlDelegateModel *model, int index, QObject *parent)
+ : QObject(*new QQmlDelegateModelGroupPrivate, parent)
+{
+ Q_D(QQmlDelegateModelGroup);
+ d->name = name;
+ d->setModel(model, Compositor::Group(index));
+}
+
+QQmlDelegateModelGroup::~QQmlDelegateModelGroup()
+{
+}
+
+/*!
+ \qmlproperty string QtQml.Models2::DelegateModelGroup::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 QQmlDelegateModelGroup::name() const
+{
+ Q_D(const QQmlDelegateModelGroup);
+ return d->name;
+}
+
+void QQmlDelegateModelGroup::setName(const QString &name)
+{
+ Q_D(QQmlDelegateModelGroup);
+ if (d->model)
+ return;
+ if (d->name != name) {
+ d->name = name;
+ emit nameChanged();
+ }
+}
+
+/*!
+ \qmlproperty int QtQml.Models2::DelegateModelGroup::count
+
+ This property holds the number of items in the group.
+*/
+
+int QQmlDelegateModelGroup::count() const
+{
+ Q_D(const QQmlDelegateModelGroup);
+ if (!d->model)
+ return 0;
+ return QQmlDelegateModelPrivate::get(d->model)->m_compositor.count(d->group);
+}
+
+/*!
+ \qmlproperty bool QtQml.Models2::DelegateModelGroup::includeByDefault
+
+ This property holds whether new items are assigned to this group by default.
+*/
+
+bool QQmlDelegateModelGroup::defaultInclude() const
+{
+ Q_D(const QQmlDelegateModelGroup);
+ return d->defaultInclude;
+}
+
+void QQmlDelegateModelGroup::setDefaultInclude(bool include)
+{
+ Q_D(QQmlDelegateModelGroup);
+ if (d->defaultInclude != include) {
+ d->defaultInclude = include;
+
+ if (d->model) {
+ if (include)
+ QQmlDelegateModelPrivate::get(d->model)->m_compositor.setDefaultGroup(d->group);
+ else
+ QQmlDelegateModelPrivate::get(d->model)->m_compositor.clearDefaultGroup(d->group);
+ }
+ emit defaultIncludeChanged();
+ }
+}
+
+/*!
+ \qmlmethod object QtQml.Models2::DelegateModelGroup::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
+ DelegateModel attached as well as the model for that item. It has the properties:
+
+ \list
+ \li \b model The model data of the item. This is the same as the model context property in
+ a delegate
+ \li \b groups A list the of names of groups the item is a member of. This property can be
+ written to change the item's membership.
+ \li \b inItems Whether the item belongs to the \l {QtQml.Models2::DelegateModel::items}{items} group.
+ Writing to this property will add or remove the item from the group.
+ \li \b itemsIndex The index of the item within the \l {QtQml.Models2::DelegateModel::items}{items} group.
+ \li \b {in<GroupName>} Whether the item belongs to the dynamic group \e groupName. Writing to
+ this property will add or remove the item from the group.
+ \li \b {<groupName>Index} The index of the item within the dynamic group \e groupName.
+ \li \b isUnresolved Whether the item is bound to an index in the model assigned to
+ DelegateModel::model. Returns true if the item is not bound to the model, and false if it is.
+ \endlist
+*/
+
+QQmlV8Handle QQmlDelegateModelGroup::get(int index)
+{
+ Q_D(QQmlDelegateModelGroup);
+ if (!d->model)
+ return QQmlV8Handle::fromHandle(v8::Undefined());;
+
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
+ if (!model->m_context->isValid()) {
+ return QQmlV8Handle::fromHandle(v8::Undefined());
+ } else if (index < 0 || index >= model->m_compositor.count(d->group)) {
+ qmlInfo(this) << tr("get: index out of range");
+ return QQmlV8Handle::fromHandle(v8::Undefined());
+ }
+
+ Compositor::iterator it = model->m_compositor.find(d->group, index);
+ QQmlDelegateModelItem *cacheItem = it->inCache()
+ ? model->m_cache.at(it.cacheIndex)
+ : 0;
+
+ if (!cacheItem) {
+ cacheItem = model->m_adaptorModel.createItem(
+ model->m_cacheMetaType, model->m_context->engine(), it.modelIndex());
+ if (!cacheItem)
+ return QQmlV8Handle::fromHandle(v8::Undefined());
+ cacheItem->groups = it->flags;
+
+ model->m_cache.insert(it.cacheIndex, cacheItem);
+ model->m_compositor.setFlags(it, 1, Compositor::CacheFlag);
+ }
+
+ if (model->m_cacheMetaType->constructor.IsEmpty())
+ model->m_cacheMetaType->initializeConstructor();
+ v8::Local<v8::Object> handle = model->m_cacheMetaType->constructor->NewInstance();
+ handle->SetExternalResource(cacheItem);
+ ++cacheItem->scriptRef;
+
+ return QQmlV8Handle::fromHandle(handle);
+}
+
+bool QQmlDelegateModelGroupPrivate::parseIndex(
+ const v8::Local<v8::Value> &value, int *index, Compositor::Group *group) const
+{
+ if (value->IsInt32()) {
+ *index = value->Int32Value();
+ return true;
+ } else if (value->IsObject()) {
+ v8::Local<v8::Object> object = value->ToObject();
+ QQmlDelegateModelItem * const cacheItem = v8_resource_cast<QQmlDelegateModelItem>(object);
+ if (QQmlDelegateModelPrivate *model = cacheItem && cacheItem->metaType->model
+ ? QQmlDelegateModelPrivate::get(cacheItem->metaType->model)
+ : 0) {
+ *index = model->m_cache.indexOf(cacheItem);
+ *group = Compositor::Cache;
+ return true;
+ }
+ }
+ return false;
+}
+
+/*!
+ \qmlmethod QtQml.Models2::DelegateModelGroup::insert(int index, jsdict data, array groups = undefined)
+ \qmlmethod QtQml.Models2::DelegateModelGroup::insert(jsdict data, var groups = undefined)
+
+ Creates a new entry at \a index in a DelegateModel with the values from \a data that
+ correspond to roles in the model assigned to DelegateModel::model.
+
+ If no index is supplied the data is appended to the model.
+
+ The optional \a groups parameter identifies the groups the new entry should belong to,
+ if unspecified this is equal to the group insert was called on.
+
+ Data inserted into a DelegateModel can later be merged with an existing entry in
+ DelegateModel::model using the \l resolve() function. This can be used to create placeholder
+ items that are later replaced by actual data.
+*/
+
+void QQmlDelegateModelGroup::insert(QQmlV8Function *args)
+{
+ Q_D(QQmlDelegateModelGroup);
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
+
+ int index = model->m_compositor.count(d->group);
+ Compositor::Group group = d->group;
+
+ if (args->Length() == 0)
+ return;
+
+ int i = 0;
+ v8::Local<v8::Value> v = (*args)[i];
+ if (d->parseIndex(v, &index, &group)) {
+ if (index < 0 || index > model->m_compositor.count(group)) {
+ qmlInfo(this) << tr("insert: index out of range");
+ return;
+ }
+ if (++i == args->Length())
+ return;
+ v = (*args)[i];
+ }
+
+ Compositor::insert_iterator before = index < model->m_compositor.count(group)
+ ? model->m_compositor.findInsertPosition(group, index)
+ : model->m_compositor.end();
+
+ int groups = 1 << d->group;
+ if (++i < args->Length())
+ groups |= model->m_cacheMetaType->parseGroups((*args)[i]);
+
+ if (v->IsArray()) {
+ return;
+ } else if (v->IsObject()) {
+ model->insert(before, v->ToObject(), groups);
+ model->emitChanges();
+ }
+}
+
+/*!
+ \qmlmethod QtQml.Models2::DelegateModelGroup::create(int index)
+ \qmlmethod QtQml.Models2::DelegateModelGroup::create(int index, jsdict data, array groups = undefined)
+ \qmlmethod QtQml.Models2::DelegateModelGroup::create(jsdict data, array groups = undefined)
+
+ Returns a reference to the instantiated item at \a index in the group.
+
+ If a \a data object is provided it will be \l {insert}{inserted} at \a index and an item
+ referencing this new entry will be returned. The optional \a groups parameter identifies
+ the groups the new entry should belong to, if unspecified this is equal to the group create()
+ was called on.
+
+ All items returned by create are added to the
+ \l {QtQml.Models2::DelegateModel::persistedItems}{persistedItems} group. Items in this
+ group remain instantiated when not referenced by any view.
+*/
+
+void QQmlDelegateModelGroup::create(QQmlV8Function *args)
+{
+ Q_D(QQmlDelegateModelGroup);
+ if (!d->model)
+ return;
+
+ if (args->Length() == 0)
+ return;
+
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
+
+ int index = model->m_compositor.count(d->group);
+ Compositor::Group group = d->group;
+
+ int i = 0;
+ v8::Local<v8::Value> v = (*args)[i];
+ if (d->parseIndex(v, &index, &group))
+ ++i;
+
+ if (i < args->Length() && index >= 0 && index <= model->m_compositor.count(group)) {
+ v = (*args)[i];
+ if (v->IsObject()) {
+ int groups = 1 << d->group;
+ if (++i < args->Length())
+ groups |= model->m_cacheMetaType->parseGroups((*args)[i]);
+
+ Compositor::insert_iterator before = index < model->m_compositor.count(group)
+ ? model->m_compositor.findInsertPosition(group, index)
+ : model->m_compositor.end();
+
+ index = before.index[d->group];
+ group = d->group;
+
+ if (!model->insert(before, v->ToObject(), groups)) {
+ return;
+ }
+ }
+ }
+ if (index < 0 || index >= model->m_compositor.count(group)) {
+ qmlInfo(this) << tr("create: index out of range");
+ return;
+ }
+
+ QObject *object = model->object(group, index, false);
+ if (object) {
+ QVector<Compositor::Insert> inserts;
+ Compositor::iterator it = model->m_compositor.find(group, index);
+ model->m_compositor.setFlags(it, 1, d->group, Compositor::PersistedFlag, &inserts);
+ model->itemsInserted(inserts);
+ model->m_cache.at(it.cacheIndex)->releaseObject();
+ }
+
+ args->returnValue(args->engine()->newQObject(object));
+ model->emitChanges();
+}
+
+/*!
+ \qmlmethod QtQml.Models2::DelegateModelGroup::resolve(int from, int to)
+
+ Binds an unresolved item at \a from to an item in DelegateModel::model at index \a to.
+
+ Unresolved items are entries whose data has been \l {insert()}{inserted} into a DelegateModelGroup
+ instead of being derived from a DelegateModel::model index. Resolving an item will replace
+ the item at the target index with the unresolved item. A resolved an item will reflect the data
+ of the source model at its bound index and will move when that index moves like any other item.
+
+ If a new item is replaced in the DelegateModelGroup onChanged() handler its insertion and
+ replacement will be communicated to views as an atomic operation, creating the appearance
+ that the model contents have not changed, or if the unresolved and model item are not adjacent
+ that the previously unresolved item has simply moved.
+
+*/
+void QQmlDelegateModelGroup::resolve(QQmlV8Function *args)
+{
+ Q_D(QQmlDelegateModelGroup);
+ if (!d->model)
+ return;
+
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
+
+ if (args->Length() < 2)
+ return;
+
+ int from = -1;
+ int to = -1;
+ Compositor::Group fromGroup = d->group;
+ Compositor::Group toGroup = d->group;
+
+ v8::Local<v8::Value> v = (*args)[0];
+ if (d->parseIndex(v, &from, &fromGroup)) {
+ if (from < 0 || from >= model->m_compositor.count(fromGroup)) {
+ qmlInfo(this) << tr("resolve: from index out of range");
+ return;
+ }
+ } else {
+ qmlInfo(this) << tr("resolve: from index invalid");
+ return;
+ }
+
+ v = (*args)[1];
+ if (d->parseIndex(v, &to, &toGroup)) {
+ if (to < 0 || to >= model->m_compositor.count(toGroup)) {
+ qmlInfo(this) << tr("resolve: to index out of range");
+ return;
+ }
+ } else {
+ qmlInfo(this) << tr("resolve: to index invalid");
+ return;
+ }
+
+ Compositor::iterator fromIt = model->m_compositor.find(fromGroup, from);
+ Compositor::iterator toIt = model->m_compositor.find(toGroup, to);
+
+ if (!fromIt->isUnresolved()) {
+ qmlInfo(this) << tr("resolve: from is not an unresolved item");
+ return;
+ }
+ if (!toIt->list) {
+ qmlInfo(this) << tr("resolve: to is not a model item");
+ return;
+ }
+
+ const int unresolvedFlags = fromIt->flags;
+ const int resolvedFlags = toIt->flags;
+ const int resolvedIndex = toIt.modelIndex();
+ void * const resolvedList = toIt->list;
+
+ QQmlDelegateModelItem *cacheItem = model->m_cache.at(fromIt.cacheIndex);
+ cacheItem->groups &= ~Compositor::UnresolvedFlag;
+
+ if (toIt.cacheIndex > fromIt.cacheIndex)
+ toIt.decrementIndexes(1, unresolvedFlags);
+ if (!toIt->inGroup(fromGroup) || toIt.index[fromGroup] > from)
+ from += 1;
+
+ model->itemsMoved(
+ QVector<Compositor::Remove>() << Compositor::Remove(fromIt, 1, unresolvedFlags, 0),
+ QVector<Compositor::Insert>() << Compositor::Insert(toIt, 1, unresolvedFlags, 0));
+ model->itemsInserted(
+ QVector<Compositor::Insert>() << Compositor::Insert(toIt, 1, (resolvedFlags & ~unresolvedFlags) | Compositor::CacheFlag));
+ toIt.incrementIndexes(1, resolvedFlags | unresolvedFlags);
+ model->itemsRemoved(QVector<Compositor::Remove>() << Compositor::Remove(toIt, 1, resolvedFlags));
+
+ model->m_compositor.setFlags(toGroup, to, 1, unresolvedFlags & ~Compositor::UnresolvedFlag);
+ model->m_compositor.clearFlags(fromGroup, from, 1, unresolvedFlags);
+
+ if (resolvedFlags & Compositor::CacheFlag)
+ model->m_compositor.insert(Compositor::Cache, toIt.cacheIndex, resolvedList, resolvedIndex, 1, Compositor::CacheFlag);
+
+ Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache));
+
+ if (!cacheItem->isReferenced()) {
+ Q_ASSERT(toIt.cacheIndex == model->m_cache.indexOf(cacheItem));
+ model->m_cache.removeAt(toIt.cacheIndex);
+ model->m_compositor.clearFlags(Compositor::Cache, toIt.cacheIndex, 1, Compositor::CacheFlag);
+ delete cacheItem;
+ Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache));
+ } else {
+ cacheItem->resolveIndex(model->m_adaptorModel, resolvedIndex);
+ if (cacheItem->attached)
+ cacheItem->attached->emitUnresolvedChanged();
+ }
+
+ model->emitChanges();
+}
+
+/*!
+ \qmlmethod QtQml.Models2::DelegateModelGroup::remove(int index, int count)
+
+ Removes \a count items starting at \a index from the group.
+*/
+
+void QQmlDelegateModelGroup::remove(QQmlV8Function *args)
+{
+ Q_D(QQmlDelegateModelGroup);
+ if (!d->model)
+ return;
+ Compositor::Group group = d->group;
+ int index = -1;
+ int count = 1;
+
+ if (args->Length() == 0)
+ return;
+
+ int i = 0;
+ v8::Local<v8::Value> v = (*args)[i];
+ if (!d->parseIndex(v, &index, &group)) {
+ qmlInfo(this) << tr("remove: invalid index");
+ return;
+ }
+
+ if (++i < args->Length()) {
+ v = (*args)[i];
+ if (v->IsInt32())
+ count = v->Int32Value();
+ }
+
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
+ if (index < 0 || index >= model->m_compositor.count(group)) {
+ qmlInfo(this) << tr("remove: index out of range");
+ } else if (count != 0) {
+ Compositor::iterator it = model->m_compositor.find(group, index);
+ if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) {
+ qmlInfo(this) << tr("remove: invalid count");
+ } else {
+ model->removeGroups(it, count, d->group, 1 << d->group);
+ }
+ }
+}
+
+bool QQmlDelegateModelGroupPrivate::parseGroupArgs(
+ QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const
+{
+ if (!model || !QQmlDelegateModelPrivate::get(model)->m_cacheMetaType)
+ return false;
+
+ if (args->Length() < 2)
+ return false;
+
+ int i = 0;
+ v8::Local<v8::Value> v = (*args)[i];
+ if (!parseIndex(v, index, group))
+ return false;
+
+ v = (*args)[++i];
+ if (v->IsInt32()) {
+ *count = v->Int32Value();
+
+ if (++i == args->Length())
+ return false;
+ v = (*args)[i];
+ }
+
+ *groups = QQmlDelegateModelPrivate::get(model)->m_cacheMetaType->parseGroups(v);
+
+ return true;
+}
+
+/*!
+ \qmlmethod QtQml.Models2::DelegateModelGroup::addGroups(int index, int count, stringlist groups)
+
+ Adds \a count items starting at \a index to \a groups.
+*/
+
+void QQmlDelegateModelGroup::addGroups(QQmlV8Function *args)
+{
+ Q_D(QQmlDelegateModelGroup);
+ Compositor::Group group = d->group;
+ int index = -1;
+ int count = 1;
+ int groups = 0;
+
+ if (!d->parseGroupArgs(args, &group, &index, &count, &groups))
+ return;
+
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
+ if (index < 0 || index >= model->m_compositor.count(group)) {
+ qmlInfo(this) << tr("addGroups: index out of range");
+ } else if (count != 0) {
+ Compositor::iterator it = model->m_compositor.find(group, index);
+ if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) {
+ qmlInfo(this) << tr("addGroups: invalid count");
+ } else {
+ model->addGroups(it, count, d->group, groups);
+ }
+ }
+}
+
+/*!
+ \qmlmethod QtQml.Models2::DelegateModelGroup::removeGroups(int index, int count, stringlist groups)
+
+ Removes \a count items starting at \a index from \a groups.
+*/
+
+void QQmlDelegateModelGroup::removeGroups(QQmlV8Function *args)
+{
+ Q_D(QQmlDelegateModelGroup);
+ Compositor::Group group = d->group;
+ int index = -1;
+ int count = 1;
+ int groups = 0;
+
+ if (!d->parseGroupArgs(args, &group, &index, &count, &groups))
+ return;
+
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
+ if (index < 0 || index >= model->m_compositor.count(group)) {
+ qmlInfo(this) << tr("removeGroups: index out of range");
+ } else if (count != 0) {
+ Compositor::iterator it = model->m_compositor.find(group, index);
+ if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) {
+ qmlInfo(this) << tr("removeGroups: invalid count");
+ } else {
+ model->removeGroups(it, count, d->group, groups);
+ }
+ }
+}
+
+/*!
+ \qmlmethod QtQml.Models2::DelegateModelGroup::setGroups(int index, int count, stringlist groups)
+
+ Sets the \a groups \a count items starting at \a index belong to.
+*/
+
+void QQmlDelegateModelGroup::setGroups(QQmlV8Function *args)
+{
+ Q_D(QQmlDelegateModelGroup);
+ Compositor::Group group = d->group;
+ int index = -1;
+ int count = 1;
+ int groups = 0;
+
+ if (!d->parseGroupArgs(args, &group, &index, &count, &groups))
+ return;
+
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
+ if (index < 0 || index >= model->m_compositor.count(group)) {
+ qmlInfo(this) << tr("setGroups: index out of range");
+ } else if (count != 0) {
+ Compositor::iterator it = model->m_compositor.find(group, index);
+ if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) {
+ qmlInfo(this) << tr("setGroups: invalid count");
+ } else {
+ model->setGroups(it, count, d->group, groups);
+ }
+ }
+}
+
+/*!
+ \qmlmethod QtQml.Models2::DelegateModelGroup::setGroups(int index, int count, stringlist groups)
+
+ Sets the \a groups \a count items starting at \a index belong to.
+*/
+
+/*!
+ \qmlmethod QtQml.Models2::DelegateModelGroup::move(var from, var to, int count)
+
+ Moves \a count at \a from in a group \a to a new position.
+*/
+
+void QQmlDelegateModelGroup::move(QQmlV8Function *args)
+{
+ Q_D(QQmlDelegateModelGroup);
+
+ if (args->Length() < 2)
+ return;
+
+ Compositor::Group fromGroup = d->group;
+ Compositor::Group toGroup = d->group;
+ int from = -1;
+ int to = -1;
+ int count = 1;
+
+ if (!d->parseIndex((*args)[0], &from, &fromGroup)) {
+ qmlInfo(this) << tr("move: invalid from index");
+ return;
+ }
+
+ if (!d->parseIndex((*args)[1], &to, &toGroup)) {
+ qmlInfo(this) << tr("move: invalid to index");
+ return;
+ }
+
+ if (args->Length() > 2) {
+ v8::Local<v8::Value> v = (*args)[2];
+ if (v->IsInt32())
+ count = v->Int32Value();
+ }
+
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model);
+
+ if (count < 0) {
+ qmlInfo(this) << tr("move: invalid count");
+ } else if (from < 0 || from + count > model->m_compositor.count(fromGroup)) {
+ qmlInfo(this) << tr("move: from index out of range");
+ } else if (!model->m_compositor.verifyMoveTo(fromGroup, from, toGroup, to, count, d->group)) {
+ qmlInfo(this) << tr("move: to index out of range");
+ } else if (count > 0) {
+ QVector<Compositor::Remove> removes;
+ QVector<Compositor::Insert> inserts;
+
+ model->m_compositor.move(fromGroup, from, toGroup, to, count, d->group, &removes, &inserts);
+ model->itemsMoved(removes, inserts);
+ model->emitChanges();
+ }
+
+}
+
+/*!
+ \qmlsignal QtQml.Models2::DelegateModelGroup::onChanged(array removed, array inserted)
+
+ This handler is called when items have been removed from or inserted into the group.
+
+ Each object in the \a removed and \a inserted arrays has two values; the \e index of the first
+ item inserted or removed and a \e count of the number of consecutive items inserted or removed.
+
+ Each index is adjusted for previous changes with all removed items preceding any inserted
+ items.
+*/
+
+//============================================================================
+
+QQmlPartsModel::QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent)
+ : QQmlInstanceModel(*new QObjectPrivate, parent)
+ , m_model(model)
+ , m_part(part)
+ , m_compositorGroup(Compositor::Cache)
+ , m_inheritGroup(true)
+{
+ QQmlDelegateModelPrivate *d = QQmlDelegateModelPrivate::get(m_model);
+ if (d->m_cacheMetaType) {
+ QQmlDelegateModelGroupPrivate::get(d->m_groups[1])->emitters.insert(this);
+ m_compositorGroup = Compositor::Default;
+ } else {
+ d->m_pendingParts.insert(this);
+ }
+}
+
+QQmlPartsModel::~QQmlPartsModel()
+{
+}
+
+QString QQmlPartsModel::filterGroup() const
+{
+ if (m_inheritGroup)
+ return m_model->filterGroup();
+ return m_filterGroup;
+}
+
+void QQmlPartsModel::setFilterGroup(const QString &group)
+{
+ if (QQmlDelegateModelPrivate::get(m_model)->m_transaction) {
+ qmlInfo(this) << tr("The group of a DelegateModel cannot be changed within onChanged");
+ return;
+ }
+
+ if (m_filterGroup != group || m_inheritGroup) {
+ m_filterGroup = group;
+ m_inheritGroup = false;
+ updateFilterGroup();
+
+ emit filterGroupChanged();
+ }
+}
+
+void QQmlPartsModel::resetFilterGroup()
+{
+ if (!m_inheritGroup) {
+ m_inheritGroup = true;
+ updateFilterGroup();
+ emit filterGroupChanged();
+ }
+}
+
+void QQmlPartsModel::updateFilterGroup()
+{
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
+ if (!model->m_cacheMetaType)
+ return;
+
+ if (m_inheritGroup) {
+ if (m_filterGroup == model->m_filterGroup)
+ return;
+ m_filterGroup = model->m_filterGroup;
+ }
+
+ QQmlListCompositor::Group previousGroup = m_compositorGroup;
+ m_compositorGroup = Compositor::Default;
+ QQmlDelegateModelGroupPrivate::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;
+ }
+ }
+
+ QQmlDelegateModelGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this);
+ if (m_compositorGroup != previousGroup) {
+ QVector<QQmlChangeSet::Remove> removes;
+ QVector<QQmlChangeSet::Insert> inserts;
+ model->m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts);
+
+ QQmlChangeSet changeSet;
+ changeSet.move(removes, inserts);
+ if (!changeSet.isEmpty())
+ emit modelUpdated(changeSet, false);
+
+ if (changeSet.difference() != 0)
+ emit countChanged();
+ }
+}
+
+void QQmlPartsModel::updateFilterGroup(
+ Compositor::Group group, const QQmlChangeSet &changeSet)
+{
+ if (!m_inheritGroup)
+ return;
+
+ m_compositorGroup = group;
+ QQmlDelegateModelGroupPrivate::get(QQmlDelegateModelPrivate::get(m_model)->m_groups[m_compositorGroup])->emitters.insert(this);
+
+ if (!changeSet.isEmpty())
+ emit modelUpdated(changeSet, false);
+
+ if (changeSet.difference() != 0)
+ emit countChanged();
+
+ emit filterGroupChanged();
+}
+
+int QQmlPartsModel::count() const
+{
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
+ return model->m_delegate
+ ? model->m_compositor.count(m_compositorGroup)
+ : 0;
+}
+
+bool QQmlPartsModel::isValid() const
+{
+ return m_model->isValid();
+}
+
+QObject *QQmlPartsModel::object(int index, bool asynchronous)
+{
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
+
+ if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) {
+ qWarning() << "DelegateModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup);
+ return 0;
+ }
+
+ QObject *object = model->object(m_compositorGroup, index, asynchronous);
+
+ if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) {
+ QObject *part = package->part(m_part);
+ if (!part)
+ return 0;
+ m_packaged.insertMulti(part, package);
+ return part;
+ }
+
+ model->release(object);
+ if (!model->m_delegateValidated) {
+ if (object)
+ qmlInfo(model->m_delegate) << tr("Delegate component must be Package type.");
+ model->m_delegateValidated = true;
+ }
+
+ return 0;
+}
+
+QQmlInstanceModel::ReleaseFlags QQmlPartsModel::release(QObject *item)
+{
+ QQmlInstanceModel::ReleaseFlags flags = 0;
+
+ QHash<QObject *, QQuickPackage *>::iterator it = m_packaged.find(item);
+ if (it != m_packaged.end()) {
+ QQuickPackage *package = *it;
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
+ flags = model->release(package);
+ m_packaged.erase(it);
+ if (!m_packaged.contains(item))
+ flags &= ~Referenced;
+ if (flags & Destroyed)
+ QQmlDelegateModelPrivate::get(m_model)->emitDestroyingPackage(package);
+ }
+ return flags;
+}
+
+QString QQmlPartsModel::stringValue(int index, const QString &role)
+{
+ return QQmlDelegateModelPrivate::get(m_model)->stringValue(m_compositorGroup, index, role);
+}
+
+void QQmlPartsModel::setWatchedRoles(QList<QByteArray> roles)
+{
+ QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model);
+ model->m_adaptorModel.replaceWatchedRoles(m_watchedRoles, roles);
+ m_watchedRoles = roles;
+}
+
+int QQmlPartsModel::indexOf(QObject *item, QObject *) const
+{
+ QHash<QObject *, QQuickPackage *>::const_iterator it = m_packaged.find(item);
+ if (it != m_packaged.end()) {
+ if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(*it))
+ return cacheItem->groupIndex(m_compositorGroup);
+ }
+ return -1;
+}
+
+void QQmlPartsModel::createdPackage(int index, QQuickPackage *package)
+{
+ emit createdItem(index, package->part(m_part));
+}
+
+void QQmlPartsModel::initPackage(int index, QQuickPackage *package)
+{
+ emit initItem(index, package->part(m_part));
+}
+
+void QQmlPartsModel::destroyingPackage(QQuickPackage *package)
+{
+ QObject *item = package->part(m_part);
+ Q_ASSERT(!m_packaged.contains(item));
+ emit destroyingItem(item);
+}
+
+void QQmlPartsModel::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset)
+{
+ emit modelUpdated(changeSet, reset);
+ if (changeSet.difference() != 0)
+ emit countChanged();
+}
+
+//============================================================================
+
+v8::Handle<v8::Value> get_change_index(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ return info.This()->GetInternalField(0);
+}
+
+v8::Handle<v8::Value> get_change_count(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ return info.This()->GetInternalField(1);
+}
+
+v8::Handle<v8::Value> get_change_moveId(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ return info.This()->GetInternalField(2);
+}
+
+class QQmlDelegateModelGroupChangeArray : public QV8ObjectResource
+{
+ V8_RESOURCE_TYPE(ChangeSetArrayType)
+public:
+ QQmlDelegateModelGroupChangeArray(QV8Engine *engine)
+ : QV8ObjectResource(engine)
+ {
+ }
+
+ virtual quint32 count() const = 0;
+ virtual const QQmlChangeSet::Change &at(int index) const = 0;
+
+ static v8::Handle<v8::Value> get_change(quint32 index, const v8::AccessorInfo &info)
+ {
+ QQmlDelegateModelGroupChangeArray *array = v8_resource_cast<QQmlDelegateModelGroupChangeArray>(info.This());
+ V8ASSERT_TYPE(array, "Not a valid change array");
+
+ if (index >= array->count())
+ return v8::Undefined();
+
+ const QQmlChangeSet::Change &change = array->at(index);
+
+ v8::Local<v8::Object> object = engineData(array->engine)->constructorChange->NewInstance();
+ object->SetInternalField(0, v8::Int32::New(change.index));
+ object->SetInternalField(1, v8::Int32::New(change.count));
+ if (change.isMove())
+ object->SetInternalField(2, v8::Int32::New(change.moveId));
+
+ return object;
+ }
+
+ static v8::Handle<v8::Value> get_length(v8::Local<v8::String>, const v8::AccessorInfo &info)
+ {
+ QQmlDelegateModelGroupChangeArray *array = v8_resource_cast<QQmlDelegateModelGroupChangeArray>(info.This());
+ V8ASSERT_TYPE(array, "Not a valid change array");
+
+ return v8::Integer::New(array->count());
+ }
+
+ static v8::Local<v8::Function> constructor()
+ {
+ v8::Local<v8::FunctionTemplate> changeArray = v8::FunctionTemplate::New();
+ changeArray->InstanceTemplate()->SetHasExternalResource(true);
+ changeArray->InstanceTemplate()->SetIndexedPropertyHandler(get_change);
+ changeArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), get_length);
+ return changeArray->GetFunction();
+ }
+};
+
+class QQmlDelegateModelGroupRemoveArray : public QQmlDelegateModelGroupChangeArray
+{
+public:
+ QQmlDelegateModelGroupRemoveArray(QV8Engine *engine, const QVector<QQmlChangeSet::Remove> &changes)
+ : QQmlDelegateModelGroupChangeArray(engine)
+ , changes(changes)
+ {
+ }
+
+ quint32 count() const { return changes.count(); }
+ const QQmlChangeSet::Change &at(int index) const { return changes.at(index); }
+
+private:
+ QVector<QQmlChangeSet::Remove> changes;
+};
+
+class QQmlDelegateModelGroupInsertArray : public QQmlDelegateModelGroupChangeArray
+{
+public:
+ QQmlDelegateModelGroupInsertArray(QV8Engine *engine, const QVector<QQmlChangeSet::Insert> &changes)
+ : QQmlDelegateModelGroupChangeArray(engine)
+ , changes(changes)
+ {
+ }
+
+ quint32 count() const { return changes.count(); }
+ const QQmlChangeSet::Change &at(int index) const { return changes.at(index); }
+
+private:
+ QVector<QQmlChangeSet::Insert> changes;
+};
+
+QQmlDelegateModelEngineData::QQmlDelegateModelEngineData(QV8Engine *)
+{
+ strings = qPersistentNew(v8::Array::New(StringCount));
+ strings->Set(Model, v8::String::New("model"));
+ strings->Set(Groups, v8::String::New("groups"));
+ strings->Set(IsUnresolved, v8::String::New("isUnresolved"));
+ strings->Set(ItemsIndex, v8::String::New("itemsIndex"));
+ strings->Set(PersistedItemsIndex, v8::String::New("persistedItemsIndex"));
+ strings->Set(InItems, v8::String::New("inItems"));
+ strings->Set(InPersistedItems, v8::String::New("inPersistedItems"));
+
+ v8::Local<v8::FunctionTemplate> change = v8::FunctionTemplate::New();
+ change->InstanceTemplate()->SetAccessor(v8::String::New("index"), get_change_index);
+ change->InstanceTemplate()->SetAccessor(v8::String::New("count"), get_change_count);
+ change->InstanceTemplate()->SetAccessor(v8::String::New("moveId"), get_change_moveId);
+ change->InstanceTemplate()->SetInternalFieldCount(3);
+ constructorChange = qPersistentNew(change->GetFunction());
+ constructorChangeArray = qPersistentNew(QQmlDelegateModelGroupChangeArray::constructor());
+}
+
+QQmlDelegateModelEngineData::~QQmlDelegateModelEngineData()
+{
+ qPersistentDispose(strings);
+ qPersistentDispose(constructorChange);
+ qPersistentDispose(constructorChangeArray);
+}
+
+v8::Local<v8::Object> QQmlDelegateModelEngineData::array(
+ QV8Engine *engine, const QVector<QQmlChangeSet::Remove> &changes)
+{
+ v8::Local<v8::Object> array = constructorChangeArray->NewInstance();
+ array->SetExternalResource(new QQmlDelegateModelGroupRemoveArray(engine, changes));
+ return array;
+}
+
+v8::Local<v8::Object> QQmlDelegateModelEngineData::array(
+ QV8Engine *engine, const QVector<QQmlChangeSet::Insert> &changes)
+{
+ v8::Local<v8::Object> array = constructorChangeArray->NewInstance();
+ array->SetExternalResource(new QQmlDelegateModelGroupInsertArray(engine, changes));
+ return array;
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h
new file mode 100644
index 0000000000..5702c59787
--- /dev/null
+++ b/src/qml/types/qqmldelegatemodel_p.h
@@ -0,0 +1,234 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLDATAMODEL_P_H
+#define QQMLDATAMODEL_P_H
+
+#include <private/qtqmlglobal_p.h>
+#include <private/qqmllistcompositor_p.h>
+#include <private/qqmlobjectmodel_p.h>
+
+#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qstringlist.h>
+
+#include <private/qv8engine_p.h>
+#include <private/qqmlglobal_p.h>
+
+Q_DECLARE_METATYPE(QModelIndex)
+
+QT_BEGIN_NAMESPACE
+
+class QQmlChangeSet;
+class QQmlComponent;
+class QQuickPackage;
+class QQmlV8Function;
+class QQmlDelegateModelGroup;
+class QQmlDelegateModelAttached;
+class QQmlDelegateModelPrivate;
+
+
+class Q_QML_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QQmlDelegateModel)
+
+ Q_PROPERTY(QVariant model READ model WRITE setModel)
+ Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate)
+ Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup)
+ Q_PROPERTY(QQmlDelegateModelGroup *items READ items CONSTANT) //TODO : worth renaming?
+ Q_PROPERTY(QQmlDelegateModelGroup *persistedItems READ persistedItems CONSTANT)
+ Q_PROPERTY(QQmlListProperty<QQmlDelegateModelGroup> groups READ groups CONSTANT)
+ Q_PROPERTY(QObject *parts READ parts CONSTANT)
+ Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged)
+ Q_CLASSINFO("DefaultProperty", "delegate")
+ Q_INTERFACES(QQmlParserStatus)
+public:
+ QQmlDelegateModel();
+ QQmlDelegateModel(QQmlContext *, QObject *parent=0);
+ virtual ~QQmlDelegateModel();
+
+ void classBegin();
+ void componentComplete();
+
+ QVariant model() const;
+ void setModel(const QVariant &);
+
+ QQmlComponent *delegate() const;
+ void setDelegate(QQmlComponent *);
+
+ QVariant rootIndex() const;
+ void setRootIndex(const QVariant &root);
+
+ Q_INVOKABLE QVariant modelIndex(int idx) const;
+ Q_INVOKABLE QVariant parentModelIndex() const;
+
+ int count() const;
+ bool isValid() const { return delegate() != 0; }
+ QObject *object(int index, bool asynchronous=false);
+ ReleaseFlags release(QObject *object);
+ void cancel(int index);
+ virtual QString stringValue(int index, const QString &role);
+ virtual void setWatchedRoles(QList<QByteArray> roles);
+
+ int indexOf(QObject *object, QObject *objectContext) const;
+
+ QString filterGroup() const;
+ void setFilterGroup(const QString &group);
+ void resetFilterGroup();
+
+ QQmlDelegateModelGroup *items();
+ QQmlDelegateModelGroup *persistedItems();
+ QQmlListProperty<QQmlDelegateModelGroup> groups();
+ QObject *parts();
+
+ bool event(QEvent *);
+
+ static QQmlDelegateModelAttached *qmlAttachedProperties(QObject *obj);
+
+Q_SIGNALS:
+ void filterGroupChanged();
+ void defaultGroupsChanged();
+ void rootIndexChanged();
+
+private Q_SLOTS:
+ void _q_itemsChanged(int index, int count, const QVector<int> &roles);
+ void _q_itemsInserted(int index, int count);
+ void _q_itemsRemoved(int index, int count);
+ void _q_itemsMoved(int from, int to, int count);
+ void _q_modelReset();
+ void _q_rowsInserted(const QModelIndex &,int,int);
+ void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end);
+ void _q_rowsRemoved(const QModelIndex &,int,int);
+ void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int);
+ void _q_dataChanged(const QModelIndex&,const QModelIndex&,const QVector<int> &);
+ void _q_layoutChanged();
+
+private:
+ Q_DISABLE_COPY(QQmlDelegateModel)
+};
+
+class QQmlDelegateModelGroupPrivate;
+class Q_QML_PRIVATE_EXPORT QQmlDelegateModelGroup : 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:
+ QQmlDelegateModelGroup(QObject *parent = 0);
+ QQmlDelegateModelGroup(const QString &name, QQmlDelegateModel *model, int compositorType, QObject *parent = 0);
+ ~QQmlDelegateModelGroup();
+
+ QString name() const;
+ void setName(const QString &name);
+
+ int count() const;
+
+ bool defaultInclude() const;
+ void setDefaultInclude(bool include);
+
+ Q_INVOKABLE QQmlV8Handle get(int index);
+
+public Q_SLOTS:
+ void insert(QQmlV8Function *);
+ void create(QQmlV8Function *);
+ void resolve(QQmlV8Function *);
+ void remove(QQmlV8Function *);
+ void addGroups(QQmlV8Function *);
+ void removeGroups(QQmlV8Function *);
+ void setGroups(QQmlV8Function *);
+ void move(QQmlV8Function *);
+
+Q_SIGNALS:
+ void countChanged();
+ void nameChanged();
+ void defaultIncludeChanged();
+ void changed(const QQmlV8Handle &removed, const QQmlV8Handle &inserted);
+private:
+ Q_DECLARE_PRIVATE(QQmlDelegateModelGroup)
+};
+
+class QQmlDelegateModelItem;
+class QQmlDelegateModelAttachedMetaObject;
+class QQmlDelegateModelAttached : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQmlDelegateModel *model READ model CONSTANT)
+ Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged)
+ Q_PROPERTY(bool isUnresolved READ isUnresolved NOTIFY unresolvedChanged)
+public:
+ QQmlDelegateModelAttached(QObject *parent);
+ QQmlDelegateModelAttached(QQmlDelegateModelItem *cacheItem, QObject *parent);
+ ~QQmlDelegateModelAttached() {}
+
+ void setCacheItem(QQmlDelegateModelItem *item);
+
+ QQmlDelegateModel *model() const;
+
+ QStringList groups() const;
+ void setGroups(const QStringList &groups);
+
+ bool isUnresolved() const;
+
+ void emitChanges();
+
+ void emitUnresolvedChanged() { emit unresolvedChanged(); }
+
+Q_SIGNALS:
+ void groupsChanged();
+ void unresolvedChanged();
+
+public:
+ QQmlDelegateModelItem *m_cacheItem;
+ int m_previousGroups;
+ int m_currentIndex[QQmlListCompositor::MaximumGroupCount];
+ int m_previousIndex[QQmlListCompositor::MaximumGroupCount];
+
+ friend class QQmlDelegateModelAttachedMetaObject;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQmlDelegateModel)
+QML_DECLARE_TYPEINFO(QQmlDelegateModel, QML_HAS_ATTACHED_PROPERTIES)
+QML_DECLARE_TYPE(QQmlDelegateModelGroup)
+
+#endif // QQMLDATAMODEL_P_H
diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h
new file mode 100644
index 0000000000..68242f433d
--- /dev/null
+++ b/src/qml/types/qqmldelegatemodel_p_p.h
@@ -0,0 +1,411 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLDATAMODEL_P_P_H
+#define QQMLDATAMODEL_P_P_H
+
+#include "qqmldelegatemodel_p.h"
+
+
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlincubator.h>
+
+#include <private/qqmladaptormodel_p.h>
+#include <private/qqmlopenmetaobject_p.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+typedef QQmlListCompositor Compositor;
+
+class QQmlDelegateModelAttachedMetaObject;
+
+class QQmlDelegateModelItemMetaType : public QQmlRefCount
+{
+public:
+ QQmlDelegateModelItemMetaType(QV8Engine *engine, QQmlDelegateModel *model, const QStringList &groupNames);
+ ~QQmlDelegateModelItemMetaType();
+
+ void initializeMetaObject();
+ void initializeConstructor();
+
+ int parseGroups(const QStringList &groupNames) const;
+ int parseGroups(const v8::Local<v8::Value> &groupNames) const;
+
+ static void release_index(v8::Persistent<v8::Value> object, void *parameter);
+ static void release_model(v8::Persistent<v8::Value> object, void *parameter);
+
+ static v8::Handle<v8::Value> get_model(v8::Local<v8::String>, const v8::AccessorInfo &info);
+ static v8::Handle<v8::Value> get_groups(v8::Local<v8::String>, const v8::AccessorInfo &info);
+ static void set_groups(
+ v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info);
+ static v8::Handle<v8::Value> get_member(v8::Local<v8::String>, const v8::AccessorInfo &info);
+ static void set_member(
+ v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info);
+ static v8::Handle<v8::Value> get_index(v8::Local<v8::String>, const v8::AccessorInfo &info);
+
+ QQmlGuard<QQmlDelegateModel> model;
+ const int groupCount;
+ QV8Engine * const v8Engine;
+ QQmlDelegateModelAttachedMetaObject *metaObject;
+ const QStringList groupNames;
+ v8::Persistent<v8::ObjectTemplate> constructor;
+};
+
+class QQmlAdaptorModel;
+class QQDMIncubationTask;
+
+class QQmlDelegateModelItem : public QObject, public QV8ObjectResource
+{
+ Q_OBJECT
+ Q_PROPERTY(int index READ modelIndex NOTIFY modelIndexChanged)
+ Q_PROPERTY(QObject *model READ modelObject CONSTANT)
+ V8_RESOURCE_TYPE(VisualDataItemType)
+public:
+ QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, int modelIndex);
+ ~QQmlDelegateModelItem();
+
+ void referenceObject() { ++objectRef; }
+ bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); }
+ bool isObjectReferenced() const { return objectRef != 0 || (groups & Compositor::PersistedFlag); }
+
+ bool isReferenced() const {
+ return scriptRef
+ || incubationTask
+ || ((groups & Compositor::UnresolvedFlag) && (groups & Compositor::GroupMask));
+ }
+
+ void Dispose();
+
+ QObject *modelObject() { return this; }
+
+ void incubateObject(
+ QQmlComponent *component,
+ QQmlEngine *engine,
+ QQmlContextData *context,
+ QQmlContextData *forContext);
+ void destroyObject();
+
+ static QQmlDelegateModelItem *dataForObject(QObject *object);
+
+ int groupIndex(Compositor::Group group);
+
+ int modelIndex() const { return index; }
+ void setModelIndex(int idx) { index = idx; emit modelIndexChanged(); }
+
+ virtual v8::Handle<v8::Value> get() { return engine->newQObject(this); }
+
+ virtual void setValue(const QString &role, const QVariant &value) { Q_UNUSED(role); Q_UNUSED(value); }
+ virtual bool resolveIndex(const QQmlAdaptorModel &, int) { return false; }
+
+ QQmlDelegateModelItemMetaType * const metaType;
+ QQmlContextData *contextData;
+ QObject *object;
+ QQmlDelegateModelAttached *attached;
+ QQDMIncubationTask *incubationTask;
+ int objectRef;
+ int scriptRef;
+ int groups;
+ int index;
+
+
+Q_SIGNALS:
+ void modelIndexChanged();
+
+protected:
+ void objectDestroyed(QObject *);
+};
+
+
+class QQmlDelegateModelPrivate;
+class QQDMIncubationTask : public QQmlIncubator
+{
+public:
+ QQDMIncubationTask(QQmlDelegateModelPrivate *l, IncubationMode mode)
+ : QQmlIncubator(mode)
+ , incubating(0)
+ , vdm(l) {}
+
+ virtual void statusChanged(Status);
+ virtual void setInitialState(QObject *);
+
+ QQmlDelegateModelItem *incubating;
+ QQmlDelegateModelPrivate *vdm;
+ int index[QQmlListCompositor::MaximumGroupCount];
+};
+
+
+class QQmlDelegateModelGroupEmitter
+{
+public:
+ virtual ~QQmlDelegateModelGroupEmitter() {}
+ virtual void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) = 0;
+ virtual void createdPackage(int, QQuickPackage *) {}
+ virtual void initPackage(int, QQuickPackage *) {}
+ virtual void destroyingPackage(QQuickPackage *) {}
+
+ QIntrusiveListNode emitterNode;
+};
+
+typedef QIntrusiveList<QQmlDelegateModelGroupEmitter, &QQmlDelegateModelGroupEmitter::emitterNode> QQmlDelegateModelGroupEmitterList;
+
+class QQmlDelegateModelGroupPrivate : public QObjectPrivate
+{
+public:
+ Q_DECLARE_PUBLIC(QQmlDelegateModelGroup)
+
+ QQmlDelegateModelGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {}
+
+ static QQmlDelegateModelGroupPrivate *get(QQmlDelegateModelGroup *group) {
+ return static_cast<QQmlDelegateModelGroupPrivate *>(QObjectPrivate::get(group)); }
+
+ void setModel(QQmlDelegateModel *model, Compositor::Group group);
+ bool isChangedConnected();
+ void emitChanges(QV8Engine *engine);
+ void emitModelUpdated(bool reset);
+
+ void createdPackage(int index, QQuickPackage *package);
+ void initPackage(int index, QQuickPackage *package);
+ void destroyingPackage(QQuickPackage *package);
+
+ bool parseIndex(const v8::Local<v8::Value> &value, int *index, Compositor::Group *group) const;
+ bool parseGroupArgs(
+ QQmlV8Function *args, Compositor::Group *group, int *index, int *count, int *groups) const;
+
+ Compositor::Group group;
+ QQmlGuard<QQmlDelegateModel> model;
+ QQmlDelegateModelGroupEmitterList emitters;
+ QQmlChangeSet changeSet;
+ QString name;
+ bool defaultInclude;
+};
+
+class QQmlDelegateModelParts;
+
+class QQmlDelegateModelPrivate : public QObjectPrivate, public QQmlDelegateModelGroupEmitter
+{
+ Q_DECLARE_PUBLIC(QQmlDelegateModel)
+public:
+ QQmlDelegateModelPrivate(QQmlContext *);
+ ~QQmlDelegateModelPrivate();
+
+ static QQmlDelegateModelPrivate *get(QQmlDelegateModel *m) {
+ return static_cast<QQmlDelegateModelPrivate *>(QObjectPrivate::get(m));
+ }
+
+ void init();
+ void connectModel(QQmlAdaptorModel *model);
+
+ QObject *object(Compositor::Group group, int index, bool asynchronous);
+ QQmlDelegateModel::ReleaseFlags release(QObject *object);
+ QString stringValue(Compositor::Group group, int index, const QString &name);
+ void emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package);
+ void emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package);
+ void emitCreatedItem(QQDMIncubationTask *incubationTask, QObject *item) {
+ emit q_func()->createdItem(incubationTask->index[m_compositorGroup], item); }
+ void emitInitItem(QQDMIncubationTask *incubationTask, QObject *item) {
+ emit q_func()->initItem(incubationTask->index[m_compositorGroup], item); }
+ void emitDestroyingPackage(QQuickPackage *package);
+ void emitDestroyingItem(QObject *item) { emit q_func()->destroyingItem(item); }
+ void removeCacheItem(QQmlDelegateModelItem *cacheItem);
+
+ void updateFilterGroup();
+
+ void addGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags);
+ void removeGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags);
+ void setGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags);
+
+ void itemsInserted(
+ const QVector<Compositor::Insert> &inserts,
+ QVarLengthArray<QVector<QQmlChangeSet::Insert>, Compositor::MaximumGroupCount> *translatedInserts,
+ QHash<int, QList<QQmlDelegateModelItem *> > *movedItems = 0);
+ void itemsInserted(const QVector<Compositor::Insert> &inserts);
+ void itemsRemoved(
+ const QVector<Compositor::Remove> &removes,
+ QVarLengthArray<QVector<QQmlChangeSet::Remove>, Compositor::MaximumGroupCount> *translatedRemoves,
+ QHash<int, QList<QQmlDelegateModelItem *> > *movedItems = 0);
+ void itemsRemoved(const QVector<Compositor::Remove> &removes);
+ void itemsMoved(
+ const QVector<Compositor::Remove> &removes, const QVector<Compositor::Insert> &inserts);
+ void itemsChanged(const QVector<Compositor::Change> &changes);
+ template <typename T> static v8::Local<v8::Array> buildChangeList(const QVector<T> &changes);
+ void emitChanges();
+ void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset);
+
+ bool insert(Compositor::insert_iterator &before, const v8::Local<v8::Object> &object, int groups);
+
+ static void group_append(QQmlListProperty<QQmlDelegateModelGroup> *property, QQmlDelegateModelGroup *group);
+ static int group_count(QQmlListProperty<QQmlDelegateModelGroup> *property);
+ static QQmlDelegateModelGroup *group_at(QQmlListProperty<QQmlDelegateModelGroup> *property, int index);
+
+ void releaseIncubator(QQDMIncubationTask *incubationTask);
+ void incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status);
+ void setInitialState(QQDMIncubationTask *incubationTask, QObject *o);
+
+ QQmlAdaptorModel m_adaptorModel;
+ QQmlListCompositor m_compositor;
+ QQmlComponent *m_delegate;
+ QQmlDelegateModelItemMetaType *m_cacheMetaType;
+ QQmlContext *m_context;
+ QQmlDelegateModelParts *m_parts;
+ QQmlDelegateModelGroupEmitterList m_pendingParts;
+
+ QList<QQmlDelegateModelItem *> m_cache;
+ QList<QQDMIncubationTask *> m_finishedIncubating;
+ QList<QByteArray> m_watchedRoles;
+
+ QString m_filterGroup;
+
+ int m_count;
+ int m_groupCount;
+
+ QQmlListCompositor::Group m_compositorGroup;
+ bool m_complete : 1;
+ bool m_delegateValidated : 1;
+ bool m_reset : 1;
+ bool m_transaction : 1;
+ bool m_incubatorCleanupScheduled : 1;
+
+ union {
+ struct {
+ QQmlDelegateModelGroup *m_cacheItems;
+ QQmlDelegateModelGroup *m_items;
+ QQmlDelegateModelGroup *m_persistedItems;
+ };
+ QQmlDelegateModelGroup *m_groups[Compositor::MaximumGroupCount];
+ };
+};
+
+class QQmlPartsModel : public QQmlInstanceModel, public QQmlDelegateModelGroupEmitter
+{
+ Q_OBJECT
+ Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup)
+public:
+ QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent = 0);
+ ~QQmlPartsModel();
+
+ QString filterGroup() const;
+ void setFilterGroup(const QString &group);
+ void resetFilterGroup();
+ void updateFilterGroup();
+ void updateFilterGroup(Compositor::Group group, const QQmlChangeSet &changeSet);
+
+ int count() const;
+ bool isValid() const;
+ QObject *object(int index, bool asynchronous=false);
+ ReleaseFlags release(QObject *item);
+ QString stringValue(int index, const QString &role);
+ QList<QByteArray> watchedRoles() const { return m_watchedRoles; }
+ void setWatchedRoles(QList<QByteArray> roles);
+
+ int indexOf(QObject *item, QObject *objectContext) const;
+
+ void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset);
+
+ void createdPackage(int index, QQuickPackage *package);
+ void initPackage(int index, QQuickPackage *package);
+ void destroyingPackage(QQuickPackage *package);
+
+Q_SIGNALS:
+ void filterGroupChanged();
+
+private:
+ QQmlDelegateModel *m_model;
+ QHash<QObject *, QQuickPackage *> m_packaged;
+ QString m_part;
+ QString m_filterGroup;
+ QList<QByteArray> m_watchedRoles;
+ Compositor::Group m_compositorGroup;
+ bool m_inheritGroup;
+};
+
+class QMetaPropertyBuilder;
+
+class QQmlDelegateModelPartsMetaObject : public QQmlOpenMetaObject
+{
+public:
+ QQmlDelegateModelPartsMetaObject(QObject *parent)
+ : QQmlOpenMetaObject(parent) {}
+
+ virtual void propertyCreated(int, QMetaPropertyBuilder &);
+ virtual QVariant initialValue(int);
+};
+
+class QQmlDelegateModelParts : public QObject
+{
+Q_OBJECT
+public:
+ QQmlDelegateModelParts(QQmlDelegateModel *parent);
+
+ QQmlDelegateModel *model;
+ QList<QQmlPartsModel *> models;
+};
+
+class QQmlDelegateModelAttachedMetaObject : public QAbstractDynamicMetaObject, public QQmlRefCount
+{
+public:
+ QQmlDelegateModelAttachedMetaObject(
+ QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject);
+ ~QQmlDelegateModelAttachedMetaObject();
+
+ void objectDestroyed(QObject *);
+ int metaCall(QObject *, QMetaObject::Call, int _id, void **);
+
+private:
+ QQmlDelegateModelItemMetaType * const metaType;
+ QMetaObject * const metaObject;
+ const int memberPropertyOffset;
+ const int indexPropertyOffset;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp
new file mode 100644
index 0000000000..9609e91b1c
--- /dev/null
+++ b/src/qml/types/qqmllistmodel.cpp
@@ -0,0 +1,2588 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmllistmodel_p_p.h"
+#include "qqmllistmodelworkeragent_p.h"
+#include <private/qqmlopenmetaobject_p.h>
+#include <private/qqmljsast_p.h>
+#include <private/qqmljsengine_p.h>
+
+
+#include <private/qqmlcustomparser_p.h>
+#include <private/qqmlscript_p.h>
+#include <private/qqmlengine_p.h>
+#include <qqmlcontext.h>
+#include <qqmlinfo.h>
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qstack.h>
+#include <QXmlStreamReader>
+
+QT_BEGIN_NAMESPACE
+
+// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models.
+enum { MIN_LISTMODEL_UID = 1024 };
+
+static QAtomicInt uidCounter(MIN_LISTMODEL_UID);
+
+template <typename T>
+static bool isMemoryUsed(const char *mem)
+{
+ for (size_t i=0 ; i < sizeof(T) ; ++i) {
+ if (mem[i] != 0)
+ return true;
+ }
+
+ return false;
+}
+
+static QString roleTypeName(ListLayout::Role::DataType t)
+{
+ QString result;
+ const char *roleTypeNames[] = { "String", "Number", "Bool", "List", "QObject", "VariantMap", "DateTime" };
+
+ if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType)
+ result = QString::fromLatin1(roleTypeNames[t]);
+
+ return result;
+}
+
+const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type)
+{
+ QStringHash<Role *>::Node *node = roleHash.findNode(key);
+ if (node) {
+ const Role &r = *node->value;
+ if (type != r.type)
+ qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type));
+ return r;
+ }
+
+ return createRole(key, type);
+}
+
+const ListLayout::Role &ListLayout::getRoleOrCreate(v8::Handle<v8::String> key, Role::DataType type)
+{
+ QHashedV8String hashedKey(key);
+ QStringHash<Role *>::Node *node = roleHash.findNode(hashedKey);
+ if (node) {
+ const Role &r = *node->value;
+ if (type != r.type)
+ qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type));
+ return r;
+ }
+
+ QString qkey;
+ qkey.resize(key->Length());
+ key->Write(reinterpret_cast<uint16_t*>(qkey.data()));
+
+ return createRole(qkey, type);
+}
+
+const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type)
+{
+ const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QQmlGuard<QObject>), sizeof(QVariantMap), sizeof(QDateTime) };
+ const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime) };
+
+ Role *r = new Role;
+ r->name = key;
+ r->type = type;
+
+ if (type == Role::List) {
+ r->subLayout = new ListLayout;
+ } else {
+ r->subLayout = 0;
+ }
+
+ int dataSize = dataSizes[type];
+ int dataAlignment = dataAlignments[type];
+
+ int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1);
+ if (dataOffset + dataSize > ListElement::BLOCK_SIZE) {
+ r->blockIndex = ++currentBlock;
+ r->blockOffset = 0;
+ currentBlockOffset = dataSize;
+ } else {
+ r->blockIndex = currentBlock;
+ r->blockOffset = dataOffset;
+ currentBlockOffset = dataOffset + dataSize;
+ }
+
+ int roleIndex = roles.count();
+ r->index = roleIndex;
+
+ roles.append(r);
+ roleHash.insert(key, r);
+
+ return *r;
+}
+
+ListLayout::ListLayout(const ListLayout *other) : currentBlock(0), currentBlockOffset(0)
+{
+ for (int i=0 ; i < other->roles.count() ; ++i) {
+ Role *role = new Role(other->roles[i]);
+ roles.append(role);
+ roleHash.insert(role->name, role);
+ }
+ currentBlockOffset = other->currentBlockOffset;
+ currentBlock = other->currentBlock;
+}
+
+ListLayout::~ListLayout()
+{
+ for (int i=0 ; i < roles.count() ; ++i) {
+ delete roles[i];
+ }
+}
+
+void ListLayout::sync(ListLayout *src, ListLayout *target)
+{
+ int roleOffset = target->roles.count();
+ int newRoleCount = src->roles.count() - roleOffset;
+
+ for (int i=0 ; i < newRoleCount ; ++i) {
+ Role *role = new Role(src->roles[roleOffset + i]);
+ target->roles.append(role);
+ target->roleHash.insert(role->name, role);
+ }
+
+ target->currentBlockOffset = src->currentBlockOffset;
+ target->currentBlock = src->currentBlock;
+}
+
+ListLayout::Role::Role(const Role *other)
+{
+ name = other->name;
+ type = other->type;
+ blockIndex = other->blockIndex;
+ blockOffset = other->blockOffset;
+ index = other->index;
+ if (other->subLayout)
+ subLayout = new ListLayout(other->subLayout);
+ else
+ subLayout = 0;
+}
+
+ListLayout::Role::~Role()
+{
+ delete subLayout;
+}
+
+const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QVariant &data)
+{
+ Role::DataType type;
+
+ switch (data.type()) {
+ case QVariant::Double: type = Role::Number; break;
+ case QVariant::Int: type = Role::Number; break;
+ case QVariant::UserType: type = Role::List; break;
+ case QVariant::Bool: type = Role::Bool; break;
+ case QVariant::String: type = Role::String; break;
+ case QVariant::Map: type = Role::VariantMap; break;
+ default: type = Role::Invalid; break;
+ }
+
+ if (type == Role::Invalid) {
+ qmlInfo(0) << "Can't create role for unsupported data type";
+ return 0;
+ }
+
+ return &getRoleOrCreate(key, type);
+}
+
+const ListLayout::Role *ListLayout::getExistingRole(const QString &key)
+{
+ Role *r = 0;
+ QStringHash<Role *>::Node *node = roleHash.findNode(key);
+ if (node)
+ r = node->value;
+ return r;
+}
+
+const ListLayout::Role *ListLayout::getExistingRole(v8::Handle<v8::String> key)
+{
+ Role *r = 0;
+ QHashedV8String hashedKey(key);
+ QStringHash<Role *>::Node *node = roleHash.findNode(hashedKey);
+ if (node)
+ r = node->value;
+ return r;
+}
+
+ModelObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex)
+{
+ ListElement *e = elements[elementIndex];
+ if (e->m_objectCache == 0) {
+ e->m_objectCache = new ModelObject(model, elementIndex);
+ }
+ return e->m_objectCache;
+}
+
+void ListModel::sync(ListModel *src, ListModel *target, QHash<int, ListModel *> *targetModelHash)
+{
+ // Sanity check
+ target->m_uid = src->m_uid;
+ if (targetModelHash)
+ targetModelHash->insert(target->m_uid, target);
+
+ // Build hash of elements <-> uid for each of the lists
+ QHash<int, ElementSync> elementHash;
+ for (int i=0 ; i < target->elements.count() ; ++i) {
+ ListElement *e = target->elements.at(i);
+ int uid = e->getUid();
+ ElementSync sync;
+ sync.target = e;
+ elementHash.insert(uid, sync);
+ }
+ for (int i=0 ; i < src->elements.count() ; ++i) {
+ ListElement *e = src->elements.at(i);
+ int uid = e->getUid();
+
+ QHash<int, ElementSync>::iterator it = elementHash.find(uid);
+ if (it == elementHash.end()) {
+ ElementSync sync;
+ sync.src = e;
+ elementHash.insert(uid, sync);
+ } else {
+ ElementSync &sync = it.value();
+ sync.src = e;
+ }
+ }
+
+ // Get list of elements that are in the target but no longer in the source. These get deleted first.
+ QHash<int, ElementSync>::iterator it = elementHash.begin();
+ QHash<int, ElementSync>::iterator end = elementHash.end();
+ while (it != end) {
+ const ElementSync &s = it.value();
+ if (s.src == 0) {
+ s.target->destroy(target->m_layout);
+ target->elements.removeOne(s.target);
+ delete s.target;
+ }
+ ++it;
+ }
+
+ // Sync the layouts
+ ListLayout::sync(src->m_layout, target->m_layout);
+
+ // Clear the target list, and append in correct order from the source
+ target->elements.clear();
+ for (int i=0 ; i < src->elements.count() ; ++i) {
+ ListElement *srcElement = src->elements.at(i);
+ it = elementHash.find(srcElement->getUid());
+ const ElementSync &s = it.value();
+ ListElement *targetElement = s.target;
+ if (targetElement == 0) {
+ targetElement = new ListElement(srcElement->getUid());
+ }
+ ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout, targetModelHash);
+ target->elements.append(targetElement);
+ }
+
+ target->updateCacheIndices();
+
+ // Update values stored in target meta objects
+ for (int i=0 ; i < target->elements.count() ; ++i) {
+ ListElement *e = target->elements[i];
+ if (e->m_objectCache)
+ e->m_objectCache->updateValues();
+ }
+}
+
+ListModel::ListModel(ListLayout *layout, QQmlListModel *modelCache, int uid) : m_layout(layout), m_modelCache(modelCache)
+{
+ if (uid == -1)
+ uid = uidCounter.fetchAndAddOrdered(1);
+ m_uid = uid;
+}
+
+void ListModel::destroy()
+{
+ clear();
+ m_uid = -1;
+ m_layout = 0;
+ if (m_modelCache && m_modelCache->m_primary == false)
+ delete m_modelCache;
+ m_modelCache = 0;
+}
+
+int ListModel::appendElement()
+{
+ int elementIndex = elements.count();
+ newElement(elementIndex);
+ return elementIndex;
+}
+
+void ListModel::insertElement(int index)
+{
+ newElement(index);
+ updateCacheIndices();
+}
+
+void ListModel::move(int from, int to, int n)
+{
+ if (from > to) {
+ // Only move forwards - flip if backwards moving
+ int tfrom = from;
+ int tto = to;
+ from = tto;
+ to = tto+n;
+ n = tfrom-tto;
+ }
+
+ QPODVector<ListElement *, 4> store;
+ for (int i=0 ; i < (to-from) ; ++i)
+ store.append(elements[from+n+i]);
+ for (int i=0 ; i < n ; ++i)
+ store.append(elements[from+i]);
+ for (int i=0 ; i < store.count() ; ++i)
+ elements[from+i] = store[i];
+
+ updateCacheIndices();
+}
+
+void ListModel::newElement(int index)
+{
+ ListElement *e = new ListElement;
+ elements.insert(index, e);
+}
+
+void ListModel::updateCacheIndices()
+{
+ for (int i=0 ; i < elements.count() ; ++i) {
+ ListElement *e = elements.at(i);
+ if (e->m_objectCache) {
+ e->m_objectCache->m_elementIndex = i;
+ }
+ }
+}
+
+QVariant ListModel::getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV8Engine *eng)
+{
+ ListElement *e = elements[elementIndex];
+ const ListLayout::Role &r = m_layout->getExistingRole(roleIndex);
+ return e->getProperty(r, owner, eng);
+}
+
+ListModel *ListModel::getListProperty(int elementIndex, const ListLayout::Role &role)
+{
+ ListElement *e = elements[elementIndex];
+ return e->getListProperty(role);
+}
+
+void ListModel::set(int elementIndex, v8::Handle<v8::Object> object, QVector<int> *roles, QV8Engine *eng)
+{
+ ListElement *e = elements[elementIndex];
+
+ v8::Local<v8::Array> propertyNames = object->GetPropertyNames();
+ int propertyCount = propertyNames->Length();
+
+ for (int i=0 ; i < propertyCount ; ++i) {
+ v8::Local<v8::String> propertyName = propertyNames->Get(i)->ToString();
+ v8::Local<v8::Value> propertyValue = object->Get(propertyName);
+
+ // Check if this key exists yet
+ int roleIndex = -1;
+
+ // Add the value now
+ if (propertyValue->IsString()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String);
+ v8::Handle<v8::String> jsString = propertyValue->ToString();
+ QString qstr;
+ qstr.resize(jsString->Length());
+ jsString->Write(reinterpret_cast<uint16_t*>(qstr.data()));
+ roleIndex = e->setStringProperty(r, qstr);
+ } else if (propertyValue->IsNumber()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number);
+ roleIndex = e->setDoubleProperty(r, propertyValue->NumberValue());
+ } else if (propertyValue->IsArray()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
+ ListModel *subModel = new ListModel(r.subLayout, 0, -1);
+
+ v8::Handle<v8::Array> subArray = v8::Handle<v8::Array>::Cast(propertyValue);
+ int arrayLength = subArray->Length();
+ for (int j=0 ; j < arrayLength ; ++j) {
+ v8::Handle<v8::Object> subObject = subArray->Get(j)->ToObject();
+ subModel->append(subObject, eng);
+ }
+
+ roleIndex = e->setListProperty(r, subModel);
+ } else if (propertyValue->IsBoolean()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool);
+ roleIndex = e->setBoolProperty(r, propertyValue->BooleanValue());
+ } else if (propertyValue->IsDate()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime);
+ QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle<v8::Date>::Cast(propertyValue)->NumberValue());
+ roleIndex = e->setDateTimeProperty(r, dt);
+ } else if (propertyValue->IsObject()) {
+ QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource();
+ if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
+ QObject *o = QV8QObjectWrapper::toQObject(r);
+ const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
+ if (role.type == ListLayout::Role::QObject)
+ roleIndex = e->setQObjectProperty(role, o);
+ } else {
+ const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap);
+ if (role.type == ListLayout::Role::VariantMap)
+ roleIndex = e->setVariantMapProperty(role, propertyValue->ToObject(), eng);
+ }
+ } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) {
+ const ListLayout::Role *r = m_layout->getExistingRole(propertyName);
+ if (r)
+ e->clearProperty(*r);
+ }
+
+ if (roleIndex != -1)
+ roles->append(roleIndex);
+ }
+
+ if (e->m_objectCache) {
+ e->m_objectCache->updateValues(*roles);
+ }
+}
+
+void ListModel::set(int elementIndex, v8::Handle<v8::Object> object, QV8Engine *eng)
+{
+ ListElement *e = elements[elementIndex];
+
+ v8::Local<v8::Array> propertyNames = object->GetPropertyNames();
+ int propertyCount = propertyNames->Length();
+
+ for (int i=0 ; i < propertyCount ; ++i) {
+ v8::Local<v8::String> propertyName = propertyNames->Get(i)->ToString();
+ v8::Local<v8::Value> propertyValue = object->Get(propertyName);
+
+ // Add the value now
+ if (propertyValue->IsString()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String);
+ if (r.type == ListLayout::Role::String) {
+ v8::Handle<v8::String> jsString = propertyValue->ToString();
+ QString qstr;
+ qstr.resize(jsString->Length());
+ jsString->Write(reinterpret_cast<uint16_t*>(qstr.data()));
+ e->setStringPropertyFast(r, qstr);
+ }
+ } else if (propertyValue->IsNumber()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number);
+ if (r.type == ListLayout::Role::Number) {
+ e->setDoublePropertyFast(r, propertyValue->NumberValue());
+ }
+ } else if (propertyValue->IsArray()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
+ if (r.type == ListLayout::Role::List) {
+ ListModel *subModel = new ListModel(r.subLayout, 0, -1);
+
+ v8::Handle<v8::Array> subArray = v8::Handle<v8::Array>::Cast(propertyValue);
+ int arrayLength = subArray->Length();
+ for (int j=0 ; j < arrayLength ; ++j) {
+ v8::Handle<v8::Object> subObject = subArray->Get(j)->ToObject();
+ subModel->append(subObject, eng);
+ }
+
+ e->setListPropertyFast(r, subModel);
+ }
+ } else if (propertyValue->IsBoolean()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool);
+ if (r.type == ListLayout::Role::Bool) {
+ e->setBoolPropertyFast(r, propertyValue->BooleanValue());
+ }
+ } else if (propertyValue->IsDate()) {
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime);
+ if (r.type == ListLayout::Role::DateTime) {
+ QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle<v8::Date>::Cast(propertyValue)->NumberValue());
+ e->setDateTimePropertyFast(r, dt);
+ }
+ } else if (propertyValue->IsObject()) {
+ QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource();
+ if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
+ QObject *o = QV8QObjectWrapper::toQObject(r);
+ const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
+ if (r.type == ListLayout::Role::QObject)
+ e->setQObjectPropertyFast(r, o);
+ } else {
+ const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap);
+ if (role.type == ListLayout::Role::VariantMap)
+ e->setVariantMapFast(role, propertyValue->ToObject(), eng);
+ }
+ } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) {
+ const ListLayout::Role *r = m_layout->getExistingRole(propertyName);
+ if (r)
+ e->clearProperty(*r);
+ }
+ }
+}
+
+void ListModel::clear()
+{
+ int elementCount = elements.count();
+ for (int i=0 ; i < elementCount ; ++i) {
+ elements[i]->destroy(m_layout);
+ delete elements[i];
+ }
+ elements.clear();
+}
+
+void ListModel::remove(int index, int count)
+{
+ for (int i=0 ; i < count ; ++i) {
+ elements[index+i]->destroy(m_layout);
+ delete elements[index+i];
+ }
+ elements.remove(index, count);
+ updateCacheIndices();
+}
+
+void ListModel::insert(int elementIndex, v8::Handle<v8::Object> object, QV8Engine *eng)
+{
+ insertElement(elementIndex);
+ set(elementIndex, object, eng);
+}
+
+int ListModel::append(v8::Handle<v8::Object> object, QV8Engine *eng)
+{
+ int elementIndex = appendElement();
+ set(elementIndex, object, eng);
+ return elementIndex;
+}
+
+int ListModel::setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data)
+{
+ int roleIndex = -1;
+
+ if (elementIndex >= 0 && elementIndex < elements.count()) {
+ ListElement *e = elements[elementIndex];
+
+ const ListLayout::Role *r = m_layout->getRoleOrCreate(key, data);
+ if (r) {
+ roleIndex = e->setVariantProperty(*r, data);
+
+ if (roleIndex != -1 && e->m_objectCache) {
+ QVector<int> roles;
+ roles << roleIndex;
+ e->m_objectCache->updateValues(roles);
+ }
+ }
+ }
+
+ return roleIndex;
+}
+
+int ListModel::setExistingProperty(int elementIndex, const QString &key, v8::Handle<v8::Value> data, QV8Engine *eng)
+{
+ int roleIndex = -1;
+
+ if (elementIndex >= 0 && elementIndex < elements.count()) {
+ ListElement *e = elements[elementIndex];
+ const ListLayout::Role *r = m_layout->getExistingRole(key);
+ if (r)
+ roleIndex = e->setJsProperty(*r, data, eng);
+ }
+
+ return roleIndex;
+}
+
+inline char *ListElement::getPropertyMemory(const ListLayout::Role &role)
+{
+ ListElement *e = this;
+ int blockIndex = 0;
+ while (blockIndex < role.blockIndex) {
+ if (e->next == 0) {
+ e->next = new ListElement;
+ e->next->uid = uid;
+ }
+ e = e->next;
+ ++blockIndex;
+ }
+
+ char *mem = &e->data[role.blockOffset];
+ return mem;
+}
+
+QString *ListElement::getStringProperty(const ListLayout::Role &role)
+{
+ char *mem = getPropertyMemory(role);
+ QString *s = reinterpret_cast<QString *>(mem);
+ return s->data_ptr() ? s : 0;
+}
+
+QObject *ListElement::getQObjectProperty(const ListLayout::Role &role)
+{
+ char *mem = getPropertyMemory(role);
+ QQmlGuard<QObject> *o = reinterpret_cast<QQmlGuard<QObject> *>(mem);
+ return o->data();
+}
+
+QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role)
+{
+ QVariantMap *map = 0;
+
+ char *mem = getPropertyMemory(role);
+ if (isMemoryUsed<QVariantMap>(mem))
+ map = reinterpret_cast<QVariantMap *>(mem);
+
+ return map;
+}
+
+QDateTime *ListElement::getDateTimeProperty(const ListLayout::Role &role)
+{
+ QDateTime *dt = 0;
+
+ char *mem = getPropertyMemory(role);
+ if (isMemoryUsed<QDateTime>(mem))
+ dt = reinterpret_cast<QDateTime *>(mem);
+
+ return dt;
+}
+
+QQmlGuard<QObject> *ListElement::getGuardProperty(const ListLayout::Role &role)
+{
+ char *mem = getPropertyMemory(role);
+
+ bool existingGuard = false;
+ for (size_t i=0 ; i < sizeof(QQmlGuard<QObject>) ; ++i) {
+ if (mem[i] != 0) {
+ existingGuard = true;
+ break;
+ }
+ }
+
+ QQmlGuard<QObject> *o = 0;
+
+ if (existingGuard)
+ o = reinterpret_cast<QQmlGuard<QObject> *>(mem);
+
+ return o;
+}
+
+ListModel *ListElement::getListProperty(const ListLayout::Role &role)
+{
+ char *mem = getPropertyMemory(role);
+ ListModel **value = reinterpret_cast<ListModel **>(mem);
+ return *value;
+}
+
+QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV8Engine *eng)
+{
+ char *mem = getPropertyMemory(role);
+
+ QVariant data;
+
+ switch (role.type) {
+ case ListLayout::Role::Number:
+ {
+ double *value = reinterpret_cast<double *>(mem);
+ data = *value;
+ }
+ break;
+ case ListLayout::Role::String:
+ {
+ QString *value = reinterpret_cast<QString *>(mem);
+ if (value->data_ptr() != 0)
+ data = *value;
+ }
+ break;
+ case ListLayout::Role::Bool:
+ {
+ bool *value = reinterpret_cast<bool *>(mem);
+ data = *value;
+ }
+ break;
+ case ListLayout::Role::List:
+ {
+ ListModel **value = reinterpret_cast<ListModel **>(mem);
+ ListModel *model = *value;
+
+ if (model) {
+ if (model->m_modelCache == 0) {
+ model->m_modelCache = new QQmlListModel(owner, model, eng);
+ QQmlEngine::setContextForObject(model->m_modelCache, QQmlEngine::contextForObject(owner));
+ }
+
+ QObject *object = model->m_modelCache;
+ data = QVariant::fromValue(object);
+ }
+ }
+ break;
+ case ListLayout::Role::QObject:
+ {
+ QQmlGuard<QObject> *guard = reinterpret_cast<QQmlGuard<QObject> *>(mem);
+ QObject *object = guard->data();
+ if (object)
+ data = QVariant::fromValue(object);
+ }
+ break;
+ case ListLayout::Role::VariantMap:
+ {
+ if (isMemoryUsed<QVariantMap>(mem)) {
+ QVariantMap *map = reinterpret_cast<QVariantMap *>(mem);
+ data = *map;
+ }
+ }
+ break;
+ case ListLayout::Role::DateTime:
+ {
+ if (isMemoryUsed<QDateTime>(mem)) {
+ QDateTime *dt = reinterpret_cast<QDateTime *>(mem);
+ data = *dt;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return data;
+}
+
+int ListElement::setStringProperty(const ListLayout::Role &role, const QString &s)
+{
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::String) {
+ char *mem = getPropertyMemory(role);
+ QString *c = reinterpret_cast<QString *>(mem);
+ bool changed;
+ if (c->data_ptr() == 0) {
+ new (mem) QString(s);
+ changed = true;
+ } else {
+ changed = c->compare(s) != 0;
+ *c = s;
+ }
+ if (changed)
+ roleIndex = role.index;
+ }
+
+ return roleIndex;
+}
+
+int ListElement::setDoubleProperty(const ListLayout::Role &role, double d)
+{
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::Number) {
+ char *mem = getPropertyMemory(role);
+ double *value = new (mem) double;
+ bool changed = *value != d;
+ *value = d;
+ if (changed)
+ roleIndex = role.index;
+ }
+
+ return roleIndex;
+}
+
+int ListElement::setBoolProperty(const ListLayout::Role &role, bool b)
+{
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::Bool) {
+ char *mem = getPropertyMemory(role);
+ bool *value = new (mem) bool;
+ bool changed = *value != b;
+ *value = b;
+ if (changed)
+ roleIndex = role.index;
+ }
+
+ return roleIndex;
+}
+
+int ListElement::setListProperty(const ListLayout::Role &role, ListModel *m)
+{
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::List) {
+ char *mem = getPropertyMemory(role);
+ ListModel **value = new (mem) ListModel *;
+ if (*value) {
+ (*value)->destroy();
+ delete *value;
+ }
+ *value = m;
+ roleIndex = role.index;
+ }
+
+ return roleIndex;
+}
+
+int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o)
+{
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::QObject) {
+ char *mem = getPropertyMemory(role);
+ QQmlGuard<QObject> *g = reinterpret_cast<QQmlGuard<QObject> *>(mem);
+ bool existingGuard = false;
+ for (size_t i=0 ; i < sizeof(QQmlGuard<QObject>) ; ++i) {
+ if (mem[i] != 0) {
+ existingGuard = true;
+ break;
+ }
+ }
+ bool changed;
+ if (existingGuard) {
+ changed = g->data() != o;
+ g->~QQmlGuard();
+ } else {
+ changed = true;
+ }
+ new (mem) QQmlGuard<QObject>(o);
+ if (changed)
+ roleIndex = role.index;
+ }
+
+ return roleIndex;
+}
+
+int ListElement::setVariantMapProperty(const ListLayout::Role &role, v8::Handle<v8::Object> o, QV8Engine *eng)
+{
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::VariantMap) {
+ char *mem = getPropertyMemory(role);
+ if (isMemoryUsed<QVariantMap>(mem)) {
+ QVariantMap *map = reinterpret_cast<QVariantMap *>(mem);
+ map->~QMap();
+ }
+ new (mem) QVariantMap(eng->variantMapFromJS(o));
+ roleIndex = role.index;
+ }
+
+ return roleIndex;
+}
+
+int ListElement::setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m)
+{
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::VariantMap) {
+ char *mem = getPropertyMemory(role);
+ if (isMemoryUsed<QVariantMap>(mem)) {
+ QVariantMap *map = reinterpret_cast<QVariantMap *>(mem);
+ map->~QMap();
+ }
+ if (m)
+ new (mem) QVariantMap(*m);
+ else
+ new (mem) QVariantMap;
+ roleIndex = role.index;
+ }
+
+ return roleIndex;
+}
+
+int ListElement::setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt)
+{
+ int roleIndex = -1;
+
+ if (role.type == ListLayout::Role::DateTime) {
+ char *mem = getPropertyMemory(role);
+ if (isMemoryUsed<QDateTime>(mem)) {
+ QDateTime *dt = reinterpret_cast<QDateTime *>(mem);
+ dt->~QDateTime();
+ }
+ new (mem) QDateTime(dt);
+ roleIndex = role.index;
+ }
+
+ return roleIndex;
+}
+
+void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s)
+{
+ char *mem = getPropertyMemory(role);
+ new (mem) QString(s);
+}
+
+void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d)
+{
+ char *mem = getPropertyMemory(role);
+ double *value = new (mem) double;
+ *value = d;
+}
+
+void ListElement::setBoolPropertyFast(const ListLayout::Role &role, bool b)
+{
+ char *mem = getPropertyMemory(role);
+ bool *value = new (mem) bool;
+ *value = b;
+}
+
+void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QObject *o)
+{
+ char *mem = getPropertyMemory(role);
+ new (mem) QQmlGuard<QObject>(o);
+}
+
+void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m)
+{
+ char *mem = getPropertyMemory(role);
+ ListModel **value = new (mem) ListModel *;
+ *value = m;
+}
+
+void ListElement::setVariantMapFast(const ListLayout::Role &role, v8::Handle<v8::Object> o, QV8Engine *eng)
+{
+ char *mem = getPropertyMemory(role);
+ QVariantMap *map = new (mem) QVariantMap;
+ *map = eng->variantMapFromJS(o);
+}
+
+void ListElement::setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt)
+{
+ char *mem = getPropertyMemory(role);
+ new (mem) QDateTime(dt);
+}
+
+void ListElement::clearProperty(const ListLayout::Role &role)
+{
+ switch (role.type) {
+ case ListLayout::Role::String:
+ setStringProperty(role, QString());
+ break;
+ case ListLayout::Role::Number:
+ setDoubleProperty(role, 0.0);
+ break;
+ case ListLayout::Role::Bool:
+ setBoolProperty(role, false);
+ break;
+ case ListLayout::Role::List:
+ setListProperty(role, 0);
+ break;
+ case ListLayout::Role::QObject:
+ setQObjectProperty(role, 0);
+ break;
+ case ListLayout::Role::DateTime:
+ setDateTimeProperty(role, QDateTime());
+ break;
+ case ListLayout::Role::VariantMap:
+ setVariantMapProperty(role, 0);
+ break;
+ default:
+ break;
+ }
+}
+
+ListElement::ListElement()
+{
+ m_objectCache = 0;
+ uid = uidCounter.fetchAndAddOrdered(1);
+ next = 0;
+ memset(data, 0, sizeof(data));
+}
+
+ListElement::ListElement(int existingUid)
+{
+ m_objectCache = 0;
+ uid = existingUid;
+ next = 0;
+ memset(data, 0, sizeof(data));
+}
+
+ListElement::~ListElement()
+{
+ delete next;
+}
+
+void ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash<int, ListModel *> *targetModelHash)
+{
+ for (int i=0 ; i < srcLayout->roleCount() ; ++i) {
+ const ListLayout::Role &srcRole = srcLayout->getExistingRole(i);
+ const ListLayout::Role &targetRole = targetLayout->getExistingRole(i);
+
+ switch (srcRole.type) {
+ case ListLayout::Role::List:
+ {
+ ListModel *srcSubModel = src->getListProperty(srcRole);
+ ListModel *targetSubModel = target->getListProperty(targetRole);
+
+ if (srcSubModel) {
+ if (targetSubModel == 0) {
+ targetSubModel = new ListModel(targetRole.subLayout, 0, srcSubModel->getUid());
+ target->setListPropertyFast(targetRole, targetSubModel);
+ }
+ ListModel::sync(srcSubModel, targetSubModel, targetModelHash);
+ }
+ }
+ break;
+ case ListLayout::Role::QObject:
+ {
+ QObject *object = src->getQObjectProperty(srcRole);
+ target->setQObjectProperty(targetRole, object);
+ }
+ break;
+ case ListLayout::Role::String:
+ case ListLayout::Role::Number:
+ case ListLayout::Role::Bool:
+ case ListLayout::Role::DateTime:
+ {
+ QVariant v = src->getProperty(srcRole, 0, 0);
+ target->setVariantProperty(targetRole, v);
+ }
+ case ListLayout::Role::VariantMap:
+ {
+ QVariantMap *map = src->getVariantMapProperty(srcRole);
+ target->setVariantMapProperty(targetRole, map);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+}
+
+void ListElement::destroy(ListLayout *layout)
+{
+ if (layout) {
+ for (int i=0 ; i < layout->roleCount() ; ++i) {
+ const ListLayout::Role &r = layout->getExistingRole(i);
+
+ switch (r.type) {
+ case ListLayout::Role::String:
+ {
+ QString *string = getStringProperty(r);
+ if (string)
+ string->~QString();
+ }
+ break;
+ case ListLayout::Role::List:
+ {
+ ListModel *model = getListProperty(r);
+ if (model) {
+ model->destroy();
+ delete model;
+ }
+ }
+ break;
+ case ListLayout::Role::QObject:
+ {
+ QQmlGuard<QObject> *guard = getGuardProperty(r);
+ if (guard)
+ guard->~QQmlGuard();
+ }
+ break;
+ case ListLayout::Role::VariantMap:
+ {
+ QVariantMap *map = getVariantMapProperty(r);
+ if (map)
+ map->~QMap();
+ }
+ break;
+ case ListLayout::Role::DateTime:
+ {
+ QDateTime *dt = getDateTimeProperty(r);
+ if (dt)
+ dt->~QDateTime();
+ }
+ break;
+ default:
+ // other types don't need explicit cleanup.
+ break;
+ }
+ }
+
+ delete m_objectCache;
+ }
+
+ if (next)
+ next->destroy(0);
+ uid = -1;
+}
+
+int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant &d)
+{
+ int roleIndex = -1;
+
+ switch (role.type) {
+ case ListLayout::Role::Number:
+ roleIndex = setDoubleProperty(role, d.toDouble());
+ break;
+ case ListLayout::Role::String:
+ roleIndex = setStringProperty(role, d.toString());
+ break;
+ case ListLayout::Role::Bool:
+ roleIndex = setBoolProperty(role, d.toBool());
+ break;
+ case ListLayout::Role::List:
+ roleIndex = setListProperty(role, d.value<ListModel *>());
+ break;
+ case ListLayout::Role::VariantMap: {
+ QVariantMap map = d.toMap();
+ roleIndex = setVariantMapProperty(role, &map);
+ }
+ break;
+ case ListLayout::Role::DateTime:
+ roleIndex = setDateTimeProperty(role, d.toDateTime());
+ break;
+ default:
+ break;
+ }
+
+ return roleIndex;
+}
+
+int ListElement::setJsProperty(const ListLayout::Role &role, v8::Handle<v8::Value> d, QV8Engine *eng)
+{
+ // Check if this key exists yet
+ int roleIndex = -1;
+
+ // Add the value now
+ if (d->IsString()) {
+ v8::Handle<v8::String> jsString = d->ToString();
+ QString qstr;
+ qstr.resize(jsString->Length());
+ jsString->Write(reinterpret_cast<uint16_t*>(qstr.data()));
+ roleIndex = setStringProperty(role, qstr);
+ } else if (d->IsNumber()) {
+ roleIndex = setDoubleProperty(role, d->NumberValue());
+ } else if (d->IsArray()) {
+ if (role.type == ListLayout::Role::List) {
+ ListModel *subModel = new ListModel(role.subLayout, 0, -1);
+ v8::Handle<v8::Array> subArray = v8::Handle<v8::Array>::Cast(d);
+ int arrayLength = subArray->Length();
+ for (int j=0 ; j < arrayLength ; ++j) {
+ v8::Handle<v8::Object> subObject = subArray->Get(j)->ToObject();
+ subModel->append(subObject, eng);
+ }
+ roleIndex = setListProperty(role, subModel);
+ } else {
+ qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List));
+ }
+ } else if (d->IsBoolean()) {
+ roleIndex = setBoolProperty(role, d->BooleanValue());
+ } else if (d->IsDate()) {
+ QDateTime dt = QV8Engine::qtDateTimeFromJsDate(v8::Handle<v8::Date>::Cast(d)->NumberValue());
+ roleIndex = setDateTimeProperty(role, dt);
+ } else if (d->IsObject()) {
+ QV8ObjectResource *r = (QV8ObjectResource *) d->ToObject()->GetExternalResource();
+ if (role.type == ListLayout::Role::QObject && r && r->resourceType() == QV8ObjectResource::QObjectType) {
+ QObject *o = QV8QObjectWrapper::toQObject(r);
+ roleIndex = setQObjectProperty(role, o);
+ } else if (role.type == ListLayout::Role::VariantMap) {
+ roleIndex = setVariantMapProperty(role, d->ToObject(), eng);
+ }
+ } else if (d.IsEmpty() || d->IsUndefined() || d->IsNull()) {
+ clearProperty(role);
+ }
+
+ return roleIndex;
+}
+
+ModelObject::ModelObject(QQmlListModel *model, int elementIndex)
+: m_model(model), m_elementIndex(elementIndex), m_meta(new ModelNodeMetaObject(this))
+{
+ updateValues();
+ setNodeUpdatesEnabled(true);
+}
+
+void ModelObject::updateValues()
+{
+ int roleCount = m_model->m_listModel->roleCount();
+ for (int i=0 ; i < roleCount ; ++i) {
+ const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i);
+ QByteArray name = role.name.toUtf8();
+ const QVariant &data = m_model->data(m_elementIndex, i);
+ setValue(name, data, role.type == ListLayout::Role::List);
+ }
+}
+
+void ModelObject::updateValues(const QVector<int> &roles)
+{
+ int roleCount = roles.count();
+ for (int i=0 ; i < roleCount ; ++i) {
+ int roleIndex = roles.at(i);
+ const ListLayout::Role &role = m_model->m_listModel->getExistingRole(roleIndex);
+ QByteArray name = role.name.toUtf8();
+ const QVariant &data = m_model->data(m_elementIndex, roleIndex);
+ setValue(name, data, role.type == ListLayout::Role::List);
+ }
+}
+
+ModelNodeMetaObject::ModelNodeMetaObject(ModelObject *object)
+: QQmlOpenMetaObject(object), m_enabled(false), m_obj(object)
+{
+}
+
+ModelNodeMetaObject::~ModelNodeMetaObject()
+{
+}
+
+void ModelNodeMetaObject::propertyWritten(int index)
+{
+ if (!m_enabled)
+ return;
+
+ QV8Engine *eng = m_obj->m_model->engine();
+
+ QString propName = QString::fromUtf8(name(index));
+ QVariant value = operator[](index);
+
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(eng->context());
+
+ v8::Handle<v8::Value> v = eng->fromVariant(value);
+
+ int roleIndex = m_obj->m_model->m_listModel->setExistingProperty(m_obj->m_elementIndex, propName, v, eng);
+ if (roleIndex != -1) {
+ QVector<int> roles;
+ roles << roleIndex;
+ m_obj->m_model->emitItemsChanged(m_obj->m_elementIndex, 1, roles);
+ }
+}
+
+DynamicRoleModelNode::DynamicRoleModelNode(QQmlListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this))
+{
+ setNodeUpdatesEnabled(true);
+}
+
+DynamicRoleModelNode *DynamicRoleModelNode::create(const QVariantMap &obj, QQmlListModel *owner)
+{
+ DynamicRoleModelNode *object = new DynamicRoleModelNode(owner, uidCounter.fetchAndAddOrdered(1));
+ QVector<int> roles;
+ object->updateValues(obj, roles);
+ return object;
+}
+
+void DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash<int, QQmlListModel *> *targetModelHash)
+{
+ for (int i=0 ; i < src->m_meta->count() ; ++i) {
+ const QByteArray &name = src->m_meta->name(i);
+ QVariant value = src->m_meta->value(i);
+
+ QQmlListModel *srcModel = qobject_cast<QQmlListModel *>(value.value<QObject *>());
+ QQmlListModel *targetModel = qobject_cast<QQmlListModel *>(target->m_meta->value(i).value<QObject *>());
+
+ if (srcModel) {
+ if (targetModel == 0)
+ targetModel = QQmlListModel::createWithOwner(target->m_owner);
+
+ QQmlListModel::sync(srcModel, targetModel, targetModelHash);
+
+ QObject *targetModelObject = targetModel;
+ value = QVariant::fromValue(targetModelObject);
+ } else if (targetModel) {
+ delete targetModel;
+ }
+
+ target->setValue(name, value);
+ }
+}
+
+void DynamicRoleModelNode::updateValues(const QVariantMap &object, QVector<int> &roles)
+{
+ const QList<QString> &keys = object.keys();
+
+ QList<QString>::const_iterator it = keys.begin();
+ QList<QString>::const_iterator end = keys.end();
+
+ while (it != end) {
+ const QString &key = *it;
+
+ int roleIndex = m_owner->m_roles.indexOf(key);
+ if (roleIndex == -1) {
+ roleIndex = m_owner->m_roles.count();
+ m_owner->m_roles.append(key);
+ }
+
+ QVariant value = object[key];
+
+ if (value.type() == QVariant::List) {
+ QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner);
+
+ QVariantList subArray = value.toList();
+ QVariantList::const_iterator subIt = subArray.begin();
+ QVariantList::const_iterator subEnd = subArray.end();
+ while (subIt != subEnd) {
+ const QVariantMap &subObject = subIt->toMap();
+ subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
+ ++subIt;
+ }
+
+ QObject *subModelObject = subModel;
+ value = QVariant::fromValue(subModelObject);
+ }
+
+ const QByteArray &keyUtf8 = key.toUtf8();
+
+ QQmlListModel *existingModel = qobject_cast<QQmlListModel *>(m_meta->value(keyUtf8).value<QObject *>());
+ if (existingModel)
+ delete existingModel;
+
+ if (m_meta->setValue(keyUtf8, value))
+ roles << roleIndex;
+
+ ++it;
+ }
+}
+
+DynamicRoleModelNodeMetaObject::DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object)
+ : QQmlOpenMetaObject(object), m_enabled(false), m_owner(object)
+{
+}
+
+DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject()
+{
+ for (int i=0 ; i < count() ; ++i) {
+ QQmlListModel *subModel = qobject_cast<QQmlListModel *>(value(i).value<QObject *>());
+ if (subModel)
+ delete subModel;
+ }
+}
+
+void DynamicRoleModelNodeMetaObject::propertyWrite(int index)
+{
+ if (!m_enabled)
+ return;
+
+ QVariant v = value(index);
+ QQmlListModel *model = qobject_cast<QQmlListModel *>(v.value<QObject *>());
+ if (model)
+ delete model;
+}
+
+void DynamicRoleModelNodeMetaObject::propertyWritten(int index)
+{
+ if (!m_enabled)
+ return;
+
+ QQmlListModel *parentModel = m_owner->m_owner;
+
+ QVariant v = value(index);
+ if (v.type() == QVariant::List) {
+ QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel);
+
+ QVariantList subArray = v.toList();
+ QVariantList::const_iterator subIt = subArray.begin();
+ QVariantList::const_iterator subEnd = subArray.end();
+ while (subIt != subEnd) {
+ const QVariantMap &subObject = subIt->toMap();
+ subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
+ ++subIt;
+ }
+
+ QObject *subModelObject = subModel;
+ v = QVariant::fromValue(subModelObject);
+
+ setValue(index, v);
+ }
+
+ int elementIndex = parentModel->m_modelObjects.indexOf(m_owner);
+ int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData()));
+
+ if (elementIndex != -1 && roleIndex != -1) {
+
+ QVector<int> roles;
+ roles << roleIndex;
+
+ parentModel->emitItemsChanged(elementIndex, 1, roles);
+ }
+}
+
+QQmlListModelParser::ListInstruction *QQmlListModelParser::ListModelData::instructions() const
+{
+ return (QQmlListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData));
+}
+
+/*!
+ \qmltype ListModel
+ \instantiates QQmlListModel
+ \inqmlmodule QtQml.Models 2
+ \brief Defines a free-form list data source
+
+ The ListModel is a simple container of ListElement definitions, each containing data roles.
+ The contents can be defined dynamically, or explicitly in QML.
+
+ This type is also available in the QtQuick 2 import. For full documentation, see \l QtQuick2::ListModel
+*/
+/*!
+ \qmltype ListModel
+ \instantiates QQmlListModel
+ \inqmlmodule QtQuick 2
+ \brief Defines a free-form list data source
+ \ingroup qtquick-models
+
+ The ListModel is a simple container of ListElement definitions, each containing data roles.
+ The contents can be defined dynamically, or explicitly in QML.
+
+ The number of elements in the model can be obtained from its \l count property.
+ A number of familiar methods are also provided to manipulate the contents of the
+ model, including append(), insert(), move(), remove() and set(). These methods
+ accept dictionaries as their arguments; these are translated to ListElement objects
+ by the model.
+
+ Elements can be manipulated via the model using the setProperty() method, which
+ allows the roles of the specified element to be set and changed.
+
+ \section1 Example Usage
+
+ The following example shows a ListModel containing three elements, with the roles
+ "name" and "cost".
+
+ \div {class="float-right"}
+ \inlineimage listmodel.png
+ \enddiv
+
+ \snippet qml/listmodel/listmodel.qml 0
+
+ Roles (properties) in each element must begin with a lower-case letter and
+ should be common to all elements in a model. The ListElement documentation
+ provides more guidelines for how elements should be defined.
+
+ Since the example model contains an \c id property, it can be referenced
+ by views, such as the ListView in this example:
+
+ \snippet qml/listmodel/listmodel-simple.qml 0
+ \dots 8
+ \snippet qml/listmodel/listmodel-simple.qml 1
+
+ It is possible for roles to contain list data. In the following example we
+ create a list of fruit attributes:
+
+ \snippet qml/listmodel/listmodel-nested.qml model
+
+ The delegate displays all the fruit attributes:
+
+ \div {class="float-right"}
+ \inlineimage listmodel-nested.png
+ \enddiv
+
+ \snippet qml/listmodel/listmodel-nested.qml delegate
+
+ \section1 Modifying List Models
+
+ The content of a ListModel may be created and modified using the clear(),
+ append(), set(), insert() and setProperty() methods. For example:
+
+ \snippet qml/listmodel/listmodel-modify.qml delegate
+
+ Note that when creating content dynamically the set of available properties
+ cannot be changed once set. Whatever properties are first added to the model
+ are the only permitted properties in the model.
+
+ \section1 Using Threaded List Models with WorkerScript
+
+ ListModel can be used together with WorkerScript access a list model
+ from multiple threads. This is useful if list modifications are
+ synchronous and take some time: the list operations can be moved to a
+ different thread to avoid blocking of the main GUI thread.
+
+ Here is an example that uses WorkerScript to periodically append the
+ current time to a list model:
+
+ \snippet quick/threading/threadedlistmodel/timedisplay.qml 0
+
+ The included file, \tt dataloader.js, looks like this:
+
+ \snippet quick/threading/threadedlistmodel/dataloader.js 0
+
+ The timer in the main example sends messages to the worker script by calling
+ \l WorkerScript::sendMessage(). When this message is received,
+ \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js,
+ which appends the current time to the list model.
+
+ Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()}
+ handler. You must call sync() or else the changes made to the list from the external
+ thread will not be reflected in the list model in the main thread.
+
+ \sa {qml-data-models}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtQml
+*/
+
+QQmlListModel::QQmlListModel(QObject *parent)
+: QAbstractListModel(parent)
+{
+ m_mainThread = true;
+ m_primary = true;
+ m_agent = 0;
+ m_uid = uidCounter.fetchAndAddOrdered(1);
+ m_dynamicRoles = false;
+
+ m_layout = new ListLayout;
+ m_listModel = new ListModel(m_layout, this, -1);
+
+ m_engine = 0;
+}
+
+QQmlListModel::QQmlListModel(const QQmlListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent)
+: QAbstractListModel(parent)
+{
+ m_mainThread = owner->m_mainThread;
+ m_primary = false;
+ m_agent = owner->m_agent;
+
+ Q_ASSERT(owner->m_dynamicRoles == false);
+ m_dynamicRoles = false;
+ m_layout = 0;
+ m_listModel = data;
+
+ m_engine = eng;
+}
+
+QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent)
+: QAbstractListModel(agent)
+{
+ m_mainThread = false;
+ m_primary = true;
+ m_agent = agent;
+ m_dynamicRoles = orig->m_dynamicRoles;
+
+ m_layout = new ListLayout(orig->m_layout);
+ m_listModel = new ListModel(m_layout, this, orig->m_listModel->getUid());
+
+ if (m_dynamicRoles)
+ sync(orig, this, 0);
+ else
+ ListModel::sync(orig->m_listModel, m_listModel, 0);
+
+ m_engine = 0;
+}
+
+QQmlListModel::~QQmlListModel()
+{
+ for (int i=0 ; i < m_modelObjects.count() ; ++i)
+ delete m_modelObjects[i];
+
+ if (m_primary) {
+ m_listModel->destroy();
+ delete m_listModel;
+
+ if (m_mainThread && m_agent) {
+ m_agent->modelDestroyed();
+ m_agent->release();
+ }
+ }
+
+ m_listModel = 0;
+
+ delete m_layout;
+ m_layout = 0;
+}
+
+QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner)
+{
+ QQmlListModel *model = new QQmlListModel;
+
+ model->m_mainThread = newOwner->m_mainThread;
+ model->m_engine = newOwner->m_engine;
+ model->m_agent = newOwner->m_agent;
+ model->m_dynamicRoles = newOwner->m_dynamicRoles;
+
+ if (model->m_mainThread && model->m_agent)
+ model->m_agent->addref();
+
+ QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner));
+
+ return model;
+}
+
+QV8Engine *QQmlListModel::engine() const
+{
+ if (m_engine == 0) {
+ m_engine = QQmlEnginePrivate::getV8Engine(qmlEngine(this));
+ }
+
+ return m_engine;
+}
+
+void QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target, QHash<int, QQmlListModel *> *targetModelHash)
+{
+ Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles);
+
+ target->m_uid = src->m_uid;
+ if (targetModelHash)
+ targetModelHash->insert(target->m_uid, target);
+ target->m_roles = src->m_roles;
+
+ // Build hash of elements <-> uid for each of the lists
+ QHash<int, ElementSync> elementHash;
+ for (int i=0 ; i < target->m_modelObjects.count() ; ++i) {
+ DynamicRoleModelNode *e = target->m_modelObjects.at(i);
+ int uid = e->getUid();
+ ElementSync sync;
+ sync.target = e;
+ elementHash.insert(uid, sync);
+ }
+ for (int i=0 ; i < src->m_modelObjects.count() ; ++i) {
+ DynamicRoleModelNode *e = src->m_modelObjects.at(i);
+ int uid = e->getUid();
+
+ QHash<int, ElementSync>::iterator it = elementHash.find(uid);
+ if (it == elementHash.end()) {
+ ElementSync sync;
+ sync.src = e;
+ elementHash.insert(uid, sync);
+ } else {
+ ElementSync &sync = it.value();
+ sync.src = e;
+ }
+ }
+
+ // Get list of elements that are in the target but no longer in the source. These get deleted first.
+ QHash<int, ElementSync>::iterator it = elementHash.begin();
+ QHash<int, ElementSync>::iterator end = elementHash.end();
+ while (it != end) {
+ const ElementSync &s = it.value();
+ if (s.src == 0) {
+ int targetIndex = target->m_modelObjects.indexOf(s.target);
+ target->m_modelObjects.remove(targetIndex, 1);
+ delete s.target;
+ }
+ ++it;
+ }
+
+ // Clear the target list, and append in correct order from the source
+ target->m_modelObjects.clear();
+ for (int i=0 ; i < src->m_modelObjects.count() ; ++i) {
+ DynamicRoleModelNode *srcElement = src->m_modelObjects.at(i);
+ it = elementHash.find(srcElement->getUid());
+ const ElementSync &s = it.value();
+ DynamicRoleModelNode *targetElement = s.target;
+ if (targetElement == 0) {
+ targetElement = new DynamicRoleModelNode(target, srcElement->getUid());
+ }
+ DynamicRoleModelNode::sync(srcElement, targetElement, targetModelHash);
+ target->m_modelObjects.append(targetElement);
+ }
+}
+
+void QQmlListModel::emitItemsChanged(int index, int count, const QVector<int> &roles)
+{
+ if (count <= 0)
+ return;
+
+ if (m_mainThread) {
+ emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);;
+ } else {
+ int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
+ m_agent->data.changedChange(uid, index, count, roles);
+ }
+}
+
+void QQmlListModel::emitItemsRemoved(int index, int count)
+{
+ if (count <= 0)
+ return;
+
+ if (m_mainThread) {
+ beginRemoveRows(QModelIndex(), index, index + count - 1);
+ endRemoveRows();
+ emit countChanged();
+ } else {
+ int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
+ if (index == 0 && count == this->count())
+ m_agent->data.clearChange(uid);
+ m_agent->data.removeChange(uid, index, count);
+ }
+}
+
+void QQmlListModel::emitItemsInserted(int index, int count)
+{
+ if (count <= 0)
+ return;
+
+ if (m_mainThread) {
+ beginInsertRows(QModelIndex(), index, index + count - 1);
+ endInsertRows();
+ emit countChanged();
+ } else {
+ int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
+ m_agent->data.insertChange(uid, index, count);
+ }
+}
+
+void QQmlListModel::emitItemsMoved(int from, int to, int n)
+{
+ if (n <= 0)
+ return;
+
+ if (m_mainThread) {
+ beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to);
+ endMoveRows();
+ } else {
+ int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
+ m_agent->data.moveChange(uid, from, n, to);
+ }
+}
+
+QQmlListModelWorkerAgent *QQmlListModel::agent()
+{
+ if (m_agent)
+ return m_agent;
+
+ m_agent = new QQmlListModelWorkerAgent(this);
+ return m_agent;
+}
+
+QModelIndex QQmlListModel::index(int row, int column, const QModelIndex &parent) const
+{
+ return row >= 0 && row < count() && column == 0 && !parent.isValid()
+ ? createIndex(row, column)
+ : QModelIndex();
+}
+
+int QQmlListModel::rowCount(const QModelIndex &parent) const
+{
+ return !parent.isValid() ? count() : 0;
+}
+
+QVariant QQmlListModel::data(const QModelIndex &index, int role) const
+{
+ return data(index.row(), role);
+}
+
+QVariant QQmlListModel::data(int index, int role) const
+{
+ QVariant v;
+
+ if (index >= count() || index < 0)
+ return v;
+
+ if (m_dynamicRoles)
+ v = m_modelObjects[index]->getValue(m_roles[role]);
+ else
+ v = m_listModel->getProperty(index, role, this, engine());
+
+ return v;
+}
+
+QHash<int, QByteArray> QQmlListModel::roleNames() const
+{
+ QHash<int, QByteArray> roleNames;
+
+ if (m_dynamicRoles) {
+ for (int i = 0 ; i < m_roles.count() ; ++i)
+ roleNames.insert(i, m_roles.at(i).toUtf8());
+ } else {
+ for (int i = 0 ; i < m_listModel->roleCount() ; ++i) {
+ const ListLayout::Role &r = m_listModel->getExistingRole(i);
+ roleNames.insert(i, r.name.toUtf8());
+ }
+ }
+
+ return roleNames;
+}
+
+/*!
+ \qmlproperty bool QtQml2::ListModel::dynamicRoles
+
+ By default, the type of a role is fixed the first time
+ the role is used. For example, if you create a role called
+ "data" and assign a number to it, you can no longer assign
+ a string to the "data" role. However, when the dynamicRoles
+ property is enabled, the type of a given role is not fixed
+ and can be different between elements.
+
+ The dynamicRoles property must be set before any data is
+ added to the ListModel, and must be set from the main
+ thread.
+
+ A ListModel that has data statically defined (via the
+ ListElement QML syntax) cannot have the dynamicRoles
+ property enabled.
+
+ There is a significant performance cost to using a
+ ListModel with dynamic roles enabled. The cost varies
+ from platform to platform but is typically somewhere
+ between 4-6x slower than using static role types.
+
+ Due to the performance cost of using dynamic roles,
+ they are disabled by default.
+*/
+void QQmlListModel::setDynamicRoles(bool enableDynamicRoles)
+{
+ if (m_mainThread && m_agent == 0) {
+ if (enableDynamicRoles) {
+ if (m_layout->roleCount())
+ qmlInfo(this) << tr("unable to enable dynamic roles as this model is not empty!");
+ else
+ m_dynamicRoles = true;
+ } else {
+ if (m_roles.count()) {
+ qmlInfo(this) << tr("unable to enable static roles as this model is not empty!");
+ } else {
+ m_dynamicRoles = false;
+ }
+ }
+ } else {
+ qmlInfo(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created");
+ }
+}
+
+/*!
+ \qmlproperty int QtQml2::ListModel::count
+ The number of data entries in the model.
+*/
+int QQmlListModel::count() const
+{
+ int count;
+
+ if (m_dynamicRoles)
+ count = m_modelObjects.count();
+ else {
+ count = m_listModel->elementCount();
+ }
+
+ return count;
+}
+
+/*!
+ \qmlmethod QtQml2::ListModel::clear()
+
+ Deletes all content from the model.
+
+ \sa append(), remove()
+*/
+void QQmlListModel::clear()
+{
+ int cleared = count();
+
+ if (m_dynamicRoles) {
+ for (int i=0 ; i < m_modelObjects.count() ; ++i)
+ delete m_modelObjects[i];
+ m_modelObjects.clear();
+ } else {
+ m_listModel->clear();
+ }
+
+ emitItemsRemoved(0, cleared);
+}
+
+/*!
+ \qmlmethod QtQml2::ListModel::remove(int index, int count = 1)
+
+ Deletes the content at \a index from the model.
+
+ \sa clear()
+*/
+void QQmlListModel::remove(QQmlV8Function *args)
+{
+ int argLength = args->Length();
+
+ if (argLength == 1 || argLength == 2) {
+ int index = (*args)[0]->Int32Value();
+ int removeCount = (argLength == 2 ? ((*args)[1]->Int32Value()) : 1);
+
+ if (index < 0 || index+removeCount > count() || removeCount <= 0) {
+ qmlInfo(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count());
+ return;
+ }
+
+ if (m_dynamicRoles) {
+ for (int i=0 ; i < removeCount ; ++i)
+ delete m_modelObjects[index+i];
+ m_modelObjects.remove(index, removeCount);
+ } else {
+ m_listModel->remove(index, removeCount);
+ }
+
+ emitItemsRemoved(index, removeCount);
+ } else {
+ qmlInfo(this) << tr("remove: incorrect number of arguments");
+ }
+}
+
+/*!
+ \qmlmethod QtQml2::ListModel::insert(int index, jsobject dict)
+
+ Adds a new item to the list model at position \a index, with the
+ values in \a dict.
+
+ \code
+ fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"})
+ \endcode
+
+ The \a index must be to an existing item in the list, or one past
+ the end of the list (equivalent to append).
+
+ \sa set(), append()
+*/
+
+void QQmlListModel::insert(QQmlV8Function *args)
+{
+ if (args->Length() == 2) {
+
+ v8::Handle<v8::Value> arg0 = (*args)[0];
+ int index = arg0->Int32Value();
+
+ if (index < 0 || index > count()) {
+ qmlInfo(this) << tr("insert: index %1 out of range").arg(index);
+ return;
+ }
+
+ v8::Handle<v8::Value> arg1 = (*args)[1];
+
+ if (arg1->IsArray()) {
+ v8::Handle<v8::Array> objectArray = v8::Handle<v8::Array>::Cast(arg1);
+ int objectArrayLength = objectArray->Length();
+ for (int i=0 ; i < objectArrayLength ; ++i) {
+ v8::Handle<v8::Object> argObject = objectArray->Get(i)->ToObject();
+
+ if (m_dynamicRoles) {
+ m_modelObjects.insert(index+i, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
+ } else {
+ m_listModel->insert(index+i, argObject, args->engine());
+ }
+ }
+ emitItemsInserted(index, objectArrayLength);
+ } else if (arg1->IsObject()) {
+ v8::Handle<v8::Object> argObject = arg1->ToObject();
+
+ if (m_dynamicRoles) {
+ m_modelObjects.insert(index, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
+ } else {
+ m_listModel->insert(index, argObject, args->engine());
+ }
+
+ emitItemsInserted(index, 1);
+ } else {
+ qmlInfo(this) << tr("insert: value is not an object");
+ }
+ } else {
+ qmlInfo(this) << tr("insert: value is not an object");
+ }
+}
+
+/*!
+ \qmlmethod QtQml2::ListModel::move(int from, int to, int n)
+
+ Moves \a n items \a from one position \a to another.
+
+ The from and to ranges must exist; for example, to move the first 3 items
+ to the end of the list:
+
+ \code
+ fruitModel.move(0, fruitModel.count - 3, 3)
+ \endcode
+
+ \sa append()
+*/
+void QQmlListModel::move(int from, int to, int n)
+{
+ if (n==0 || from==to)
+ return;
+ if (!canMove(from, to, n)) {
+ qmlInfo(this) << tr("move: out of range");
+ return;
+ }
+
+ if (m_dynamicRoles) {
+
+ int realFrom = from;
+ int realTo = to;
+ int realN = n;
+
+ if (from > to) {
+ // Only move forwards - flip if backwards moving
+ int tfrom = from;
+ int tto = to;
+ realFrom = tto;
+ realTo = tto+n;
+ realN = tfrom-tto;
+ }
+
+ QPODVector<DynamicRoleModelNode *, 4> store;
+ for (int i=0 ; i < (realTo-realFrom) ; ++i)
+ store.append(m_modelObjects[realFrom+realN+i]);
+ for (int i=0 ; i < realN ; ++i)
+ store.append(m_modelObjects[realFrom+i]);
+ for (int i=0 ; i < store.count() ; ++i)
+ m_modelObjects[realFrom+i] = store[i];
+
+ } else {
+ m_listModel->move(from, to, n);
+ }
+
+ emitItemsMoved(from, to, n);
+}
+
+/*!
+ \qmlmethod QtQml2::ListModel::append(jsobject dict)
+
+ Adds a new item to the end of the list model, with the
+ values in \a dict.
+
+ \code
+ fruitModel.append({"cost": 5.95, "name":"Pizza"})
+ \endcode
+
+ \sa set(), remove()
+*/
+void QQmlListModel::append(QQmlV8Function *args)
+{
+ if (args->Length() == 1) {
+ v8::Handle<v8::Value> arg = (*args)[0];
+
+ if (arg->IsArray()) {
+ v8::Handle<v8::Array> objectArray = v8::Handle<v8::Array>::Cast(arg);
+ int objectArrayLength = objectArray->Length();
+
+ int index = count();
+ for (int i=0 ; i < objectArrayLength ; ++i) {
+ v8::Handle<v8::Object> argObject = objectArray->Get(i)->ToObject();
+
+ if (m_dynamicRoles) {
+ m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
+ } else {
+ m_listModel->append(argObject, args->engine());
+ }
+ }
+
+ emitItemsInserted(index, objectArrayLength);
+ } else if (arg->IsObject()) {
+ v8::Handle<v8::Object> argObject = arg->ToObject();
+
+ int index;
+
+ if (m_dynamicRoles) {
+ index = m_modelObjects.count();
+ m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
+ } else {
+ index = m_listModel->append(argObject, args->engine());
+ }
+
+ emitItemsInserted(index, 1);
+ } else {
+ qmlInfo(this) << tr("append: value is not an object");
+ }
+ } else {
+ qmlInfo(this) << tr("append: value is not an object");
+ }
+}
+
+/*!
+ \qmlmethod object QtQml2::ListModel::get(int index)
+
+ Returns the item at \a index in the list model. This allows the item
+ data to be accessed or modified from JavaScript:
+
+ \code
+ Component.onCompleted: {
+ fruitModel.append({"cost": 5.95, "name":"Jackfruit"});
+ console.log(fruitModel.get(0).cost);
+ fruitModel.get(0).cost = 10.95;
+ }
+ \endcode
+
+ The \a index must be an element in the list.
+
+ Note that properties of the returned object that are themselves objects
+ will also be models, and this get() method is used to access elements:
+
+ \code
+ fruitModel.append(..., "attributes":
+ [{"name":"spikes","value":"7mm"},
+ {"name":"color","value":"green"}]);
+ fruitModel.get(0).attributes.get(1).value; // == "green"
+ \endcode
+
+ \warning The returned object is not guaranteed to remain valid. It
+ should not be used in \l{Property Binding}{property bindings}.
+
+ \sa append()
+*/
+QQmlV8Handle QQmlListModel::get(int index) const
+{
+ v8::Handle<v8::Value> result = v8::Undefined();
+
+ if (index >= 0 && index < count()) {
+ QV8Engine *v8engine = engine();
+
+ if (m_dynamicRoles) {
+ DynamicRoleModelNode *object = m_modelObjects[index];
+ result = v8engine->newQObject(object);
+ } else {
+ ModelObject *object = m_listModel->getOrCreateModelObject(const_cast<QQmlListModel *>(this), index);
+ result = v8engine->newQObject(object);
+ }
+ }
+
+ return QQmlV8Handle::fromHandle(result);
+}
+
+/*!
+ \qmlmethod QtQml2::ListModel::set(int index, jsobject dict)
+
+ Changes the item at \a index in the list model with the
+ values in \a dict. Properties not appearing in \a dict
+ are left unchanged.
+
+ \code
+ fruitModel.set(3, {"cost": 5.95, "name":"Pizza"})
+ \endcode
+
+ If \a index is equal to count() then a new item is appended to the
+ list. Otherwise, \a index must be an element in the list.
+
+ \sa append()
+*/
+void QQmlListModel::set(int index, const QQmlV8Handle &handle)
+{
+ v8::Handle<v8::Value> valuemap = handle.toHandle();
+
+ if (!valuemap->IsObject() || valuemap->IsArray()) {
+ qmlInfo(this) << tr("set: value is not an object");
+ return;
+ }
+ if (index > count() || index < 0) {
+ qmlInfo(this) << tr("set: index %1 out of range").arg(index);
+ return;
+ }
+
+ v8::Handle<v8::Object> object = valuemap->ToObject();
+
+ if (index == count()) {
+
+ if (m_dynamicRoles) {
+ m_modelObjects.append(DynamicRoleModelNode::create(engine()->variantMapFromJS(object), this));
+ } else {
+ m_listModel->insert(index, object, engine());
+ }
+
+ emitItemsInserted(index, 1);
+ } else {
+
+ QVector<int> roles;
+
+ if (m_dynamicRoles) {
+ m_modelObjects[index]->updateValues(engine()->variantMapFromJS(object), roles);
+ } else {
+ m_listModel->set(index, object, &roles, engine());
+ }
+
+ if (roles.count())
+ emitItemsChanged(index, 1, roles);
+ }
+}
+
+/*!
+ \qmlmethod QtQml2::ListModel::setProperty(int index, string property, variant value)
+
+ Changes the \a property of the item at \a index in the list model to \a value.
+
+ \code
+ fruitModel.setProperty(3, "cost", 5.95)
+ \endcode
+
+ The \a index must be an element in the list.
+
+ \sa append()
+*/
+void QQmlListModel::setProperty(int index, const QString& property, const QVariant& value)
+{
+ if (count() == 0 || index >= count() || index < 0) {
+ qmlInfo(this) << tr("set: index %1 out of range").arg(index);
+ return;
+ }
+
+ if (m_dynamicRoles) {
+ int roleIndex = m_roles.indexOf(property);
+ if (roleIndex == -1) {
+ roleIndex = m_roles.count();
+ m_roles.append(property);
+ }
+ if (m_modelObjects[index]->setValue(property.toUtf8(), value)) {
+ QVector<int> roles;
+ roles << roleIndex;
+ emitItemsChanged(index, 1, roles);
+ }
+ } else {
+ int roleIndex = m_listModel->setOrCreateProperty(index, property, value);
+ if (roleIndex != -1) {
+
+ QVector<int> roles;
+ roles << roleIndex;
+
+ emitItemsChanged(index, 1, roles);
+ }
+ }
+}
+
+/*!
+ \qmlmethod QtQml2::ListModel::sync()
+
+ Writes any unsaved changes to the list model after it has been modified
+ from a worker script.
+*/
+void QQmlListModel::sync()
+{
+ // This is just a dummy method to make it look like sync() exists in
+ // ListModel (and not just QQmlListModelWorkerAgent) and to let
+ // us document sync().
+ qmlInfo(this) << "List sync() can only be called from a WorkerScript";
+}
+
+bool QQmlListModelParser::compileProperty(const QQmlCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data)
+{
+ QList<QVariant> values = prop.assignedValues();
+ for(int ii = 0; ii < values.count(); ++ii) {
+ const QVariant &value = values.at(ii);
+
+ if(value.userType() == qMetaTypeId<QQmlCustomParserNode>()) {
+ QQmlCustomParserNode node =
+ qvariant_cast<QQmlCustomParserNode>(value);
+
+ if (node.name() != listElementTypeName) {
+ const QMetaObject *mo = resolveType(node.name());
+ if (mo != &QQmlListElement::staticMetaObject) {
+ error(node, QQmlListModel::tr("ListElement: cannot contain nested elements"));
+ return false;
+ }
+ listElementTypeName = node.name(); // cache right name for next time
+ }
+
+ {
+ ListInstruction li;
+ li.type = ListInstruction::Push;
+ li.dataIdx = -1;
+ instr << li;
+ }
+
+ QList<QQmlCustomParserProperty> props = node.properties();
+ for(int jj = 0; jj < props.count(); ++jj) {
+ const QQmlCustomParserProperty &nodeProp = props.at(jj);
+ if (nodeProp.name().isEmpty()) {
+ error(nodeProp, QQmlListModel::tr("ListElement: cannot contain nested elements"));
+ return false;
+ }
+ if (nodeProp.name() == QStringLiteral("id")) {
+ error(nodeProp, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property"));
+ return false;
+ }
+
+ ListInstruction li;
+ int ref = data.count();
+ data.append(nodeProp.name().toUtf8());
+ data.append('\0');
+ li.type = ListInstruction::Set;
+ li.dataIdx = ref;
+ instr << li;
+
+ if(!compileProperty(nodeProp, instr, data))
+ return false;
+
+ li.type = ListInstruction::Pop;
+ li.dataIdx = -1;
+ instr << li;
+ }
+
+ {
+ ListInstruction li;
+ li.type = ListInstruction::Pop;
+ li.dataIdx = -1;
+ instr << li;
+ }
+
+ } else {
+
+ QQmlScript::Variant variant =
+ qvariant_cast<QQmlScript::Variant>(value);
+
+ int ref = data.count();
+
+ QByteArray d;
+ d += char(variant.type()); // type tag
+ if (variant.isString()) {
+ d += variant.asString().toUtf8();
+ } else if (variant.isNumber()) {
+ d += QByteArray::number(variant.asNumber(),'g',20);
+ } else if (variant.isBoolean()) {
+ d += char(variant.asBoolean());
+ } else if (variant.isScript()) {
+ if (definesEmptyList(variant.asScript())) {
+ d[0] = char(QQmlScript::Variant::Invalid); // marks empty list
+ } else {
+ QByteArray script = variant.asScript().toUtf8();
+ bool ok;
+ int v = evaluateEnum(script, &ok);
+ if (!ok) {
+ using namespace QQmlJS;
+ AST::Node *node = variant.asAST();
+ AST::StringLiteral *literal = 0;
+ if (AST::CallExpression *callExpr = AST::cast<AST::CallExpression *>(node)) {
+ if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(callExpr->base)) {
+ if (idExpr->name == QLatin1String("QT_TR_NOOP") || idExpr->name == QLatin1String("QT_TRID_NOOP")) {
+ if (callExpr->arguments && !callExpr->arguments->next)
+ literal = AST::cast<AST::StringLiteral *>(callExpr->arguments->expression);
+ if (!literal) {
+ error(prop, QQmlListModel::tr("ListElement: improperly specified %1").arg(idExpr->name.toString()));
+ return false;
+ }
+ } else if (idExpr->name == QLatin1String("QT_TRANSLATE_NOOP")) {
+ if (callExpr->arguments && callExpr->arguments->next && !callExpr->arguments->next->next)
+ literal = AST::cast<AST::StringLiteral *>(callExpr->arguments->next->expression);
+ if (!literal) {
+ error(prop, QQmlListModel::tr("ListElement: improperly specified QT_TRANSLATE_NOOP"));
+ return false;
+ }
+ }
+ }
+ }
+
+ if (literal) {
+ d[0] = char(QQmlScript::Variant::String);
+ d += literal->value.toUtf8();
+ } else {
+ error(prop, QQmlListModel::tr("ListElement: cannot use script for property value"));
+ return false;
+ }
+ } else {
+ d[0] = char(QQmlScript::Variant::Number);
+ d += QByteArray::number(v);
+ }
+ }
+ }
+ d.append('\0');
+ data.append(d);
+
+ ListInstruction li;
+ li.type = ListInstruction::Value;
+ li.dataIdx = ref;
+ instr << li;
+ }
+ }
+
+ return true;
+}
+
+QByteArray QQmlListModelParser::compile(const QList<QQmlCustomParserProperty> &customProps)
+{
+ QList<ListInstruction> instr;
+ QByteArray data;
+ listElementTypeName = QString(); // unknown
+
+ for(int ii = 0; ii < customProps.count(); ++ii) {
+ const QQmlCustomParserProperty &prop = customProps.at(ii);
+ if(!prop.name().isEmpty()) { // isn't default property
+ error(prop, QQmlListModel::tr("ListModel: undefined property '%1'").arg(prop.name()));
+ return QByteArray();
+ }
+
+ if(!compileProperty(prop, instr, data)) {
+ return QByteArray();
+ }
+ }
+
+ int size = sizeof(ListModelData) +
+ instr.count() * sizeof(ListInstruction) +
+ data.count();
+
+ QByteArray rv;
+ rv.resize(size);
+
+ ListModelData *lmd = (ListModelData *)rv.data();
+ lmd->dataOffset = sizeof(ListModelData) +
+ instr.count() * sizeof(ListInstruction);
+ lmd->instrCount = instr.count();
+ for (int ii = 0; ii < instr.count(); ++ii)
+ lmd->instructions()[ii] = instr.at(ii);
+ ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count());
+
+ return rv;
+}
+
+void QQmlListModelParser::setCustomData(QObject *obj, const QByteArray &d)
+{
+ QQmlListModel *rv = static_cast<QQmlListModel *>(obj);
+
+ QV8Engine *engine = QQmlEnginePrivate::getV8Engine(qmlEngine(rv));
+ rv->m_engine = engine;
+
+ const ListModelData *lmd = (const ListModelData *)d.constData();
+ const char *data = ((const char *)lmd) + lmd->dataOffset;
+
+ bool setRoles = false;
+
+ QStack<DataStackElement> stack;
+
+ for (int ii = 0; ii < lmd->instrCount; ++ii) {
+ const ListInstruction &instr = lmd->instructions()[ii];
+
+ switch(instr.type) {
+ case ListInstruction::Push:
+ {
+ Q_ASSERT(!rv->m_dynamicRoles);
+
+ ListModel *subModel = 0;
+
+ if (stack.count() == 0) {
+ subModel = rv->m_listModel;
+ } else {
+ const DataStackElement &e0 = stack.at(stack.size() - 1);
+ DataStackElement &e1 = stack[stack.size() - 2];
+
+ const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name);
+ if (role.type == ListLayout::Role::List) {
+ subModel = e1.model->getListProperty(e1.elementIndex, role);
+
+ if (subModel == 0) {
+ subModel = new ListModel(role.subLayout, 0, -1);
+ QVariant vModel = QVariant::fromValue(subModel);
+ e1.model->setOrCreateProperty(e1.elementIndex, e0.name, vModel);
+ }
+ }
+ }
+
+ DataStackElement e;
+ e.model = subModel;
+ e.elementIndex = subModel ? subModel->appendElement() : -1;
+ stack.push(e);
+ }
+ break;
+
+ case ListInstruction::Pop:
+ stack.pop();
+ break;
+
+ case ListInstruction::Value:
+ {
+ const DataStackElement &e0 = stack.at(stack.size() - 1);
+ DataStackElement &e1 = stack[stack.size() - 2];
+
+ QString name = e0.name;
+ QVariant value;
+
+ switch (QQmlScript::Variant::Type(data[instr.dataIdx])) {
+ case QQmlScript::Variant::Invalid:
+ {
+ const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name);
+ ListModel *emptyModel = new ListModel(role.subLayout, 0, -1);
+ value = QVariant::fromValue(emptyModel);
+ }
+ break;
+ case QQmlScript::Variant::Boolean:
+ value = bool(data[1 + instr.dataIdx]);
+ break;
+ case QQmlScript::Variant::Number:
+ value = QByteArray(data + 1 + instr.dataIdx).toDouble();
+ break;
+ case QQmlScript::Variant::String:
+ value = QString::fromUtf8(data + 1 + instr.dataIdx);
+ break;
+ default:
+ Q_ASSERT("Format error in ListInstruction");
+ }
+
+ e1.model->setOrCreateProperty(e1.elementIndex, name, value);
+ setRoles = true;
+ }
+ break;
+
+ case ListInstruction::Set:
+ {
+ DataStackElement e;
+ e.name = QString::fromUtf8(data + instr.dataIdx);
+ stack.push(e);
+ }
+ break;
+ }
+ }
+
+ if (setRoles == false)
+ qmlInfo(obj) << "All ListElement declarations are empty, no roles can be created unless dynamicRoles is set.";
+}
+
+bool QQmlListModelParser::definesEmptyList(const QString &s)
+{
+ if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) {
+ for (int i=1; i<s.length()-1; i++) {
+ if (!s[i].isSpace())
+ return false;
+ }
+ return true;
+ }
+ return false;
+}
+
+
+/*!
+ \qmltype ListElement
+ \instantiates QQmlListElement
+ \inqmlmodule QtQml.Models 2
+ \brief Defines a data item in a ListModel
+
+ List elements are defined inside ListModel definitions, and represent items in a list.
+
+ This type is also available in the QtQuick 2 import. For full documentation, see \l QtQuick2::ListElement
+*/
+/*!
+ \qmltype ListElement
+ \instantiates QQmlListElement
+ \inqmlmodule QtQuick 2
+ \brief Defines a data item in a ListModel
+ \ingroup qtquick-models
+
+ List elements are defined inside ListModel definitions, and represent items in a
+ list that will be displayed using ListView or \l Repeater items.
+
+ List elements are defined like other QML elements except that they contain
+ a collection of \e role definitions instead of properties. Using the same
+ syntax as property definitions, roles both define how the data is accessed
+ and include the data itself.
+
+ The names used for roles must begin with a lower-case letter and should be
+ common to all elements in a given model. Values must be simple constants; either
+ strings (quoted and optionally within a call to QT_TR_NOOP), boolean values
+ (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter).
+
+ \section1 Referencing Roles
+
+ The role names are used by delegates to obtain data from list elements.
+ Each role name is accessible in the delegate's scope, and refers to the
+ corresponding role in the current element. Where a role name would be
+ ambiguous to use, it can be accessed via the \l{ListView::}{model}
+ property (e.g., \c{model.cost} instead of \c{cost}).
+
+ \section1 Example Usage
+
+ The following model defines a series of list elements, each of which
+ contain "name" and "cost" roles and their associated values.
+
+ \snippet qml/listmodel/listelements.qml model
+
+ The delegate obtains the name and cost for each element by simply referring
+ to \c name and \c cost:
+
+ \snippet qml/listmodel/listelements.qml view
+
+ \sa ListModel
+*/
+
+QT_END_NAMESPACE
diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h
new file mode 100644
index 0000000000..70477115bd
--- /dev/null
+++ b/src/qml/types/qqmllistmodel_p.h
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLLISTMODEL_H
+#define QQMLLISTMODEL_H
+
+#include <qqml.h>
+#include <private/qqmlcustomparser_p.h>
+
+#include <QtCore/QObject>
+#include <QtCore/QStringList>
+#include <QtCore/QHash>
+#include <QtCore/QList>
+#include <QtCore/QVariant>
+#include <QtCore/qabstractitemmodel.h>
+
+#include <private/qv8engine_p.h>
+#include <private/qpodvector_p.h>
+
+QT_BEGIN_NAMESPACE
+
+
+class QQmlListModelWorkerAgent;
+class ListModel;
+class ListLayout;
+
+class Q_QML_PRIVATE_EXPORT QQmlListModel : public QAbstractListModel
+{
+ Q_OBJECT
+ Q_PROPERTY(int count READ count NOTIFY countChanged)
+ Q_PROPERTY(bool dynamicRoles READ dynamicRoles WRITE setDynamicRoles)
+
+public:
+ QQmlListModel(QObject *parent=0);
+ ~QQmlListModel();
+
+ QModelIndex index(int row, int column, const QModelIndex &parent) const;
+ int rowCount(const QModelIndex &parent) const;
+ QVariant data(const QModelIndex &index, int role) const;
+ QHash<int,QByteArray> roleNames() const;
+
+ QVariant data(int index, int role) const;
+ int count() const;
+
+ Q_INVOKABLE void clear();
+ Q_INVOKABLE void remove(QQmlV8Function *args);
+ Q_INVOKABLE void append(QQmlV8Function *args);
+ Q_INVOKABLE void insert(QQmlV8Function *args);
+ Q_INVOKABLE QQmlV8Handle get(int index) const;
+ Q_INVOKABLE void set(int index, const QQmlV8Handle &);
+ Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value);
+ Q_INVOKABLE void move(int from, int to, int count);
+ Q_INVOKABLE void sync();
+
+ QQmlListModelWorkerAgent *agent();
+
+ bool dynamicRoles() const { return m_dynamicRoles; }
+ void setDynamicRoles(bool enableDynamicRoles);
+
+Q_SIGNALS:
+ void countChanged();
+
+private:
+ friend class QQmlListModelParser;
+ friend class QQmlListModelWorkerAgent;
+ friend class ModelObject;
+ friend class ModelNodeMetaObject;
+ friend class ListModel;
+ friend class ListElement;
+ friend class DynamicRoleModelNode;
+ friend class DynamicRoleModelNodeMetaObject;
+
+ // Constructs a flat list model for a worker agent
+ QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent);
+ QQmlListModel(const QQmlListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent=0);
+
+ QV8Engine *engine() const;
+
+ inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); }
+
+ QQmlListModelWorkerAgent *m_agent;
+ mutable QV8Engine *m_engine;
+ bool m_mainThread;
+ bool m_primary;
+
+ bool m_dynamicRoles;
+
+ ListLayout *m_layout;
+ ListModel *m_listModel;
+
+ QVector<class DynamicRoleModelNode *> m_modelObjects;
+ QVector<QString> m_roles;
+ int m_uid;
+
+ struct ElementSync
+ {
+ ElementSync() : src(0), target(0) {}
+
+ DynamicRoleModelNode *src;
+ DynamicRoleModelNode *target;
+ };
+
+ int getUid() const { return m_uid; }
+
+ static void sync(QQmlListModel *src, QQmlListModel *target, QHash<int, QQmlListModel *> *targetModelHash);
+ static QQmlListModel *createWithOwner(QQmlListModel *newOwner);
+
+ void emitItemsChanged(int index, int count, const QVector<int> &roles);
+ void emitItemsRemoved(int index, int count);
+ void emitItemsInserted(int index, int count);
+ void emitItemsMoved(int from, int to, int n);
+};
+
+// ### FIXME
+class QQmlListElement : public QObject
+{
+Q_OBJECT
+};
+
+class QQmlListModelParser : public QQmlCustomParser
+{
+public:
+ QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {}
+ QByteArray compile(const QList<QQmlCustomParserProperty> &);
+ void setCustomData(QObject *, const QByteArray &);
+
+private:
+ struct ListInstruction
+ {
+ enum { Push, Pop, Value, Set } type;
+ int dataIdx;
+ };
+ struct ListModelData
+ {
+ int dataOffset;
+ int instrCount;
+ ListInstruction *instructions() const;
+ };
+ bool compileProperty(const QQmlCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data);
+
+ bool definesEmptyList(const QString &);
+
+ QString listElementTypeName;
+
+ struct DataStackElement
+ {
+ DataStackElement() : model(0), elementIndex(0) {}
+
+ QString name;
+ ListModel *model;
+ int elementIndex;
+ };
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQmlListModel)
+QML_DECLARE_TYPE(QQmlListElement)
+
+#endif // QQMLLISTMODEL_H
diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h
new file mode 100644
index 0000000000..0190081320
--- /dev/null
+++ b/src/qml/types/qqmllistmodel_p_p.h
@@ -0,0 +1,378 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLLISTMODEL_P_P_H
+#define QQMLLISTMODEL_P_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 "qqmllistmodel_p.h"
+#include <private/qqmlengine_p.h>
+#include <private/qqmlopenmetaobject_p.h>
+#include <qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+
+class DynamicRoleModelNode;
+
+class DynamicRoleModelNodeMetaObject : public QQmlOpenMetaObject
+{
+public:
+ DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object);
+ ~DynamicRoleModelNodeMetaObject();
+
+ bool m_enabled;
+
+protected:
+ void propertyWrite(int index);
+ void propertyWritten(int index);
+
+private:
+ DynamicRoleModelNode *m_owner;
+};
+
+class DynamicRoleModelNode : public QObject
+{
+ Q_OBJECT
+public:
+ DynamicRoleModelNode(QQmlListModel *owner, int uid);
+
+ static DynamicRoleModelNode *create(const QVariantMap &obj, QQmlListModel *owner);
+
+ void updateValues(const QVariantMap &object, QVector<int> &roles);
+
+ QVariant getValue(const QString &name)
+ {
+ return m_meta->value(name.toUtf8());
+ }
+
+ bool setValue(const QByteArray &name, const QVariant &val)
+ {
+ return m_meta->setValue(name, val);
+ }
+
+ void setNodeUpdatesEnabled(bool enable)
+ {
+ m_meta->m_enabled = enable;
+ }
+
+ int getUid() const
+ {
+ return m_uid;
+ }
+
+ static void sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash<int, QQmlListModel *> *targetModelHash);
+
+private:
+ QQmlListModel *m_owner;
+ int m_uid;
+ DynamicRoleModelNodeMetaObject *m_meta;
+
+ friend class DynamicRoleModelNodeMetaObject;
+};
+
+class ModelObject;
+
+class ModelNodeMetaObject : public QQmlOpenMetaObject
+{
+public:
+ ModelNodeMetaObject(ModelObject *object);
+ ~ModelNodeMetaObject();
+
+ bool m_enabled;
+
+protected:
+ void propertyWritten(int index);
+
+private:
+
+ ModelObject *m_obj;
+};
+
+class ModelObject : public QObject
+{
+ Q_OBJECT
+public:
+ ModelObject(QQmlListModel *model, int elementIndex);
+
+ void setValue(const QByteArray &name, const QVariant &val, bool force)
+ {
+ if (force) {
+ QVariant existingValue = m_meta->value(name);
+ if (existingValue.isValid()) {
+ (*m_meta)[name] = QVariant();
+ }
+ }
+ m_meta->setValue(name, val);
+ }
+
+ void setNodeUpdatesEnabled(bool enable)
+ {
+ m_meta->m_enabled = enable;
+ }
+
+ void updateValues();
+ void updateValues(const QVector<int> &roles);
+
+ QQmlListModel *m_model;
+ int m_elementIndex;
+
+private:
+ ModelNodeMetaObject *m_meta;
+};
+
+class ListLayout
+{
+public:
+ ListLayout() : currentBlock(0), currentBlockOffset(0) {}
+ ListLayout(const ListLayout *other);
+ ~ListLayout();
+
+ class Role
+ {
+ public:
+
+ Role() : type(Invalid), blockIndex(-1), blockOffset(-1), index(-1), subLayout(0) {}
+ explicit Role(const Role *other);
+ ~Role();
+
+ // This enum must be kept in sync with the roleTypeNames variable in qdeclarativelistmodel.cpp
+ enum DataType
+ {
+ Invalid = -1,
+
+ String,
+ Number,
+ Bool,
+ List,
+ QObject,
+ VariantMap,
+ DateTime,
+
+ MaxDataType
+ };
+
+ QString name;
+ DataType type;
+ int blockIndex;
+ int blockOffset;
+ int index;
+ ListLayout *subLayout;
+ };
+
+ const Role *getRoleOrCreate(const QString &key, const QVariant &data);
+ const Role &getRoleOrCreate(v8::Handle<v8::String> key, Role::DataType type);
+ const Role &getRoleOrCreate(const QString &key, Role::DataType type);
+
+ const Role &getExistingRole(int index) { return *roles.at(index); }
+ const Role *getExistingRole(const QString &key);
+ const Role *getExistingRole(v8::Handle<v8::String> key);
+
+ int roleCount() const { return roles.count(); }
+
+ static void sync(ListLayout *src, ListLayout *target);
+
+private:
+ const Role &createRole(const QString &key, Role::DataType type);
+
+ int currentBlock;
+ int currentBlockOffset;
+ QVector<Role *> roles;
+ QStringHash<Role *> roleHash;
+};
+
+class ListElement
+{
+public:
+
+ ListElement();
+ ListElement(int existingUid);
+ ~ListElement();
+
+ static void sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash<int, ListModel *> *targetModelHash);
+
+ enum
+ {
+ BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelObject *)
+ };
+
+private:
+
+ void destroy(ListLayout *layout);
+
+ int setVariantProperty(const ListLayout::Role &role, const QVariant &d);
+
+ int setJsProperty(const ListLayout::Role &role, v8::Handle<v8::Value> d, QV8Engine *eng);
+
+ int setStringProperty(const ListLayout::Role &role, const QString &s);
+ int setDoubleProperty(const ListLayout::Role &role, double n);
+ int setBoolProperty(const ListLayout::Role &role, bool b);
+ int setListProperty(const ListLayout::Role &role, ListModel *m);
+ int setQObjectProperty(const ListLayout::Role &role, QObject *o);
+ int setVariantMapProperty(const ListLayout::Role &role, v8::Handle<v8::Object> o, QV8Engine *eng);
+ int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m);
+ int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt);
+
+ void setStringPropertyFast(const ListLayout::Role &role, const QString &s);
+ void setDoublePropertyFast(const ListLayout::Role &role, double n);
+ void setBoolPropertyFast(const ListLayout::Role &role, bool b);
+ void setQObjectPropertyFast(const ListLayout::Role &role, QObject *o);
+ void setListPropertyFast(const ListLayout::Role &role, ListModel *m);
+ void setVariantMapFast(const ListLayout::Role &role, v8::Handle<v8::Object> o, QV8Engine *eng);
+ void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt);
+
+ void clearProperty(const ListLayout::Role &role);
+
+ QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV8Engine *eng);
+ ListModel *getListProperty(const ListLayout::Role &role);
+ QString *getStringProperty(const ListLayout::Role &role);
+ QObject *getQObjectProperty(const ListLayout::Role &role);
+ QQmlGuard<QObject> *getGuardProperty(const ListLayout::Role &role);
+ QVariantMap *getVariantMapProperty(const ListLayout::Role &role);
+ QDateTime *getDateTimeProperty(const ListLayout::Role &role);
+
+ inline char *getPropertyMemory(const ListLayout::Role &role);
+
+ int getUid() const { return uid; }
+
+ char data[BLOCK_SIZE];
+ ListElement *next;
+
+ int uid;
+ ModelObject *m_objectCache;
+
+ friend class ListModel;
+};
+
+class ListModel
+{
+public:
+
+ ListModel(ListLayout *layout, QQmlListModel *modelCache, int uid);
+ ~ListModel() {}
+
+ void destroy();
+
+ int setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data);
+ int setExistingProperty(int uid, const QString &key, v8::Handle<v8::Value> data, QV8Engine *eng);
+
+ QVariant getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV8Engine *eng);
+ ListModel *getListProperty(int elementIndex, const ListLayout::Role &role);
+
+ int roleCount() const
+ {
+ return m_layout->roleCount();
+ }
+
+ const ListLayout::Role &getExistingRole(int index)
+ {
+ return m_layout->getExistingRole(index);
+ }
+
+ const ListLayout::Role &getOrCreateListRole(const QString &name)
+ {
+ return m_layout->getRoleOrCreate(name, ListLayout::Role::List);
+ }
+
+ int elementCount() const
+ {
+ return elements.count();
+ }
+
+ void set(int elementIndex, v8::Handle<v8::Object> object, QVector<int> *roles, QV8Engine *eng);
+ void set(int elementIndex, v8::Handle<v8::Object> object, QV8Engine *eng);
+
+ int append(v8::Handle<v8::Object> object, QV8Engine *eng);
+ void insert(int elementIndex, v8::Handle<v8::Object> object, QV8Engine *eng);
+
+ void clear();
+ void remove(int index, int count);
+
+ int appendElement();
+ void insertElement(int index);
+
+ void move(int from, int to, int n);
+
+ int getUid() const { return m_uid; }
+
+ static void sync(ListModel *src, ListModel *target, QHash<int, ListModel *> *srcModelHash);
+
+ ModelObject *getOrCreateModelObject(QQmlListModel *model, int elementIndex);
+
+private:
+ QPODVector<ListElement *, 4> elements;
+ ListLayout *m_layout;
+ int m_uid;
+
+ QQmlListModel *m_modelCache;
+
+ struct ElementSync
+ {
+ ElementSync() : src(0), target(0) {}
+
+ ListElement *src;
+ ListElement *target;
+ };
+
+ void newElement(int index);
+
+ void updateCacheIndices();
+
+ friend class ListElement;
+ friend class QQmlListModelWorkerAgent;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(ListModel *);
+
+#endif // QQUICKLISTMODEL_P_P_H
+
diff --git a/src/qml/types/qqmllistmodelworkeragent.cpp b/src/qml/types/qqmllistmodelworkeragent.cpp
new file mode 100644
index 0000000000..9554e6d1e5
--- /dev/null
+++ b/src/qml/types/qqmllistmodelworkeragent.cpp
@@ -0,0 +1,259 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmllistmodelworkeragent_p.h"
+#include "qqmllistmodel_p_p.h"
+#include <private/qqmldata_p.h>
+#include <private/qqmlengine_p.h>
+#include <qqmlinfo.h>
+
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+
+
+QT_BEGIN_NAMESPACE
+
+
+void QQmlListModelWorkerAgent::Data::clearChange(int uid)
+{
+ for (int i=0 ; i < changes.count() ; ++i) {
+ if (changes[i].modelUid == uid) {
+ changes.removeAt(i);
+ --i;
+ }
+ }
+}
+
+void QQmlListModelWorkerAgent::Data::insertChange(int uid, int index, int count)
+{
+ Change c = { uid, Change::Inserted, index, count, 0, QVector<int>() };
+ changes << c;
+}
+
+void QQmlListModelWorkerAgent::Data::removeChange(int uid, int index, int count)
+{
+ Change c = { uid, Change::Removed, index, count, 0, QVector<int>() };
+ changes << c;
+}
+
+void QQmlListModelWorkerAgent::Data::moveChange(int uid, int index, int count, int to)
+{
+ Change c = { uid, Change::Moved, index, count, to, QVector<int>() };
+ changes << c;
+}
+
+void QQmlListModelWorkerAgent::Data::changedChange(int uid, int index, int count, const QVector<int> &roles)
+{
+ Change c = { uid, Change::Changed, index, count, 0, roles };
+ changes << c;
+}
+
+QQmlListModelWorkerAgent::QQmlListModelWorkerAgent(QQmlListModel *model)
+: m_ref(1), m_orig(model), m_copy(new QQmlListModel(model, this))
+{
+}
+
+QQmlListModelWorkerAgent::~QQmlListModelWorkerAgent()
+{
+ mutex.lock();
+ syncDone.wakeAll();
+ mutex.unlock();
+}
+
+void QQmlListModelWorkerAgent::setV8Engine(QV8Engine *eng)
+{
+ m_copy->m_engine = eng;
+}
+
+void QQmlListModelWorkerAgent::addref()
+{
+ m_ref.ref();
+}
+
+void QQmlListModelWorkerAgent::release()
+{
+ bool del = !m_ref.deref();
+
+ if (del)
+ deleteLater();
+}
+
+void QQmlListModelWorkerAgent::modelDestroyed()
+{
+ m_orig = 0;
+}
+
+int QQmlListModelWorkerAgent::count() const
+{
+ return m_copy->count();
+}
+
+void QQmlListModelWorkerAgent::clear()
+{
+ m_copy->clear();
+}
+
+void QQmlListModelWorkerAgent::remove(QQmlV8Function *args)
+{
+ m_copy->remove(args);
+}
+
+void QQmlListModelWorkerAgent::append(QQmlV8Function *args)
+{
+ m_copy->append(args);
+}
+
+void QQmlListModelWorkerAgent::insert(QQmlV8Function *args)
+{
+ m_copy->insert(args);
+}
+
+QQmlV8Handle QQmlListModelWorkerAgent::get(int index) const
+{
+ return m_copy->get(index);
+}
+
+void QQmlListModelWorkerAgent::set(int index, const QQmlV8Handle &value)
+{
+ m_copy->set(index, value);
+}
+
+void QQmlListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value)
+{
+ m_copy->setProperty(index, property, value);
+}
+
+void QQmlListModelWorkerAgent::move(int from, int to, int count)
+{
+ m_copy->move(from, to, count);
+}
+
+void QQmlListModelWorkerAgent::sync()
+{
+ Sync *s = new Sync;
+ s->data = data;
+ s->list = m_copy;
+ data.changes.clear();
+
+ mutex.lock();
+ QCoreApplication::postEvent(this, s);
+ syncDone.wait(&mutex);
+ mutex.unlock();
+}
+
+bool QQmlListModelWorkerAgent::event(QEvent *e)
+{
+ if (e->type() == QEvent::User) {
+ bool cc = false;
+ QMutexLocker locker(&mutex);
+ if (m_orig) {
+ Sync *s = static_cast<Sync *>(e);
+ const QList<Change> &changes = s->data.changes;
+
+ cc = m_orig->count() != s->list->count();
+
+ QHash<int, QQmlListModel *> targetModelDynamicHash;
+ QHash<int, ListModel *> targetModelStaticHash;
+
+ Q_ASSERT(m_orig->m_dynamicRoles == s->list->m_dynamicRoles);
+ if (m_orig->m_dynamicRoles)
+ QQmlListModel::sync(s->list, m_orig, &targetModelDynamicHash);
+ else
+ ListModel::sync(s->list->m_listModel, m_orig->m_listModel, &targetModelStaticHash);
+
+ for (int ii = 0; ii < changes.count(); ++ii) {
+ const Change &change = changes.at(ii);
+
+ QQmlListModel *model = 0;
+ if (m_orig->m_dynamicRoles) {
+ model = targetModelDynamicHash.value(change.modelUid);
+ } else {
+ ListModel *lm = targetModelStaticHash.value(change.modelUid);
+ if (lm)
+ model = lm->m_modelCache;
+ }
+
+ if (model) {
+ switch (change.type) {
+ case Change::Inserted:
+ model->beginInsertRows(
+ QModelIndex(), change.index, change.index + change.count - 1);
+ model->endInsertRows();
+ break;
+ case Change::Removed:
+ model->beginRemoveRows(
+ QModelIndex(), change.index, change.index + change.count - 1);
+ model->endRemoveRows();
+ break;
+ case Change::Moved:
+ model->beginMoveRows(
+ QModelIndex(),
+ change.index,
+ change.index + change.count - 1,
+ QModelIndex(),
+ change.to > change.index ? change.to + change.count : change.to);
+ model->endMoveRows();
+ break;
+ case Change::Changed:
+ emit model->dataChanged(
+ model->createIndex(change.index, 0),
+ model->createIndex(change.index + change.count - 1, 0),
+ change.roles);
+ break;
+ }
+ }
+ }
+ }
+
+ syncDone.wakeAll();
+ locker.unlock();
+
+ if (cc)
+ emit m_orig->countChanged();
+ return true;
+ }
+
+ return QObject::event(e);
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/qml/types/qqmllistmodelworkeragent_p.h b/src/qml/types/qqmllistmodelworkeragent_p.h
new file mode 100644
index 0000000000..614017069c
--- /dev/null
+++ b/src/qml/types/qqmllistmodelworkeragent_p.h
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKLISTMODELWORKERAGENT_P_H
+#define QQUICKLISTMODELWORKERAGENT_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 <qqml.h>
+
+#include <QMutex>
+#include <QWaitCondition>
+
+#include <private/qv8engine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+
+class QQmlListModel;
+
+class QQmlListModelWorkerAgent : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int count READ count)
+
+public:
+ QQmlListModelWorkerAgent(QQmlListModel *);
+ ~QQmlListModelWorkerAgent();
+ void setV8Engine(QV8Engine *eng);
+
+ void addref();
+ void release();
+
+ int count() const;
+
+ Q_INVOKABLE void clear();
+ Q_INVOKABLE void remove(QQmlV8Function *args);
+ Q_INVOKABLE void append(QQmlV8Function *args);
+ Q_INVOKABLE void insert(QQmlV8Function *args);
+ Q_INVOKABLE QQmlV8Handle get(int index) const;
+ Q_INVOKABLE void set(int index, const QQmlV8Handle &);
+ Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value);
+ Q_INVOKABLE void move(int from, int to, int count);
+ Q_INVOKABLE void sync();
+
+ struct VariantRef
+ {
+ VariantRef() : a(0) {}
+ VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); }
+ VariantRef(QQmlListModelWorkerAgent *_a) : a(_a) { if (a) a->addref(); }
+ ~VariantRef() { if (a) a->release(); }
+
+ VariantRef &operator=(const VariantRef &o) {
+ if (o.a) o.a->addref();
+ if (a) a->release(); a = o.a;
+ return *this;
+ }
+
+ QQmlListModelWorkerAgent *a;
+ };
+ void modelDestroyed();
+protected:
+ virtual bool event(QEvent *);
+
+private:
+ friend class QQuickWorkerScriptEnginePrivate;
+ friend class QQmlListModel;
+
+ struct Change
+ {
+ int modelUid;
+ enum { Inserted, Removed, Moved, Changed } type;
+ int index; // Inserted/Removed/Moved/Changed
+ int count; // Inserted/Removed/Moved/Changed
+ int to; // Moved
+ QVector<int> roles;
+ };
+
+ struct Data
+ {
+ QList<Change> changes;
+
+ void clearChange(int uid);
+ void insertChange(int uid, int index, int count);
+ void removeChange(int uid, int index, int count);
+ void moveChange(int uid, int index, int count, int to);
+ void changedChange(int uid, int index, int count, const QVector<int> &roles);
+ };
+ Data data;
+
+ struct Sync : public QEvent {
+ Sync() : QEvent(QEvent::User) {}
+ Data data;
+ QQmlListModel *list;
+ };
+
+ QAtomicInt m_ref;
+ QQmlListModel *m_orig;
+ QQmlListModel *m_copy;
+ QMutex mutex;
+ QWaitCondition syncDone;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QQmlListModelWorkerAgent::VariantRef)
+
+#endif // QQUICKLISTMODELWORKERAGENT_P_H
+
diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp
new file mode 100644
index 0000000000..4f6b0a5580
--- /dev/null
+++ b/src/qml/types/qqmlmodelsmodule.cpp
@@ -0,0 +1,60 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlmodelsmodule_p.h"
+#include <private/qqmllistmodel_p.h>
+#include <private/qqmldelegatemodel_p.h>
+#include <private/qqmlobjectmodel_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QQmlModelsModule::defineModule()
+{
+ const char uri[] = "QtQml.Models";
+
+ qmlRegisterType<QQmlListElement>(uri, 2, 1, "ListElement");
+ qmlRegisterCustomType<QQmlListModel>(uri, 2, 1, "ListModel", new QQmlListModelParser);
+ qmlRegisterType<QQmlDelegateModel>(uri, 2, 1, "DelegateModel");
+ qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 1, "DelegateModelGroup");
+ qmlRegisterType<QQmlObjectModel>(uri, 2, 1, "ObjectModel");
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/types/qqmlmodelsmodule_p.h b/src/qml/types/qqmlmodelsmodule_p.h
new file mode 100644
index 0000000000..6e72dadf8b
--- /dev/null
+++ b/src/qml/types/qqmlmodelsmodule_p.h
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLMODELSMODULE_H
+#define QQMLMODELSMODULE_H
+
+#include <private/qtqmlglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_QML_PRIVATE_EXPORT QQmlModelsModule
+{
+public:
+ static void defineModule();
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/types/qqmlobjectmodel.cpp b/src/qml/types/qqmlobjectmodel.cpp
new file mode 100644
index 0000000000..7f7bf92fa3
--- /dev/null
+++ b/src/qml/types/qqmlobjectmodel.cpp
@@ -0,0 +1,269 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmlobjectmodel_p.h"
+
+#include <QtCore/qcoreapplication.h>
+#include <QtQml/qqmlcontext.h>
+#include <QtQml/qqmlengine.h>
+
+#include <private/qqmlchangeset_p.h>
+#include <private/qqmlglobal_p.h>
+#include <private/qobject_p.h>
+
+#include <QtCore/qhash.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+QHash<QObject*, QQmlObjectModelAttached*> QQmlObjectModelAttached::attachedProperties;
+
+
+class QQmlObjectModelPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QQmlObjectModel)
+public:
+ class Item {
+ public:
+ Item(QObject *i) : item(i), ref(0) {}
+
+ void addRef() { ++ref; }
+ bool deref() { return --ref == 0; }
+
+ QObject *item;
+ int ref;
+ };
+
+ QQmlObjectModelPrivate() : QObjectPrivate() {}
+
+ static void children_append(QQmlListProperty<QObject> *prop, QObject *item) {
+ static_cast<QQmlObjectModelPrivate *>(prop->data)->children.append(Item(item));
+ static_cast<QQmlObjectModelPrivate *>(prop->data)->itemAppended();
+ static_cast<QQmlObjectModelPrivate *>(prop->data)->emitChildrenChanged();
+ }
+
+ static int children_count(QQmlListProperty<QObject> *prop) {
+ return static_cast<QQmlObjectModelPrivate *>(prop->data)->children.count();
+ }
+
+ static QObject *children_at(QQmlListProperty<QObject> *prop, int index) {
+ return static_cast<QQmlObjectModelPrivate *>(prop->data)->children.at(index).item;
+ }
+
+ static void children_clear(QQmlListProperty<QObject> *prop) {
+ static_cast<QQmlObjectModelPrivate *>(prop->data)->itemCleared(static_cast<QQmlObjectModelPrivate *>(prop->data)->children);
+ static_cast<QQmlObjectModelPrivate *>(prop->data)->children.clear();
+ static_cast<QQmlObjectModelPrivate *>(prop->data)->emitChildrenChanged();
+ }
+
+ void itemAppended() {
+ Q_Q(QQmlObjectModel);
+ QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.last().item);
+ attached->setIndex(children.count()-1);
+ QQmlChangeSet changeSet;
+ changeSet.insert(children.count() - 1, 1);
+ emit q->modelUpdated(changeSet, false);
+ emit q->countChanged();
+ }
+
+ void itemCleared(const QList<Item> &children) {
+ Q_Q(QQmlObjectModel);
+ foreach (const Item &child, children)
+ emit q->destroyingItem(child.item);
+ emit q->countChanged();
+ }
+
+ void emitChildrenChanged() {
+ Q_Q(QQmlObjectModel);
+ emit q->childrenChanged();
+ }
+
+ int indexOf(QObject *item) const {
+ for (int i = 0; i < children.count(); ++i)
+ if (children.at(i).item == item)
+ return i;
+ return -1;
+ }
+
+
+ QList<Item> children;
+};
+
+
+/*!
+ \qmltype ObjectModel
+ \instantiates QQmlObjectModel
+ \inqmlmodule QtQml.Models 2
+ \ingroup qtquick-models
+ \brief Defines a set of items to be used as a model
+
+ A ObjectModel contains the visual items to be used in a view.
+ When a ObjectModel is used in a view, the view does not require
+ a delegate since the ObjectModel already contains the visual
+ delegate (items).
+
+ An item can determine its index within the
+ model via the \l{ObjectModel::index}{index} attached property.
+
+ The example below places three colored rectangles in a ListView.
+ \code
+ import QtQuick 2.0
+
+ Rectangle {
+ ObjectModel {
+ id: itemModel
+ Rectangle { height: 30; width: 80; color: "red" }
+ Rectangle { height: 30; width: 80; color: "green" }
+ Rectangle { height: 30; width: 80; color: "blue" }
+ }
+
+ ListView {
+ anchors.fill: parent
+ model: itemModel
+ }
+ }
+ \endcode
+
+ \image visualitemmodel.png
+
+ \sa {quick/views/objectmodel}{ObjectModel example}
+*/
+/*!
+ \qmltype VisualItemModel
+ \instantiates QQmlObjectModel
+ \inqmlmodule QtQuick 2
+ \brief Defines a set of objects to be used as a model
+
+ The VisualItemModel type encapsulates contains the objects to be used
+ as a model.
+
+ This element is now primarily available as ObjectModel in the QtQml.Models module.
+ VisualItemModel continues to be provided, with the same implementation, in QtQuick for
+ compatibility reasons.
+
+ For full details about the type, see the \l ObjectModel documentation.
+
+ \sa {QtQml.Models2::ObjectModel}
+*/
+
+QQmlObjectModel::QQmlObjectModel(QObject *parent)
+ : QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent)
+{
+}
+
+/*!
+ \qmlattachedproperty int QtQml.Models2::ObjectModel::index
+ This attached property holds the index of this delegate's item within the model.
+
+ It is attached to each instance of the delegate.
+*/
+
+QQmlListProperty<QObject> QQmlObjectModel::children()
+{
+ Q_D(QQmlObjectModel);
+ return QQmlListProperty<QObject>(this,
+ d,
+ d->children_append,
+ d->children_count,
+ d->children_at,
+ d->children_clear);
+}
+
+/*!
+ \qmlproperty int QtQml.Models2::ObjectModel::count
+
+ The number of items in the model. This property is readonly.
+*/
+int QQmlObjectModel::count() const
+{
+ Q_D(const QQmlObjectModel);
+ return d->children.count();
+}
+
+bool QQmlObjectModel::isValid() const
+{
+ return true;
+}
+
+QObject *QQmlObjectModel::object(int index, bool)
+{
+ Q_D(QQmlObjectModel);
+ QQmlObjectModelPrivate::Item &item = d->children[index];
+ item.addRef();
+ if (item.ref == 1) {
+ emit initItem(index, item.item);
+ emit createdItem(index, item.item);
+ }
+ return item.item;
+}
+
+QQmlInstanceModel::ReleaseFlags QQmlObjectModel::release(QObject *item)
+{
+ Q_D(QQmlObjectModel);
+ int idx = d->indexOf(item);
+ if (idx >= 0) {
+ if (!d->children[idx].deref())
+ return QQmlInstanceModel::Referenced;
+ }
+ return 0;
+}
+
+QString QQmlObjectModel::stringValue(int index, const QString &name)
+{
+ Q_D(QQmlObjectModel);
+ if (index < 0 || index >= d->children.count())
+ return QString();
+ return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(name).toString();
+}
+
+int QQmlObjectModel::indexOf(QObject *item, QObject *) const
+{
+ Q_D(const QQmlObjectModel);
+ return d->indexOf(item);
+}
+
+QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj)
+{
+ return QQmlObjectModelAttached::properties(obj);
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qml/types/qqmlobjectmodel_p.h
new file mode 100644
index 0000000000..59a4a551a7
--- /dev/null
+++ b/src/qml/types/qqmlobjectmodel_p.h
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLINSTANCEMODEL_P_H
+#define QQMLINSTANCEMODEL_P_H
+
+#include <private/qtqmlglobal_p.h>
+#include <QtQml/qqml.h>
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QObject;
+class QQmlChangeSet;
+
+class Q_QML_PRIVATE_EXPORT QQmlInstanceModel : public QObject
+{
+ Q_OBJECT
+
+ Q_PROPERTY(int count READ count NOTIFY countChanged)
+
+public:
+ virtual ~QQmlInstanceModel() {}
+
+ enum ReleaseFlag { Referenced = 0x01, Destroyed = 0x02 };
+ Q_DECLARE_FLAGS(ReleaseFlags, ReleaseFlag)
+
+ virtual int count() const = 0;
+ virtual bool isValid() const = 0;
+ virtual QObject *object(int index, bool asynchronous=false) = 0;
+ virtual ReleaseFlags release(QObject *object) = 0;
+ virtual void cancel(int) {}
+ virtual QString stringValue(int, const QString &) = 0;
+ virtual void setWatchedRoles(QList<QByteArray> roles) = 0;
+
+ virtual int indexOf(QObject *object, QObject *objectContext) const = 0;
+
+Q_SIGNALS:
+ void countChanged();
+ void modelUpdated(const QQmlChangeSet &changeSet, bool reset);
+ void createdItem(int index, QObject *object);
+ void initItem(int index, QObject *object);
+ void destroyingItem(QObject *object);
+
+protected:
+ QQmlInstanceModel(QObjectPrivate &dd, QObject *parent = 0)
+ : QObject(dd, parent) {}
+
+private:
+ Q_DISABLE_COPY(QQmlInstanceModel)
+};
+
+class QQmlObjectModelAttached;
+class QQmlObjectModelPrivate;
+class Q_QML_PRIVATE_EXPORT QQmlObjectModel : public QQmlInstanceModel
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QQmlObjectModel)
+
+ Q_PROPERTY(QQmlListProperty<QObject> children READ children NOTIFY childrenChanged DESIGNABLE false)
+ Q_CLASSINFO("DefaultProperty", "children")
+
+public:
+ QQmlObjectModel(QObject *parent=0);
+ virtual ~QQmlObjectModel() {}
+
+ virtual int count() const;
+ virtual bool isValid() const;
+ virtual QObject *object(int index, bool asynchronous=false);
+ virtual ReleaseFlags release(QObject *object);
+ virtual QString stringValue(int index, const QString &role);
+ virtual void setWatchedRoles(QList<QByteArray>) {}
+
+ virtual int indexOf(QObject *object, QObject *objectContext) const;
+
+ QQmlListProperty<QObject> children();
+
+ static QQmlObjectModelAttached *qmlAttachedProperties(QObject *obj);
+
+Q_SIGNALS:
+ void childrenChanged();
+
+private:
+ Q_DISABLE_COPY(QQmlObjectModel)
+};
+
+class QQmlObjectModelAttached : public QObject
+{
+ Q_OBJECT
+
+public:
+ QQmlObjectModelAttached(QObject *parent)
+ : QObject(parent), m_index(0) {}
+ ~QQmlObjectModelAttached() {
+ attachedProperties.remove(parent());
+ }
+
+ Q_PROPERTY(int index READ index NOTIFY indexChanged)
+ int index() const { return m_index; }
+ void setIndex(int idx) {
+ if (m_index != idx) {
+ m_index = idx;
+ emit indexChanged();
+ }
+ }
+
+ static QQmlObjectModelAttached *properties(QObject *obj) {
+ QQmlObjectModelAttached *rv = attachedProperties.value(obj);
+ if (!rv) {
+ rv = new QQmlObjectModelAttached(obj);
+ attachedProperties.insert(obj, rv);
+ }
+ return rv;
+ }
+
+Q_SIGNALS:
+ void indexChanged();
+
+public:
+ int m_index;
+
+ static QHash<QObject*, QQmlObjectModelAttached*> attachedProperties;
+};
+
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQmlInstanceModel)
+QML_DECLARE_TYPE(QQmlObjectModel)
+QML_DECLARE_TYPEINFO(QQmlObjectModel, QML_HAS_ATTACHED_PROPERTIES)
+
+#endif // QQMLINSTANCEMODEL_P_H
diff --git a/src/qml/types/qqmltimer.cpp b/src/qml/types/qqmltimer.cpp
new file mode 100644
index 0000000000..a1cb8532f7
--- /dev/null
+++ b/src/qml/types/qqmltimer.cpp
@@ -0,0 +1,328 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qqmltimer_p.h"
+
+#include <QtCore/qcoreapplication.h>
+#include "private/qpauseanimationjob_p.h"
+#include <qdebug.h>
+
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+
+
+class QQmlTimerPrivate : public QObjectPrivate, public QAnimationJobChangeListener
+{
+ Q_DECLARE_PUBLIC(QQmlTimer)
+public:
+ QQmlTimerPrivate()
+ : interval(1000), running(false), repeating(false), triggeredOnStart(false)
+ , classBegun(false), componentComplete(false), firstTick(true) {}
+
+ virtual void animationFinished(QAbstractAnimationJob *);
+ virtual void animationCurrentLoopChanged(QAbstractAnimationJob *) { Q_Q(QQmlTimer); q->ticked(); }
+
+ int interval;
+ QPauseAnimationJob pause;
+ bool running : 1;
+ bool repeating : 1;
+ bool triggeredOnStart : 1;
+ bool classBegun : 1;
+ bool componentComplete : 1;
+ bool firstTick : 1;
+};
+
+/*!
+ \qmltype Timer
+ \instantiates QQmlTimer
+ \inqmlmodule QtQml 2
+ \ingroup qtquick-interceptors
+ \brief Triggers a handler at a specified interval
+
+ A Timer can be used to trigger an action either once, or repeatedly
+ at a given interval.
+
+ Here is a Timer that shows the current date and time, and updates
+ the text every 500 milliseconds. It uses the JavaScript \c Date
+ object to access the current time.
+
+ \qml
+ import QtQuick 2.0
+
+ Item {
+ Timer {
+ interval: 500; running: true; repeat: true
+ onTriggered: time.text = Date().toString()
+ }
+
+ Text { id: time }
+ }
+ \endqml
+
+ The Timer type is synchronized with the animation timer. Since the animation
+ timer is usually set to 60fps, the resolution of Timer will be
+ at best 16ms.
+
+ If the Timer is running and one of its properties is changed, the
+ elapsed time will be reset. For example, if a Timer with interval of
+ 1000ms has its \e repeat property changed 500ms after starting, the
+ elapsed time will be reset to 0, and the Timer will be triggered
+ 1000ms later.
+
+ \sa {declarative/toys/clocks}{Clocks example}
+*/
+
+QQmlTimer::QQmlTimer(QObject *parent)
+ : QObject(*(new QQmlTimerPrivate), parent)
+{
+ Q_D(QQmlTimer);
+ d->pause.addAnimationChangeListener(d, QAbstractAnimationJob::Completion | QAbstractAnimationJob::CurrentLoop);
+ d->pause.setLoopCount(1);
+ d->pause.setDuration(d->interval);
+}
+
+/*!
+ \qmlproperty int QtQml2::Timer::interval
+
+ Sets the \a interval between triggers, in milliseconds.
+
+ The default interval is 1000 milliseconds.
+*/
+void QQmlTimer::setInterval(int interval)
+{
+ Q_D(QQmlTimer);
+ if (interval != d->interval) {
+ d->interval = interval;
+ update();
+ emit intervalChanged();
+ }
+}
+
+int QQmlTimer::interval() const
+{
+ Q_D(const QQmlTimer);
+ return d->interval;
+}
+
+/*!
+ \qmlproperty bool QtQml2::Timer::running
+
+ If set to true, starts the timer; otherwise stops the timer.
+ For a non-repeating timer, \a running is set to false after the
+ timer has been triggered.
+
+ \a running defaults to false.
+
+ \sa repeat
+*/
+bool QQmlTimer::isRunning() const
+{
+ Q_D(const QQmlTimer);
+ return d->running;
+}
+
+void QQmlTimer::setRunning(bool running)
+{
+ Q_D(QQmlTimer);
+ if (d->running != running) {
+ d->running = running;
+ d->firstTick = true;
+ emit runningChanged();
+ update();
+ }
+}
+
+/*!
+ \qmlproperty bool QtQml2::Timer::repeat
+
+ If \a repeat is true the timer is triggered repeatedly at the
+ specified interval; otherwise, the timer will trigger once at the
+ specified interval and then stop (i.e. running will be set to false).
+
+ \a repeat defaults to false.
+
+ \sa running
+*/
+bool QQmlTimer::isRepeating() const
+{
+ Q_D(const QQmlTimer);
+ return d->repeating;
+}
+
+void QQmlTimer::setRepeating(bool repeating)
+{
+ Q_D(QQmlTimer);
+ if (repeating != d->repeating) {
+ d->repeating = repeating;
+ update();
+ emit repeatChanged();
+ }
+}
+
+/*!
+ \qmlproperty bool QtQml2::Timer::triggeredOnStart
+
+ When a timer is started, the first trigger is usually after the specified
+ interval has elapsed. It is sometimes desirable to trigger immediately
+ when the timer is started; for example, to establish an initial
+ state.
+
+ If \a triggeredOnStart is true, the timer is triggered immediately
+ when started, and subsequently at the specified interval. Note that if
+ \e repeat is set to false, the timer is triggered twice; once on start,
+ and again at the interval.
+
+ \a triggeredOnStart defaults to false.
+
+ \sa running
+*/
+bool QQmlTimer::triggeredOnStart() const
+{
+ Q_D(const QQmlTimer);
+ return d->triggeredOnStart;
+}
+
+void QQmlTimer::setTriggeredOnStart(bool triggeredOnStart)
+{
+ Q_D(QQmlTimer);
+ if (d->triggeredOnStart != triggeredOnStart) {
+ d->triggeredOnStart = triggeredOnStart;
+ update();
+ emit triggeredOnStartChanged();
+ }
+}
+
+/*!
+ \qmlmethod QtQml2::Timer::start()
+ \brief Starts the timer
+
+ If the timer is already running, calling this method has no effect. The
+ \c running property will be true following a call to \c start().
+*/
+void QQmlTimer::start()
+{
+ setRunning(true);
+}
+
+/*!
+ \qmlmethod QtQml2::Timer::stop()
+ \brief Stops the timer
+
+ If the timer is not running, calling this method has no effect. The
+ \c running property will be false following a call to \c stop().
+*/
+void QQmlTimer::stop()
+{
+ setRunning(false);
+}
+
+/*!
+ \qmlmethod QtQml2::Timer::restart()
+ \brief Restarts the timer
+
+ If the Timer is not running it will be started, otherwise it will be
+ stopped, reset to initial state and started. The \c running property
+ will be true following a call to \c restart().
+*/
+void QQmlTimer::restart()
+{
+ setRunning(false);
+ setRunning(true);
+}
+
+void QQmlTimer::update()
+{
+ Q_D(QQmlTimer);
+ if (d->classBegun && !d->componentComplete)
+ return;
+ d->pause.stop();
+ if (d->running) {
+ d->pause.setCurrentTime(0);
+ d->pause.setLoopCount(d->repeating ? -1 : 1);
+ d->pause.setDuration(d->interval);
+ d->pause.start();
+ if (d->triggeredOnStart && d->firstTick) {
+ QCoreApplication::removePostedEvents(this, QEvent::MetaCall);
+ QMetaObject::invokeMethod(this, "ticked", Qt::QueuedConnection);
+ }
+ }
+}
+
+void QQmlTimer::classBegin()
+{
+ Q_D(QQmlTimer);
+ d->classBegun = true;
+}
+
+void QQmlTimer::componentComplete()
+{
+ Q_D(QQmlTimer);
+ d->componentComplete = true;
+ update();
+}
+
+/*!
+ \qmlsignal QtQml2::Timer::onTriggered()
+
+ This handler is called when the Timer is triggered.
+*/
+void QQmlTimer::ticked()
+{
+ Q_D(QQmlTimer);
+ if (d->running && (d->pause.currentTime() > 0 || (d->triggeredOnStart && d->firstTick)))
+ emit triggered();
+ d->firstTick = false;
+}
+
+void QQmlTimerPrivate::animationFinished(QAbstractAnimationJob *)
+{
+ Q_Q(QQmlTimer);
+ if (repeating || !running)
+ return;
+ running = false;
+ firstTick = false;
+ emit q->triggered();
+ emit q->runningChanged();
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/types/qqmltimer_p.h b/src/qml/types/qqmltimer_p.h
new file mode 100644
index 0000000000..c625522851
--- /dev/null
+++ b/src/qml/types/qqmltimer_p.h
@@ -0,0 +1,107 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQMLTIMER_H
+#define QQMLTIMER_H
+
+#include <qqml.h>
+
+#include <QtCore/qobject.h>
+
+#include <private/qtqmlglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQmlTimerPrivate;
+class Q_QML_PRIVATE_EXPORT QQmlTimer : public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QQmlTimer)
+ Q_INTERFACES(QQmlParserStatus)
+ Q_PROPERTY(int interval READ interval WRITE setInterval NOTIFY intervalChanged)
+ Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
+ Q_PROPERTY(bool repeat READ isRepeating WRITE setRepeating NOTIFY repeatChanged)
+ Q_PROPERTY(bool triggeredOnStart READ triggeredOnStart WRITE setTriggeredOnStart NOTIFY triggeredOnStartChanged)
+ Q_PROPERTY(QObject *parent READ parent CONSTANT)
+
+public:
+ QQmlTimer(QObject *parent=0);
+
+ void setInterval(int interval);
+ int interval() const;
+
+ bool isRunning() const;
+ void setRunning(bool running);
+
+ bool isRepeating() const;
+ void setRepeating(bool repeating);
+
+ bool triggeredOnStart() const;
+ void setTriggeredOnStart(bool triggeredOnStart);
+
+protected:
+ void classBegin();
+ void componentComplete();
+
+public Q_SLOTS:
+ void start();
+ void stop();
+ void restart();
+
+Q_SIGNALS:
+ void triggered();
+ void runningChanged();
+ void intervalChanged();
+ void repeatChanged();
+ void triggeredOnStartChanged();
+
+private:
+ void update();
+
+private Q_SLOTS:
+ void ticked();
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQmlTimer)
+
+#endif
diff --git a/src/qml/types/qquickpackage.cpp b/src/qml/types/qquickpackage.cpp
new file mode 100644
index 0000000000..e885524b27
--- /dev/null
+++ b/src/qml/types/qquickpackage.cpp
@@ -0,0 +1,198 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickpackage_p.h"
+
+#include <private/qobject_p.h>
+#include <private/qqmlguard_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype Package
+ \instantiates QQuickPackage
+ \inqmlmodule QtQuick 2
+ \ingroup qtquick-views
+ \brief Specifies a collection of named items
+
+ The Package class is used in conjunction with
+ VisualDataModel to enable delegates with a shared context
+ to be provided to multiple views.
+
+ Any item within a Package may be assigned a name via the
+ \l{Package::name}{Package.name} attached property.
+
+ The example below creates a Package containing two named items;
+ \e list and \e grid. The third item in the package (the \l Rectangle) is parented to whichever
+ delegate it should appear in. This allows an item to move
+ between views.
+
+ \snippet quick/views/package/Delegate.qml 0
+
+ These named items are used as the delegates by the two views who
+ reference the special \l{VisualDataModel::parts} property to select
+ a model which provides the chosen delegate.
+
+ \snippet quick/views/package/view.qml 0
+
+ \sa {quick/views/package}{Package example}, {quick/demos/photoviewer}{Photo Viewer example}, QtQml
+*/
+
+/*!
+ \qmlattachedproperty string QtQuick2::Package::name
+ This attached property holds the name of an item within a Package.
+*/
+
+
+class QQuickPackagePrivate : public QObjectPrivate
+{
+public:
+ QQuickPackagePrivate() {}
+
+ struct DataGuard : public QQmlGuard<QObject>
+ {
+ DataGuard(QObject *obj, QList<DataGuard> *l) : list(l) { (QQmlGuard<QObject>&)*this = obj; }
+ QList<DataGuard> *list;
+ void objectDestroyed(QObject *) {
+ // we assume priv will always be destroyed after objectDestroyed calls
+ list->removeOne(*this);
+ }
+ };
+
+ QList<DataGuard> dataList;
+ static void data_append(QQmlListProperty<QObject> *prop, QObject *o) {
+ QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data);
+ list->append(DataGuard(o, list));
+ }
+ static void data_clear(QQmlListProperty<QObject> *prop) {
+ QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data);
+ list->clear();
+ }
+ static QObject *data_at(QQmlListProperty<QObject> *prop, int index) {
+ QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data);
+ return list->at(index);
+ }
+ static int data_count(QQmlListProperty<QObject> *prop) {
+ QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data);
+ return list->count();
+ }
+};
+
+QHash<QObject *, QQuickPackageAttached *> QQuickPackageAttached::attached;
+
+QQuickPackageAttached::QQuickPackageAttached(QObject *parent)
+: QObject(parent)
+{
+ attached.insert(parent, this);
+}
+
+QQuickPackageAttached::~QQuickPackageAttached()
+{
+ attached.remove(parent());
+}
+
+QString QQuickPackageAttached::name() const
+{
+ return _name;
+}
+
+void QQuickPackageAttached::setName(const QString &n)
+{
+ _name = n;
+}
+
+QQuickPackage::QQuickPackage(QObject *parent)
+ : QObject(*(new QQuickPackagePrivate), parent)
+{
+}
+
+QQuickPackage::~QQuickPackage()
+{
+}
+
+QQmlListProperty<QObject> QQuickPackage::data()
+{
+ Q_D(QQuickPackage);
+ return QQmlListProperty<QObject>(this, &d->dataList, QQuickPackagePrivate::data_append,
+ QQuickPackagePrivate::data_count,
+ QQuickPackagePrivate::data_at,
+ QQuickPackagePrivate::data_clear);
+}
+
+bool QQuickPackage::hasPart(const QString &name)
+{
+ Q_D(QQuickPackage);
+ for (int ii = 0; ii < d->dataList.count(); ++ii) {
+ QObject *obj = d->dataList.at(ii);
+ QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj);
+ if (a && a->name() == name)
+ return true;
+ }
+ return false;
+}
+
+QObject *QQuickPackage::part(const QString &name)
+{
+ Q_D(QQuickPackage);
+ if (name.isEmpty() && !d->dataList.isEmpty())
+ return d->dataList.at(0);
+
+ for (int ii = 0; ii < d->dataList.count(); ++ii) {
+ QObject *obj = d->dataList.at(ii);
+ QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj);
+ if (a && a->name() == name)
+ return obj;
+ }
+
+ if (name == QLatin1String("default") && !d->dataList.isEmpty())
+ return d->dataList.at(0);
+
+ return 0;
+}
+
+QQuickPackageAttached *QQuickPackage::qmlAttachedProperties(QObject *o)
+{
+ return new QQuickPackageAttached(o);
+}
+
+
+
+QT_END_NAMESPACE
diff --git a/src/qml/types/qquickpackage_p.h b/src/qml/types/qquickpackage_p.h
new file mode 100644
index 0000000000..9427c886a8
--- /dev/null
+++ b/src/qml/types/qquickpackage_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKPACKAGE_H
+#define QQUICKPACKAGE_H
+
+#include <qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickPackagePrivate;
+class QQuickPackageAttached;
+class Q_AUTOTEST_EXPORT QQuickPackage : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QQuickPackage)
+
+ Q_CLASSINFO("DefaultProperty", "data")
+ Q_PROPERTY(QQmlListProperty<QObject> data READ data)
+
+public:
+ QQuickPackage(QObject *parent=0);
+ virtual ~QQuickPackage();
+
+ QQmlListProperty<QObject> data();
+
+ QObject *part(const QString & = QString());
+ bool hasPart(const QString &);
+
+ static QQuickPackageAttached *qmlAttachedProperties(QObject *);
+};
+
+class QQuickPackageAttached : public QObject
+{
+Q_OBJECT
+Q_PROPERTY(QString name READ name WRITE setName)
+public:
+ QQuickPackageAttached(QObject *parent);
+ virtual ~QQuickPackageAttached();
+
+ QString name() const;
+ void setName(const QString &n);
+
+ static QHash<QObject *, QQuickPackageAttached *> attached;
+private:
+ QString _name;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickPackage)
+QML_DECLARE_TYPEINFO(QQuickPackage, QML_HAS_ATTACHED_PROPERTIES)
+
+#endif // QQUICKPACKAGE_H
diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp
new file mode 100644
index 0000000000..ad09e1ec0c
--- /dev/null
+++ b/src/qml/types/qquickworkerscript.cpp
@@ -0,0 +1,740 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickworkerscript_p.h"
+#include "qqmllistmodel_p.h"
+#include "qqmllistmodelworkeragent_p.h"
+#include <private/qqmlengine_p.h>
+#include <private/qqmlexpression_p.h>
+
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+#include <QtQml/qjsengine.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qwaitcondition.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qdatetime.h>
+#include <QtNetwork/qnetworkaccessmanager.h>
+#include <QtQml/qqmlinfo.h>
+#include <QtQml/qqmlfile.h>
+#include "qqmlnetworkaccessmanagerfactory.h"
+
+#include <private/qv8engine_p.h>
+#include <private/qv8worker_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class WorkerDataEvent : public QEvent
+{
+public:
+ enum Type { WorkerData = QEvent::User };
+
+ WorkerDataEvent(int workerId, const QByteArray &data);
+ virtual ~WorkerDataEvent();
+
+ int workerId() const;
+ QByteArray data() const;
+
+private:
+ int m_id;
+ QByteArray m_data;
+};
+
+class WorkerLoadEvent : public QEvent
+{
+public:
+ enum Type { WorkerLoad = WorkerDataEvent::WorkerData + 1 };
+
+ WorkerLoadEvent(int workerId, const QUrl &url);
+
+ int workerId() const;
+ QUrl url() const;
+
+private:
+ int m_id;
+ QUrl m_url;
+};
+
+class WorkerRemoveEvent : public QEvent
+{
+public:
+ enum Type { WorkerRemove = WorkerLoadEvent::WorkerLoad + 1 };
+
+ WorkerRemoveEvent(int workerId);
+
+ int workerId() const;
+
+private:
+ int m_id;
+};
+
+class WorkerErrorEvent : public QEvent
+{
+public:
+ enum Type { WorkerError = WorkerRemoveEvent::WorkerRemove + 1 };
+
+ WorkerErrorEvent(const QQmlError &error);
+
+ QQmlError error() const;
+
+private:
+ QQmlError m_error;
+};
+
+class QQuickWorkerScriptEnginePrivate : public QObject
+{
+ Q_OBJECT
+public:
+ enum WorkerEventTypes {
+ WorkerDestroyEvent = QEvent::User + 100
+ };
+
+ QQuickWorkerScriptEnginePrivate(QQmlEngine *eng);
+
+ class WorkerEngine : public QV8Engine
+ {
+ public:
+ WorkerEngine(QQuickWorkerScriptEnginePrivate *parent);
+ ~WorkerEngine();
+
+ void init();
+ virtual QNetworkAccessManager *networkAccessManager();
+
+ QQuickWorkerScriptEnginePrivate *p;
+
+ v8::Local<v8::Function> sendFunction(int id);
+ void callOnMessage(v8::Handle<v8::Object> object, v8::Handle<v8::Value> arg);
+ private:
+ v8::Persistent<v8::Function> onmessage;
+ v8::Persistent<v8::Function> createsend;
+ QNetworkAccessManager *accessManager;
+ };
+
+ WorkerEngine *workerEngine;
+ static QQuickWorkerScriptEnginePrivate *get(QV8Engine *e) {
+ return static_cast<WorkerEngine *>(e)->p;
+ }
+
+ QQmlEngine *qmlengine;
+
+ QMutex m_lock;
+ QWaitCondition m_wait;
+
+ struct WorkerScript {
+ WorkerScript();
+ ~WorkerScript();
+
+ int id;
+ QUrl source;
+ bool initialized;
+ QQuickWorkerScript *owner;
+ v8::Persistent<v8::Object> object;
+ };
+
+ QHash<int, WorkerScript *> workers;
+ v8::Handle<v8::Object> getWorker(WorkerScript *);
+
+ int m_nextId;
+
+ static v8::Handle<v8::Value> sendMessage(const v8::Arguments &args);
+
+signals:
+ void stopThread();
+
+protected:
+ virtual bool event(QEvent *);
+
+private:
+ void processMessage(int, const QByteArray &);
+ void processLoad(int, const QUrl &);
+ void reportScriptException(WorkerScript *, const QQmlError &error);
+};
+
+QQuickWorkerScriptEnginePrivate::WorkerEngine::WorkerEngine(QQuickWorkerScriptEnginePrivate *parent)
+: QV8Engine(0), p(parent), accessManager(0)
+{
+}
+
+QQuickWorkerScriptEnginePrivate::WorkerEngine::~WorkerEngine()
+{
+ qPersistentDispose(createsend);
+ qPersistentDispose(onmessage);
+ delete accessManager;
+}
+
+void QQuickWorkerScriptEnginePrivate::WorkerEngine::init()
+{
+ initQmlGlobalObject();
+#define CALL_ONMESSAGE_SCRIPT \
+ "(function(object, message) { "\
+ "var isfunction = false; "\
+ "try { "\
+ "isfunction = object.WorkerScript.onMessage instanceof Function; "\
+ "} catch (e) {}" \
+ "if (isfunction) "\
+ "object.WorkerScript.onMessage(message); "\
+ "})"
+
+#define SEND_MESSAGE_CREATE_SCRIPT \
+ "(function(method, engine) { "\
+ "return (function(id) { "\
+ "return (function(message) { "\
+ "if (arguments.length) method(engine, id, message); "\
+ "}); "\
+ "}); "\
+ "})"
+
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(context());
+
+ {
+ v8::Local<v8::Script> onmessagescript = v8::Script::New(v8::String::New(CALL_ONMESSAGE_SCRIPT));
+ onmessage = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(onmessagescript->Run()));
+ }
+ {
+ v8::Local<v8::Script> createsendscript = v8::Script::New(v8::String::New(SEND_MESSAGE_CREATE_SCRIPT));
+ v8::Local<v8::Function> createsendconstructor = v8::Local<v8::Function>::Cast(createsendscript->Run());
+
+ v8::Handle<v8::Value> args[] = {
+ V8FUNCTION(QQuickWorkerScriptEnginePrivate::sendMessage, this)
+ };
+ v8::Local<v8::Value> createsendvalue = createsendconstructor->Call(global(), 1, args);
+
+ createsend = qPersistentNew<v8::Function>(v8::Handle<v8::Function>::Cast(createsendvalue));
+ }
+}
+
+// Requires handle and context scope
+v8::Local<v8::Function> QQuickWorkerScriptEnginePrivate::WorkerEngine::sendFunction(int id)
+{
+ v8::Handle<v8::Value> args[] = { v8::Integer::New(id) };
+ return v8::Local<v8::Function>::Cast(createsend->Call(global(), 1, args));
+}
+
+// Requires handle and context scope
+void QQuickWorkerScriptEnginePrivate::WorkerEngine::callOnMessage(v8::Handle<v8::Object> object,
+ v8::Handle<v8::Value> arg)
+{
+ v8::Handle<v8::Value> args[] = { object, arg };
+ onmessage->Call(global(), 2, args);
+}
+
+QNetworkAccessManager *QQuickWorkerScriptEnginePrivate::WorkerEngine::networkAccessManager()
+{
+ if (!accessManager) {
+ if (p->qmlengine && p->qmlengine->networkAccessManagerFactory()) {
+ accessManager = p->qmlengine->networkAccessManagerFactory()->create(p);
+ } else {
+ accessManager = new QNetworkAccessManager(p);
+ }
+ }
+ return accessManager;
+}
+
+QQuickWorkerScriptEnginePrivate::QQuickWorkerScriptEnginePrivate(QQmlEngine *engine)
+: workerEngine(0), qmlengine(engine), m_nextId(0)
+{
+}
+
+v8::Handle<v8::Value> QQuickWorkerScriptEnginePrivate::sendMessage(const v8::Arguments &args)
+{
+ WorkerEngine *engine = (WorkerEngine*)V8ENGINE();
+
+ int id = args[1]->Int32Value();
+
+ QByteArray data = QV8Worker::serialize(args[2], engine);
+
+ QMutexLocker locker(&engine->p->m_lock);
+ WorkerScript *script = engine->p->workers.value(id);
+ if (!script)
+ return v8::Undefined();
+
+ if (script->owner)
+ QCoreApplication::postEvent(script->owner, new WorkerDataEvent(0, data));
+
+ return v8::Undefined();
+}
+
+// Requires handle scope and context scope
+v8::Handle<v8::Object> QQuickWorkerScriptEnginePrivate::getWorker(WorkerScript *script)
+{
+ if (!script->initialized) {
+ script->initialized = true;
+
+ script->object = qPersistentNew<v8::Object>(workerEngine->contextWrapper()->urlScope(script->source));
+
+ workerEngine->contextWrapper()->setReadOnly(script->object, false);
+
+ v8::Local<v8::Object> api = v8::Object::New();
+ api->Set(v8::String::New("sendMessage"), workerEngine->sendFunction(script->id));
+
+ script->object->Set(v8::String::New("WorkerScript"), api);
+
+ workerEngine->contextWrapper()->setReadOnly(script->object, true);
+ }
+
+ return script->object;
+}
+
+bool QQuickWorkerScriptEnginePrivate::event(QEvent *event)
+{
+ if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) {
+ WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event);
+ processMessage(workerEvent->workerId(), workerEvent->data());
+ return true;
+ } else if (event->type() == (QEvent::Type)WorkerLoadEvent::WorkerLoad) {
+ WorkerLoadEvent *workerEvent = static_cast<WorkerLoadEvent *>(event);
+ processLoad(workerEvent->workerId(), workerEvent->url());
+ return true;
+ } else if (event->type() == (QEvent::Type)WorkerDestroyEvent) {
+ emit stopThread();
+ return true;
+ } else if (event->type() == (QEvent::Type)WorkerRemoveEvent::WorkerRemove) {
+ WorkerRemoveEvent *workerEvent = static_cast<WorkerRemoveEvent *>(event);
+ workers.remove(workerEvent->workerId());
+ return true;
+ } else {
+ return QObject::event(event);
+ }
+}
+
+void QQuickWorkerScriptEnginePrivate::processMessage(int id, const QByteArray &data)
+{
+ WorkerScript *script = workers.value(id);
+ if (!script)
+ return;
+
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(workerEngine->context());
+
+ v8::Handle<v8::Value> value = QV8Worker::deserialize(data, workerEngine);
+
+ v8::TryCatch tc;
+ workerEngine->callOnMessage(script->object, value);
+
+ if (tc.HasCaught()) {
+ QQmlError error;
+ QQmlExpressionPrivate::exceptionToError(tc.Message(), error);
+ reportScriptException(script, error);
+ }
+}
+
+void QQuickWorkerScriptEnginePrivate::processLoad(int id, const QUrl &url)
+{
+ if (url.isRelative())
+ return;
+
+ QString fileName = QQmlFile::urlToLocalFileOrQrc(url);
+
+ QFile f(fileName);
+ if (f.open(QIODevice::ReadOnly)) {
+ QByteArray data = f.readAll();
+ QString sourceCode = QString::fromUtf8(data);
+ QQmlScript::Parser::extractPragmas(sourceCode);
+
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(workerEngine->context());
+
+ WorkerScript *script = workers.value(id);
+ if (!script)
+ return;
+ script->source = url;
+ v8::Handle<v8::Object> activation = getWorker(script);
+ if (activation.IsEmpty())
+ return;
+
+ // XXX ???
+ // workerEngine->baseUrl = url;
+
+ v8::TryCatch tc;
+ v8::Local<v8::Script> program = workerEngine->qmlModeCompile(sourceCode, url.toString());
+
+ if (!tc.HasCaught())
+ program->Run(activation);
+
+ if (tc.HasCaught()) {
+ QQmlError error;
+ QQmlExpressionPrivate::exceptionToError(tc.Message(), error);
+ reportScriptException(script, error);
+ }
+ } else {
+ qWarning().nospace() << "WorkerScript: Cannot find source file " << url.toString();
+ }
+}
+
+void QQuickWorkerScriptEnginePrivate::reportScriptException(WorkerScript *script,
+ const QQmlError &error)
+{
+ QQuickWorkerScriptEnginePrivate *p = QQuickWorkerScriptEnginePrivate::get(workerEngine);
+
+ QMutexLocker locker(&p->m_lock);
+ if (script->owner)
+ QCoreApplication::postEvent(script->owner, new WorkerErrorEvent(error));
+}
+
+WorkerDataEvent::WorkerDataEvent(int workerId, const QByteArray &data)
+: QEvent((QEvent::Type)WorkerData), m_id(workerId), m_data(data)
+{
+}
+
+WorkerDataEvent::~WorkerDataEvent()
+{
+}
+
+int WorkerDataEvent::workerId() const
+{
+ return m_id;
+}
+
+QByteArray WorkerDataEvent::data() const
+{
+ return m_data;
+}
+
+WorkerLoadEvent::WorkerLoadEvent(int workerId, const QUrl &url)
+: QEvent((QEvent::Type)WorkerLoad), m_id(workerId), m_url(url)
+{
+}
+
+int WorkerLoadEvent::workerId() const
+{
+ return m_id;
+}
+
+QUrl WorkerLoadEvent::url() const
+{
+ return m_url;
+}
+
+WorkerRemoveEvent::WorkerRemoveEvent(int workerId)
+: QEvent((QEvent::Type)WorkerRemove), m_id(workerId)
+{
+}
+
+int WorkerRemoveEvent::workerId() const
+{
+ return m_id;
+}
+
+WorkerErrorEvent::WorkerErrorEvent(const QQmlError &error)
+: QEvent((QEvent::Type)WorkerError), m_error(error)
+{
+}
+
+QQmlError WorkerErrorEvent::error() const
+{
+ return m_error;
+}
+
+QQuickWorkerScriptEngine::QQuickWorkerScriptEngine(QQmlEngine *parent)
+: QThread(parent), d(new QQuickWorkerScriptEnginePrivate(parent))
+{
+ d->m_lock.lock();
+ connect(d, SIGNAL(stopThread()), this, SLOT(quit()), Qt::DirectConnection);
+ start(QThread::LowestPriority);
+ d->m_wait.wait(&d->m_lock);
+ d->moveToThread(this);
+ d->m_lock.unlock();
+}
+
+QQuickWorkerScriptEngine::~QQuickWorkerScriptEngine()
+{
+ d->m_lock.lock();
+ QCoreApplication::postEvent(d, new QEvent((QEvent::Type)QQuickWorkerScriptEnginePrivate::WorkerDestroyEvent));
+ d->m_lock.unlock();
+
+ //We have to force to cleanup the main thread's event queue here
+ //to make sure the main GUI release all pending locks/wait conditions which
+ //some worker script/agent are waiting for (QQmlListModelWorkerAgent::sync() for example).
+ while (!isFinished()) {
+ // We can't simply wait here, because the worker thread will not terminate
+ // until the main thread processes the last data event it generates
+ QCoreApplication::processEvents();
+ yieldCurrentThread();
+ }
+
+ d->deleteLater();
+}
+
+QQuickWorkerScriptEnginePrivate::WorkerScript::WorkerScript()
+: id(-1), initialized(false), owner(0)
+{
+}
+
+QQuickWorkerScriptEnginePrivate::WorkerScript::~WorkerScript()
+{
+ qPersistentDispose(object);
+}
+
+int QQuickWorkerScriptEngine::registerWorkerScript(QQuickWorkerScript *owner)
+{
+ typedef QQuickWorkerScriptEnginePrivate::WorkerScript WorkerScript;
+ WorkerScript *script = new WorkerScript;
+
+ script->id = d->m_nextId++;
+ script->owner = owner;
+
+ d->m_lock.lock();
+ d->workers.insert(script->id, script);
+ d->m_lock.unlock();
+
+ return script->id;
+}
+
+void QQuickWorkerScriptEngine::removeWorkerScript(int id)
+{
+ QQuickWorkerScriptEnginePrivate::WorkerScript* script = d->workers.value(id);
+ if (script) {
+ script->owner = 0;
+ QCoreApplication::postEvent(d, new WorkerRemoveEvent(id));
+ }
+}
+
+void QQuickWorkerScriptEngine::executeUrl(int id, const QUrl &url)
+{
+ QCoreApplication::postEvent(d, new WorkerLoadEvent(id, url));
+}
+
+void QQuickWorkerScriptEngine::sendMessage(int id, const QByteArray &data)
+{
+ QCoreApplication::postEvent(d, new WorkerDataEvent(id, data));
+}
+
+void QQuickWorkerScriptEngine::run()
+{
+ d->m_lock.lock();
+
+ d->workerEngine = new QQuickWorkerScriptEnginePrivate::WorkerEngine(d);
+ d->workerEngine->init();
+
+ d->m_wait.wakeAll();
+
+ d->m_lock.unlock();
+
+ exec();
+
+ qDeleteAll(d->workers);
+ d->workers.clear();
+
+ delete d->workerEngine; d->workerEngine = 0;
+}
+
+
+/*!
+ \qmltype WorkerScript
+ \instantiates QQuickWorkerScript
+ \ingroup qtquick-threading
+ \inqmlmodule QtQuick 2
+ \brief Enables the use of threads in a Qt Quick application
+
+ Use WorkerScript to run operations in a new thread.
+ This is useful for running operations in the background so
+ that the main GUI thread is not blocked.
+
+ Messages can be passed between the new thread and the parent thread
+ using \l sendMessage() and the \l {WorkerScript::onMessage}{onMessage()} handler.
+
+ An example:
+
+ \snippet qml/workerscript/workerscript.qml 0
+
+ The above worker script specifies a JavaScript file, "script.js", that handles
+ the operations to be performed in the new thread. Here is \c script.js:
+
+ \quotefile qml/workerscript/script.js
+
+ When the user clicks anywhere within the rectangle, \c sendMessage() is
+ called, triggering the \tt WorkerScript.onMessage() handler in
+ \tt script.js. This in turn sends a reply message that is then received
+ by the \tt onMessage() handler of \tt myWorker.
+
+
+ \section3 Restrictions
+
+ Since the \c WorkerScript.onMessage() function is run in a separate thread, the
+ JavaScript file is evaluated in a context separate from the main QML engine. This means
+ that unlike an ordinary JavaScript file that is imported into QML, the \c script.js
+ in the above example cannot access the properties, methods or other attributes
+ of the QML item, nor can it access any context properties set on the QML object
+ through QQmlContext.
+
+ Additionally, there are restrictions on the types of values that can be passed to and
+ from the worker script. See the sendMessage() documentation for details.
+
+ Worker script can not use \l {qtqml-javascript-imports.html}{.import} syntax.
+
+ \sa {declarative/threading/workerscript}{WorkerScript example},
+ {declarative/threading/threadedlistmodel}{Threaded ListModel example}
+*/
+QQuickWorkerScript::QQuickWorkerScript(QObject *parent)
+: QObject(parent), m_engine(0), m_scriptId(-1), m_componentComplete(true)
+{
+}
+
+QQuickWorkerScript::~QQuickWorkerScript()
+{
+ if (m_scriptId != -1) m_engine->removeWorkerScript(m_scriptId);
+}
+
+/*!
+ \qmlproperty url WorkerScript::source
+
+ This holds the url of the JavaScript file that implements the
+ \tt WorkerScript.onMessage() handler for threaded operations.
+*/
+QUrl QQuickWorkerScript::source() const
+{
+ return m_source;
+}
+
+void QQuickWorkerScript::setSource(const QUrl &source)
+{
+ if (m_source == source)
+ return;
+
+ m_source = source;
+
+ if (engine())
+ m_engine->executeUrl(m_scriptId, m_source);
+
+ emit sourceChanged();
+}
+
+/*!
+ \qmlmethod WorkerScript::sendMessage(jsobject message)
+
+ Sends the given \a message to a worker script handler in another
+ thread. The other worker script handler can receive this message
+ through the onMessage() handler.
+
+ The \c message object may only contain values of the following
+ types:
+
+ \list
+ \li boolean, number, string
+ \li JavaScript objects and arrays
+ \li ListModel objects (any other type of QObject* is not allowed)
+ \endlist
+
+ All objects and arrays are copied to the \c message. With the exception
+ of ListModel objects, any modifications by the other thread to an object
+ passed in \c message will not be reflected in the original object.
+*/
+void QQuickWorkerScript::sendMessage(QQmlV8Function *args)
+{
+ if (!engine()) {
+ qWarning("QQuickWorkerScript: Attempt to send message before WorkerScript establishment");
+ return;
+ }
+
+ v8::Handle<v8::Value> argument = v8::Undefined();
+ if (args->Length() != 0)
+ argument = (*args)[0];
+
+ m_engine->sendMessage(m_scriptId, QV8Worker::serialize(argument, args->engine()));
+}
+
+void QQuickWorkerScript::classBegin()
+{
+ m_componentComplete = false;
+}
+
+QQuickWorkerScriptEngine *QQuickWorkerScript::engine()
+{
+ if (m_engine) return m_engine;
+ if (m_componentComplete) {
+ QQmlEngine *engine = qmlEngine(this);
+ if (!engine) {
+ qWarning("QQuickWorkerScript: engine() called without qmlEngine() set");
+ return 0;
+ }
+
+ m_engine = QQmlEnginePrivate::get(engine)->getWorkerScriptEngine();
+ m_scriptId = m_engine->registerWorkerScript(this);
+
+ if (m_source.isValid())
+ m_engine->executeUrl(m_scriptId, m_source);
+
+ return m_engine;
+ }
+ return 0;
+}
+
+void QQuickWorkerScript::componentComplete()
+{
+ m_componentComplete = true;
+ engine(); // Get it started now.
+}
+
+/*!
+ \qmlsignal WorkerScript::onMessage(jsobject msg)
+
+ This handler is called when a message \a msg is received from a worker
+ script in another thread through a call to sendMessage().
+*/
+
+bool QQuickWorkerScript::event(QEvent *event)
+{
+ if (event->type() == (QEvent::Type)WorkerDataEvent::WorkerData) {
+ QQmlEngine *engine = qmlEngine(this);
+ if (engine) {
+ WorkerDataEvent *workerEvent = static_cast<WorkerDataEvent *>(event);
+ QV8Engine *v8engine = QQmlEnginePrivate::get(engine)->v8engine();
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(v8engine->context());
+ v8::Handle<v8::Value> value = QV8Worker::deserialize(workerEvent->data(), v8engine);
+ emit message(QQmlV8Handle::fromHandle(value));
+ }
+ return true;
+ } else if (event->type() == (QEvent::Type)WorkerErrorEvent::WorkerError) {
+ WorkerErrorEvent *workerEvent = static_cast<WorkerErrorEvent *>(event);
+ QQmlEnginePrivate::warning(qmlEngine(this), workerEvent->error());
+ return true;
+ } else {
+ return QObject::event(event);
+ }
+}
+
+QT_END_NAMESPACE
+
+#include <qquickworkerscript.moc>
+
diff --git a/src/qml/types/qquickworkerscript_p.h b/src/qml/types/qquickworkerscript_p.h
new file mode 100644
index 0000000000..1ab5208e45
--- /dev/null
+++ b/src/qml/types/qquickworkerscript_p.h
@@ -0,0 +1,126 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKWORKERSCRIPT_P_H
+#define QQUICKWORKERSCRIPT_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 "qqml.h"
+
+#include <QtQml/qqmlparserstatus.h>
+#include <QtCore/qthread.h>
+#include <QtQml/qjsvalue.h>
+#include <QtCore/qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+
+class QQuickWorkerScript;
+class QQuickWorkerScriptEnginePrivate;
+class QQuickWorkerScriptEngine : public QThread
+{
+Q_OBJECT
+public:
+ QQuickWorkerScriptEngine(QQmlEngine *parent = 0);
+ virtual ~QQuickWorkerScriptEngine();
+
+ int registerWorkerScript(QQuickWorkerScript *);
+ void removeWorkerScript(int);
+ void executeUrl(int, const QUrl &);
+ void sendMessage(int, const QByteArray &);
+
+protected:
+ virtual void run();
+
+private:
+ QQuickWorkerScriptEnginePrivate *d;
+};
+
+class QQmlV8Function;
+class QQmlV8Handle;
+class Q_AUTOTEST_EXPORT QQuickWorkerScript : public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
+
+ Q_INTERFACES(QQmlParserStatus)
+public:
+ QQuickWorkerScript(QObject *parent = 0);
+ virtual ~QQuickWorkerScript();
+
+ QUrl source() const;
+ void setSource(const QUrl &);
+
+public slots:
+ void sendMessage(QQmlV8Function*);
+
+signals:
+ void sourceChanged();
+ void message(const QQmlV8Handle &messageObject);
+
+protected:
+ virtual void classBegin();
+ virtual void componentComplete();
+ virtual bool event(QEvent *);
+
+private:
+ QQuickWorkerScriptEngine *engine();
+ QQuickWorkerScriptEngine *m_engine;
+ int m_scriptId;
+ QUrl m_source;
+ bool m_componentComplete;
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickWorkerScript)
+
+#endif // QQUICKWORKERSCRIPT_P_H
diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri
new file mode 100644
index 0000000000..22e62ea8de
--- /dev/null
+++ b/src/qml/types/types.pri
@@ -0,0 +1,26 @@
+SOURCES += \
+ $$PWD/qqmlbind.cpp \
+ $$PWD/qqmlconnections.cpp \
+ $$PWD/qqmldelegatemodel.cpp \
+ $$PWD/qqmllistmodel.cpp \
+ $$PWD/qqmllistmodelworkeragent.cpp \
+ $$PWD/qqmlmodelsmodule.cpp \
+ $$PWD/qqmlobjectmodel.cpp \
+ $$PWD/qqmltimer.cpp \
+ $$PWD/qquickpackage.cpp \
+ $$PWD/qquickworkerscript.cpp
+
+HEADERS += \
+ $$PWD/qqmlbind_p.h \
+ $$PWD/qqmlconnections_p.h \
+ $$PWD/qqmldelegatemodel_p.h \
+ $$PWD/qqmldelegatemodel_p_p.h \
+ $$PWD/qqmllistmodel_p.h \
+ $$PWD/qqmllistmodel_p_p.h \
+ $$PWD/qqmllistmodelworkeragent_p.h \
+ $$PWD/qqmlmodelsmodule_p.h \
+ $$PWD/qqmlobjectmodel_p.h \
+ $$PWD/qqmltimer_p.h \
+ $$PWD/qquickpackage_p.h \
+ $$PWD/qquickworkerscript_p.h
+