summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMartin Jones <martin.jones@nokia.com>2010-03-16 05:50:15 (GMT)
committerMartin Jones <martin.jones@nokia.com>2010-03-16 05:50:15 (GMT)
commit1c1465587ed116695171a73deffa1dc0729856d0 (patch)
tree9d6734313df1ef0fce7ca32b3dae39cbd9da915a /src
parenta32cd00668076674b6968b821d4b7377c95ddee9 (diff)
parentfcfc3a68a525c32d4550068d7cf47bb3a5816da6 (diff)
downloadQt-1c1465587ed116695171a73deffa1dc0729856d0.zip
Qt-1c1465587ed116695171a73deffa1dc0729856d0.tar.gz
Qt-1c1465587ed116695171a73deffa1dc0729856d0.tar.bz2
Merge branch '4.7' of scm.dev.nokia.troll.no:qt/qt-qml into 4.7
Diffstat (limited to 'src')
-rw-r--r--src/declarative/qml/qdeclarativeengine.cpp1
-rw-r--r--src/declarative/qml/qdeclarativeworkerscript.cpp647
-rw-r--r--src/declarative/qml/qdeclarativeworkerscript_p.h41
-rw-r--r--src/declarative/util/qdeclarativelistmodel.cpp954
-rw-r--r--src/declarative/util/qdeclarativelistmodel_p.h21
-rw-r--r--src/declarative/util/qdeclarativelistmodel_p_p.h199
-rw-r--r--src/declarative/util/qdeclarativelistmodelworkeragent.cpp245
-rw-r--r--src/declarative/util/qdeclarativelistmodelworkeragent_p.h155
-rw-r--r--src/declarative/util/util.pri7
9 files changed, 1290 insertions, 980 deletions
diff --git a/src/declarative/qml/qdeclarativeengine.cpp b/src/declarative/qml/qdeclarativeengine.cpp
index a3f8250..e84e267 100644
--- a/src/declarative/qml/qdeclarativeengine.cpp
+++ b/src/declarative/qml/qdeclarativeengine.cpp
@@ -149,7 +149,6 @@ void QDeclarativeEnginePrivate::defineModule()
qmlRegisterType<QDeclarativeComponent>("Qt",4,6,"Component");
qmlRegisterType<QObject>("Qt",4,6,"QtObject");
qmlRegisterType<QDeclarativeWorkerScript>("Qt",4,6,"WorkerScript");
- qmlRegisterType<QDeclarativeWorkerListModel>("Qt",4,6,"WorkerListModel");
qmlRegisterType<QDeclarativeBinding>();
}
diff --git a/src/declarative/qml/qdeclarativeworkerscript.cpp b/src/declarative/qml/qdeclarativeworkerscript.cpp
index 784353a..10c0b54 100644
--- a/src/declarative/qml/qdeclarativeworkerscript.cpp
+++ b/src/declarative/qml/qdeclarativeworkerscript.cpp
@@ -40,7 +40,8 @@
****************************************************************************/
#include "qdeclarativeworkerscript_p.h"
-
+#include "qdeclarativelistmodel_p.h"
+#include "qdeclarativelistmodelworkeragent_p.h"
#include "qdeclarativeengine_p.h"
#include <QtCore/qcoreevent.h>
@@ -104,6 +105,7 @@ private:
class QDeclarativeWorkerScriptEnginePrivate : public QObject
{
+ Q_OBJECT
public:
QDeclarativeWorkerScriptEnginePrivate(QDeclarativeEngine *eng);
@@ -165,87 +167,6 @@ private:
void processLoad(int, const QUrl &);
};
-// Currently this will leak as no-one releases it in the worker thread
-class QDeclarativeWorkerListModelAgent : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(int count READ count)
-
-public:
- QDeclarativeWorkerListModelAgent(QDeclarativeWorkerListModel *);
- ~QDeclarativeWorkerListModelAgent();
-
- void addref();
- void release();
-
- int count() const;
-
- Q_INVOKABLE void clear();
- Q_INVOKABLE void remove(int index);
- Q_INVOKABLE void append(const QScriptValue &);
- Q_INVOKABLE void insert(int index, const QScriptValue&);
- Q_INVOKABLE QScriptValue get(int index) const;
- Q_INVOKABLE void set(int index, const QScriptValue &);
- Q_INVOKABLE void sync();
-
- struct VariantRef
- {
- VariantRef() : a(0) {}
- VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); }
- VariantRef(QDeclarativeWorkerListModelAgent *_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;
- }
-
- QDeclarativeWorkerListModelAgent *a;
- };
-protected:
- virtual bool event(QEvent *);
-
-private:
- friend class QDeclarativeWorkerScriptEnginePrivate;
- friend class QDeclarativeWorkerListModel;
- QScriptEngine *m_engine;
-
- struct Change {
- enum { Inserted, Removed, Moved, Changed } type;
- int index; // Inserted/Removed/Moved/Changed
- int count; // Inserted/Removed/Moved/Changed
- int to; // Moved
- };
-
- struct Data {
- QHash<int, QString> roles;
- QHash<QString, int> strings;
- QList<QHash<int, QVariant> > values;
- QList<Change> changes;
-
- void clearChange();
- void insertChange(int index, int count);
- void removeChange(int index, int count);
- void changedChange(int index, int count);
- };
- Data data;
-
- struct Sync : public QEvent {
- Sync() : QEvent(QEvent::User) {}
- Data data;
- };
-
- QAtomicInt m_ref;
- QDeclarativeWorkerListModel *m_model;
-};
-
-QT_END_NAMESPACE
-
-Q_DECLARE_METATYPE(QDeclarativeWorkerListModelAgent::VariantRef);
-
-QT_BEGIN_NAMESPACE
-
QDeclarativeWorkerScriptEnginePrivate::QDeclarativeWorkerScriptEnginePrivate(QDeclarativeEngine *engine)
: workerEngine(0), qmlengine(engine), m_nextId(0)
{
@@ -390,10 +311,15 @@ QVariant QDeclarativeWorkerScriptEnginePrivate::scriptValueToVariant(const QScri
return QVariant(list);
} else if (value.isQObject()) {
- QDeclarativeWorkerListModel *lm = qobject_cast<QDeclarativeWorkerListModel *>(value.toQObject());
+ QDeclarativeListModel *lm = qobject_cast<QDeclarativeListModel *>(value.toQObject());
if (lm) {
- QDeclarativeWorkerListModelAgent::VariantRef v(lm->agent());
- return qVariantFromValue(v);
+ QDeclarativeListModelWorkerAgent *agent = lm->agent();
+ if (agent) {
+ QDeclarativeListModelWorkerAgent::VariantRef v(agent);
+ return qVariantFromValue(v);
+ } else {
+ return QVariant();
+ }
} else {
// No other QObject's are allowed to be sent
return QVariant();
@@ -423,11 +349,11 @@ QScriptValue QDeclarativeWorkerScriptEnginePrivate::variantToScriptValue(const Q
return QScriptValue(value.toString());
} else if (value.userType() == QMetaType::QReal) {
return QScriptValue(value.toReal());
- } else if (value.userType() == qMetaTypeId<QDeclarativeWorkerListModelAgent::VariantRef>()) {
- QDeclarativeWorkerListModelAgent::VariantRef vr = qvariant_cast<QDeclarativeWorkerListModelAgent::VariantRef>(value);
- if (vr.a->m_engine == 0)
- vr.a->m_engine = engine;
- else if (vr.a->m_engine != engine)
+ } else if (value.userType() == qMetaTypeId<QDeclarativeListModelWorkerAgent::VariantRef>()) {
+ QDeclarativeListModelWorkerAgent::VariantRef vr = qvariant_cast<QDeclarativeListModelWorkerAgent::VariantRef>(value);
+ if (vr.a->scriptEngine() == 0)
+ vr.a->setScriptEngine(engine);
+ else if (vr.a->scriptEngine() != engine)
return engine->nullValue();
QScriptValue o = engine->newQObject(vr.a);
o.setData(engine->newVariant(value)); // Keeps the agent ref so that it is cleaned up on gc
@@ -619,8 +545,6 @@ void QDeclarativeWorkerScriptEngine::run()
called, triggering the \tt WorkerScript.onMessage() handler in
\tt source.js. This in turn sends a reply message that is then received
by the \tt onMessage() handler of \tt myWorker.
-
- \sa WorkerListModel
*/
QDeclarativeWorkerScript::QDeclarativeWorkerScript(QObject *parent)
: QObject(parent), m_engine(0), m_scriptId(-1)
@@ -714,544 +638,7 @@ bool QDeclarativeWorkerScript::event(QEvent *event)
}
}
-void QDeclarativeWorkerListModelAgent::Data::clearChange()
-{
- changes.clear();
-}
-
-void QDeclarativeWorkerListModelAgent::Data::insertChange(int index, int count)
-{
- Change c = { Change::Inserted, index, count, 0 };
- changes << c;
-}
-
-void QDeclarativeWorkerListModelAgent::Data::removeChange(int index, int count)
-{
- Change c = { Change::Removed, index, count, 0 };
- changes << c;
-}
-
-void QDeclarativeWorkerListModelAgent::Data::changedChange(int index, int count)
-{
- Change c = { Change::Changed, index, count, 0 };
- changes << c;
-}
-
-QDeclarativeWorkerListModelAgent::QDeclarativeWorkerListModelAgent(QDeclarativeWorkerListModel *m)
-: m_engine(0), m_ref(1), m_model(m)
-{
- data.roles = m_model->m_roles;
- data.strings = m_model->m_strings;
- data.values = m_model->m_values;
-}
-
-QDeclarativeWorkerListModelAgent::~QDeclarativeWorkerListModelAgent()
-{
-}
-
-void QDeclarativeWorkerListModelAgent::addref()
-{
- m_ref.ref();
-}
-
-void QDeclarativeWorkerListModelAgent::release()
-{
- bool del = !m_ref.deref();
-
- if (del)
- delete this;
-}
-
-int QDeclarativeWorkerListModelAgent::count() const
-{
- return data.values.count();
-}
-
-void QDeclarativeWorkerListModelAgent::clear()
-{
- data.clearChange();
- data.removeChange(0, data.values.count());
- data.values.clear();
-}
-
-void QDeclarativeWorkerListModelAgent::remove(int index)
-{
- if (data.values.count() <= index)
- return;
-
- data.values.removeAt(index);
- data.removeChange(index, 1);
-}
-
-void QDeclarativeWorkerListModelAgent::append(const QScriptValue &value)
-{
- QHash<int, QVariant> row;
-
- QScriptValueIterator it(value);
- while (it.hasNext()) {
- it.next();
- QString name = it.name();
- QVariant v = it.value().toVariant();
-
- QHash<QString, int>::Iterator iter = data.strings.find(name);
- if (iter == data.strings.end()) {
- int role = data.roles.count();
- data.roles.insert(role, name);
- iter = data.strings.insert(name, role);
- }
- row.insert(*iter, v);
- }
-
- data.values.append(row);
- data.insertChange(data.values.count() - 1, 1);
-}
-
-void QDeclarativeWorkerListModelAgent::insert(int index, const QScriptValue &value)
-{
- if (index > data.values.count())
- return;
-
- QHash<int, QVariant> row;
-
- QScriptValueIterator it(value);
- while (it.hasNext()) {
- it.next();
- QString name = it.name();
- QVariant v = it.value().toVariant();
-
- QHash<QString, int>::Iterator iter = data.strings.find(name);
- if (iter == data.strings.end()) {
- int role = data.roles.count();
- data.roles.insert(role, name);
- iter = data.strings.insert(name, role);
- }
- row.insert(*iter, v);
- }
-
- data.values.insert(index, row);
- data.insertChange(index, 1);
-}
-
-void QDeclarativeWorkerListModelAgent::set(int index, const QScriptValue &value)
-{
- if (data.values.count() <= index)
- return;
-
- QHash<int, QVariant> row;
-
- QScriptValueIterator it(value);
- while (it.hasNext()) {
- it.next();
- QString name = it.name();
- QVariant v = it.value().toVariant();
-
- QHash<QString, int>::Iterator iter = data.strings.find(name);
- if (iter == data.strings.end()) {
- int role = data.roles.count();
- data.roles.insert(role, name);
- iter = data.strings.insert(name, role);
- }
- row.insert(*iter, v);
- }
-
- if (data.values.at(index) != row) {
- data.values[index] = row;
- data.changedChange(index, 1);
- }
-}
-
-QScriptValue QDeclarativeWorkerListModelAgent::get(int index) const
-{
- if (data.values.count() <= index)
- return m_engine->undefinedValue();
-
- QScriptValue rv = m_engine->newObject();
-
- QHash<int, QVariant> row = data.values.at(index);
- for (QHash<int, QVariant>::ConstIterator iter = row.begin(); iter != row.end(); ++iter)
- rv.setProperty(data.roles.value(iter.key()), qScriptValueFromValue(m_engine, iter.value()));
-
- return rv;
-}
-
-void QDeclarativeWorkerListModelAgent::sync()
-{
- Sync *s = new Sync;
- s->data = data;
- data.changes.clear();
- QCoreApplication::postEvent(this, s);
-}
-
-bool QDeclarativeWorkerListModelAgent::event(QEvent *e)
-{
- if (e->type() == QEvent::User) {
- Sync *s = static_cast<Sync *>(e);
-
- const QList<Change> &changes = s->data.changes;
-
- if (m_model) {
- bool cc = m_model->m_values.count() != s->data.values.count();
-
- m_model->m_roles = s->data.roles;
- m_model->m_strings = s->data.strings;
- m_model->m_values = s->data.values;
-
- for (int ii = 0; ii < changes.count(); ++ii) {
- const Change &change = changes.at(ii);
- switch (change.type) {
- case Change::Inserted:
- emit m_model->itemsInserted(change.index, change.count);
- break;
- case Change::Removed:
- emit m_model->itemsRemoved(change.index, change.count);
- break;
- case Change::Moved:
- emit m_model->itemsMoved(change.index, change.to, change.count);
- break;
- case Change::Changed:
- emit m_model->itemsMoved(change.index, change.to, change.count);
- break;
- }
- }
-
- if (cc)
- emit m_model->countChanged();
- }
- }
-
- return QObject::event(e);
-}
-
-/*!
- \qmlclass WorkerListModel QDeclarativeWorkerListModel
- \brief The WorkerListModel element provides a threaded list model.
-
- Use WorkerListModel together with WorkerScript to define a list model
- that is controlled by a separate thread. This is useful if list modification
- operations are synchronous and take some time: using WorkerListModel
- moves these operations to a different thread and avoids blocking of the
- main GUI thread.
-
- The thread that creates the WorkerListModel can modify the model for any
- initial set-up requirements. However, once the model has been modified by
- the associated WorkerScript, the model can only be modified by that worker
- script and becomes read-only to all other threads.
-
- Here is an example application that uses WorkerScript to append the
- current time to a WorkerListModel:
-
- \snippet examples/declarative/workerlistmodel/timedisplay.qml 0
-
- The included file, \tt dataloader.js, looks like this:
-
- \snippet examples/declarative/workerlistmodel/dataloader.js 0
-
- The application's \tt Timer object periodically sends a message to the
- worker script by calling \tt WorkerScript::sendMessage(). When this message
- is received, \tt WorkerScript.onMessage() is invoked in
- \tt dataloader.js, which appends the current time to the worker list
- model.
-
- Note that unlike ListModel, WorkerListModel does not have \tt move() and
- \tt setProperty() methods.
-
- \sa WorkerScript, ListModel
-*/
-QDeclarativeWorkerListModel::QDeclarativeWorkerListModel(QObject *parent)
-: QListModelInterface(parent), m_agent(0)
-{
-}
-
-QDeclarativeWorkerListModel::~QDeclarativeWorkerListModel()
-{
- if (m_agent) {
- m_agent->m_model = 0;
- m_agent->release();
- }
-}
-
-/*!
- \qmlmethod WorkerListModel::clear()
-
- Deletes all content from the model. The properties are cleared such that
- different properties may be set on subsequent additions.
-
- \sa append() remove()
-*/
-void QDeclarativeWorkerListModel::clear()
-{
- if (m_agent) {
- qmlInfo(this) << "List can only be modified from a WorkerScript";
- return;
- }
-
- int count = m_values.count();
- m_values.clear();
- if (count) {
- emit itemsRemoved(0, count);
- emit countChanged();
- }
-}
-
-/*!
- \qmlmethod WorkerListModel::remove(int index)
-
- Deletes the content at \a index from the model.
-
- \sa clear()
-*/
-void QDeclarativeWorkerListModel::remove(int index)
-{
- if (m_agent) {
- qmlInfo(this) << "List can only be modified from a WorkerScript";
- return;
- }
-
- if (m_values.count() <= index)
- return;
-
- m_values.removeAt(index);
- emit itemsRemoved(index, 1);
- emit countChanged();
-}
-
-/*!
- \qmlmethod WorkerListModel::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 QDeclarativeWorkerListModel::append(const QScriptValue &value)
-{
- if (m_agent) {
- qmlInfo(this) << "List can only be modified from a WorkerScript";
- return;
- }
-
- QHash<int, QVariant> data;
-
- QScriptValueIterator it(value);
- while (it.hasNext()) {
- it.next();
- QString name = it.name();
- QVariant v = it.value().toVariant();
-
- QHash<QString, int>::Iterator iter = m_strings.find(name);
- if (iter == m_strings.end()) {
- int role = m_roles.count();
- m_roles.insert(role, name);
- iter = m_strings.insert(name, role);
- }
- data.insert(*iter, v);
- }
-
- m_values.append(data);
-
- emit itemsInserted(m_values.count() - 1, 1);
- emit countChanged();
-}
-
-/*!
- \qmlmethod WorkerListModel::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 QDeclarativeWorkerListModel::insert(int index, const QScriptValue &value)
-{
- if (m_agent) {
- qmlInfo(this) << "List can only be modified from a WorkerScript";
- return;
- }
-
- if (index > m_values.count())
- return;
-
- QHash<int, QVariant> data;
-
- QScriptValueIterator it(value);
- while (it.hasNext()) {
- it.next();
- QString name = it.name();
- QVariant v = it.value().toVariant();
-
- QHash<QString, int>::Iterator iter = m_strings.find(name);
- if (iter == m_strings.end()) {
- int role = m_roles.count();
- m_roles.insert(role, name);
- iter = m_strings.insert(name, role);
- }
- data.insert(*iter, v);
- }
-
- m_values.insert(index, data);
- emit itemsInserted(index, 1);
- emit countChanged();
-}
-
-/*!
- \qmlmethod object ListModel::get(int index)
-
- Returns the item at \a index in the list model.
-
- \code
- FruitModel.append({"cost": 5.95, "name":"Jackfruit"})
- FruitModel.get(0).cost
- \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
-
- \sa append()
-*/
-QScriptValue QDeclarativeWorkerListModel::get(int index) const
-{
- QDeclarativeEngine *engine = qmlEngine(this);
- if (!engine || m_values.count() <= index)
- return QScriptValue();
-
- QScriptEngine *scriptEngine = QDeclarativeEnginePrivate::getScriptEngine(engine);
- QScriptValue rv = scriptEngine->newObject();
-
- QHash<int, QVariant> data = m_values.at(index);
- for (QHash<int, QVariant>::ConstIterator iter = data.begin(); iter != data.end(); ++iter)
- rv.setProperty(m_roles.value(iter.key()), qScriptValueFromValue(scriptEngine, iter.value()));
-
- return rv;
-}
-
-/*!
- \qmlmethod WorkerListModel::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 valuemap
- are left unchanged.
-
- \code
- FruitModel.set(3, {"cost": 5.95, "name":"Pizza"})
- \endcode
-
- The \a index must be an element in the list.
-
- \sa append()
-*/
-void QDeclarativeWorkerListModel::set(int index, const QScriptValue &value)
-{
- if (m_agent) {
- qmlInfo(this) << "List can only be modified from a WorkerScript";
- return;
- }
-
- if (m_values.count() <= index)
- return;
-
- QHash<int, QVariant> data;
-
- QScriptValueIterator it(value);
- while (it.hasNext()) {
- it.next();
- QString name = it.name();
- QVariant v = it.value().toVariant();
-
- QHash<QString, int>::Iterator iter = m_strings.find(name);
- if (iter == m_strings.end()) {
- int role = m_roles.count();
- m_roles.insert(role, name);
- iter = m_strings.insert(name, role);
- }
- data.insert(*iter, v);
- }
-
- if (m_values.at(index) != data) {
- m_values[index] = data;
- emit itemsChanged(index, 1, m_roles.keys());
- }
-}
-
-/*!
- \qmlmethod WorkerListModel::sync()
-
- Writes any unsaved changes to the list model. This must be called after
- changes have been made to the list model in the worker script.
-
- Note that this method can only be called from the associated worker script.
-*/
-void QDeclarativeWorkerListModel::sync()
-{
- // This is really a dummy method to make it look like sync() exists in
- // WorkerListModel (and not QDeclarativeWorkerListModelAgent) and to let
- // us document sync().
- qmlInfo(this) << "sync() can only be called from a WorkerScript";
-}
-
-QDeclarativeWorkerListModelAgent *QDeclarativeWorkerListModel::agent()
-{
- if (!m_agent)
- m_agent = new QDeclarativeWorkerListModelAgent(this);
-
- return m_agent;
-}
-
-QList<int> QDeclarativeWorkerListModel::roles() const
-{
- return m_roles.keys();
-}
-
-QString QDeclarativeWorkerListModel::toString(int role) const
-{
- return m_roles.value(role);
-}
-
-/*!
- \qmlproperty int ListModel::count
- The number of data entries in the model.
-*/
-int QDeclarativeWorkerListModel::count() const
-{
- return m_values.count();
-}
-
-QHash<int,QVariant> QDeclarativeWorkerListModel::data(int index, const QList<int> &) const
-{
- if (m_values.count() <= index)
- return QHash<int, QVariant>();
- else
- return m_values.at(index);
-}
-
-QVariant QDeclarativeWorkerListModel::data(int index, int role) const
-{
- if (m_values.count() <= index)
- return QVariant();
- else
- return m_values.at(index).value(role);
-}
-
QT_END_NAMESPACE
-#include "qdeclarativeworkerscript.moc"
+#include <qdeclarativeworkerscript.moc>
diff --git a/src/declarative/qml/qdeclarativeworkerscript_p.h b/src/declarative/qml/qdeclarativeworkerscript_p.h
index 912eac9..6cce799 100644
--- a/src/declarative/qml/qdeclarativeworkerscript_p.h
+++ b/src/declarative/qml/qdeclarativeworkerscript_p.h
@@ -55,7 +55,6 @@
#include "qdeclarative.h"
#include "qdeclarativeparserstatus.h"
-#include <private/qlistmodelinterface_p.h>
#include <QtCore/qthread.h>
#include <QtScript/qscriptvalue.h>
@@ -118,49 +117,9 @@ private:
QUrl m_source;
};
-class QDeclarativeWorkerListModelAgent;
-class Q_DECLARATIVE_EXPORT QDeclarativeWorkerListModel : public QListModelInterface
-{
- Q_OBJECT
- Q_PROPERTY(int count READ count NOTIFY countChanged)
-
-public:
- QDeclarativeWorkerListModel(QObject * = 0);
- virtual ~QDeclarativeWorkerListModel();
-
- Q_INVOKABLE void clear();
- Q_INVOKABLE void remove(int index);
- Q_INVOKABLE void append(const QScriptValue &);
- Q_INVOKABLE void insert(int index, const QScriptValue&);
- Q_INVOKABLE QScriptValue get(int index) const;
- Q_INVOKABLE void set(int index, const QScriptValue &);
- Q_INVOKABLE void sync();
-
- QDeclarativeWorkerListModelAgent *agent();
-
- virtual QList<int> roles() const;
- virtual QString toString(int role) const;
- virtual int count() const;
- virtual QHash<int,QVariant> data(int index, const QList<int> &roles = (QList<int>())) const;
- virtual QVariant data(int index, int role) const;
-
-Q_SIGNALS:
- void countChanged();
-
-private:
- friend class QDeclarativeWorkerListModelAgent;
-
- QHash<int, QString> m_roles;
- QHash<QString, int> m_strings;
- QList<QHash<int, QVariant> > m_values;
-
- QDeclarativeWorkerListModelAgent *m_agent;
-};
-
QT_END_NAMESPACE
QML_DECLARE_TYPE(QDeclarativeWorkerScript);
-QML_DECLARE_TYPE(QDeclarativeWorkerListModel);
QT_END_HEADER
diff --git a/src/declarative/util/qdeclarativelistmodel.cpp b/src/declarative/util/qdeclarativelistmodel.cpp
index e3f26d7..d612ba8 100644
--- a/src/declarative/util/qdeclarativelistmodel.cpp
+++ b/src/declarative/util/qdeclarativelistmodel.cpp
@@ -39,8 +39,8 @@
**
****************************************************************************/
-#include "qdeclarativelistmodel_p.h"
-
+#include "qdeclarativelistmodel_p_p.h"
+#include "qdeclarativelistmodelworkeragent_p.h"
#include "qdeclarativeopenmetaobject_p.h"
#include <qdeclarativecustomparser_p.h>
@@ -66,8 +66,6 @@ QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListM
return (QDeclarativeListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData));
}
-static void dump(ModelNode *node, int ind);
-
/*!
\qmlclass ListModel QDeclarativeListModel
\since 4.7
@@ -193,253 +191,146 @@ static void dump(ModelNode *node, int ind);
except by first clearing the model - whatever properties are first added are then the
only permitted properties in the model.
- \sa {qmlmodels}{Data Models}
-*/
-class ModelObject : public QObject
-{
- Q_OBJECT
-public:
- ModelObject();
+ \section2 Using threaded list models with WorkerScript
- void setValue(const QByteArray &name, const QVariant &val)
- {
- _mo->setValue(name, val);
- }
+ 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.
-private:
- QDeclarativeOpenMetaObject *_mo;
-};
+ Here is an example that uses WorkerScript to periodically append the
+ current time to a list model:
-struct ModelNode
-{
- ModelNode();
- ~ModelNode();
+ \snippet examples/declarative/listmodel-threaded/timedisplay.qml 0
- QList<QVariant> values;
- QHash<QString, ModelNode *> properties;
+ The included file, \tt dataloader.js, looks like this:
- QDeclarativeListModel *model(const QDeclarativeListModel *parent) {
- if (!modelCache) {
- modelCache = new QDeclarativeListModel;
- QDeclarativeEngine::setContextForObject(modelCache,QDeclarativeEngine::contextForObject(parent));
+ \snippet examples/declarative/listmodel-threaded/dataloader.js 0
- modelCache->_root = this;
- }
- return modelCache;
- }
+ The application's \tt Timer object periodically sends a message to the
+ worker script by calling \tt WorkerScript::sendMessage(). When this message
+ is received, \tt WorkerScript.onMessage() is invoked in
+ \tt dataloader.js, which appends the current time to the list model.
- ModelObject *object(const QDeclarativeListModel *parent) {
- if (!objectCache) {
- objectCache = new ModelObject();
- QHash<QString, ModelNode *>::iterator it;
- for (it = properties.begin(); it != properties.end(); ++it) {
- objectCache->setValue(it.key().toUtf8(), parent->valueForNode(*it));
- }
- }
- return objectCache;
- }
-
- void setObjectValue(const QScriptValue& valuemap);
- void setListValue(const QScriptValue& valuelist);
+ Note the call to sync() from the \c WorkerScript.onMessage() handler.
+ Without this call, the changes made to the list are not reflected in the
+ list model in the main thread.
- void setProperty(const QString& prop, const QVariant& val) {
- QHash<QString, ModelNode *>::const_iterator it = properties.find(prop);
- if (it != properties.end()) {
- (*it)->values[0] = val;
- } else {
- ModelNode *n = new ModelNode;
- n->values << val;
- properties.insert(prop,n);
- }
- if (objectCache)
- objectCache->setValue(prop.toUtf8(), val);
- }
+ \section3 Limitations
- QDeclarativeListModel *modelCache;
- ModelObject *objectCache;
- bool isArray;
-};
+ If a list model is to be accessed from a WorkerScript, it \bold cannot
+ contain nested list data. So, the following model cannot be used from a WorkerScript
+ because of the list contained in the "attributes" property:
-QT_END_NAMESPACE
-
-Q_DECLARE_METATYPE(ModelNode *)
-
-QT_BEGIN_NAMESPACE
-
-void ModelNode::setObjectValue(const QScriptValue& valuemap) {
- QScriptValueIterator it(valuemap);
- while (it.hasNext()) {
- it.next();
- ModelNode *value = new ModelNode;
- QScriptValue v = it.value();
- if (v.isArray()) {
- value->isArray = true;
- value->setListValue(v);
- } else {
- value->values << v.toVariant();
+ \code
+ ListModel {
+ id: fruitModel
+ ListElement {
+ name: "Apple"
+ cost: 2.45
+ attributes: [
+ ListElement { description: "Core" },
+ ListElement { description: "Deciduous" }
+ ]
}
- properties.insert(it.name(),value);
}
-}
+ \endcode
-void ModelNode::setListValue(const QScriptValue& valuelist) {
- QScriptValueIterator it(valuelist);
- values.clear();
- while (it.hasNext()) {
- it.next();
- ModelNode *value = new ModelNode;
- QScriptValue v = it.value();
- if (v.isArray()) {
- value->isArray = true;
- value->setListValue(v);
- } else if (v.isObject()) {
- value->setObjectValue(v);
- } else {
- value->values << v.toVariant();
- }
- values.append(qVariantFromValue(value));
+ In addition, the WorkerScript cannot add any nested list data to the model.
- }
-}
+ \sa {qmlmodels}{Data Models}, WorkerScript
+*/
-ModelObject::ModelObject()
-: _mo(new QDeclarativeOpenMetaObject(this))
+QDeclarativeListModel::QDeclarativeListModel(QObject *parent)
+: QListModelInterface(parent), m_agent(0), m_nested(new NestedListModel(this)), m_flat(0), m_isWorkerCopy(false)
{
}
-QDeclarativeListModel::QDeclarativeListModel(QObject *parent)
-: QListModelInterface(parent), _rolesOk(false), _root(0)
+QDeclarativeListModel::QDeclarativeListModel(bool workerCopy, QObject *parent)
+: QListModelInterface(parent), m_agent(0), m_nested(0), m_flat(0), m_isWorkerCopy(workerCopy)
{
+ if (workerCopy)
+ m_flat = new FlatListModel(this);
+ else
+ m_nested = new NestedListModel(this);
}
QDeclarativeListModel::~QDeclarativeListModel()
{
- delete _root;
+ delete m_nested;
+ delete m_flat;
}
-void QDeclarativeListModel::checkRoles() const
+bool QDeclarativeListModel::flatten()
{
- if (_rolesOk || !_root)
- return;
+ if (m_flat)
+ return true;
- for (int ii = 0; ii < _root->values.count(); ++ii) {
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(ii));
- if (node) {
- foreach (const QString &role, node->properties.keys())
- addRole(role);
- }
+ QList<int> roles = m_nested->roles();
+
+ QList<QHash<int, QVariant> > values;
+ bool hasNested = false;
+ for (int i=0; i<m_nested->count(); i++) {
+ values.append(m_nested->data(i, roles, &hasNested));
+ if (hasNested)
+ return false;
}
- _rolesOk = true;
+ FlatListModel *flat = new FlatListModel(this);
+ flat->m_values = values;
+
+ for (int i=0; i<roles.count(); i++) {
+ QString s = m_nested->toString(roles[i]);
+ flat->m_roles.insert(roles[i], s);
+ flat->m_strings.insert(s, roles[i]);
+ }
+
+ m_flat = flat;
+ delete m_nested;
+ m_nested = 0;
+ return true;
}
-void QDeclarativeListModel::addRole(const QString &role) const
+QDeclarativeListModelWorkerAgent *QDeclarativeListModel::agent()
{
- if (!roleStrings.contains(role))
- roleStrings << role;
+ if (m_agent)
+ return m_agent;
+
+ if (!flatten()) {
+ qmlInfo(this) << "List contains nested list values and cannot be used from a worker script";
+ return 0;
+ }
+
+ m_agent = new QDeclarativeListModelWorkerAgent(this);
+ return m_agent;
}
QList<int> QDeclarativeListModel::roles() const
{
- checkRoles();
- QList<int> rv;
- for (int ii = 0; ii < roleStrings.count(); ++ii)
- rv << ii;
- return rv;
+ return m_flat ? m_flat->roles() : m_nested->roles();
}
QString QDeclarativeListModel::toString(int role) const
{
- checkRoles();
- if (role < roleStrings.count())
- return roleStrings.at(role);
- else
- return QString();
-}
-
-QVariant QDeclarativeListModel::valueForNode(ModelNode *node) const
-{
- QObject *rv = 0;
-
- if (node->isArray) {
- // List
- rv = node->model(this);
- } else {
- if (!node->properties.isEmpty()) {
- // Object
- rv = node->object(this);
- } else if (node->values.count() == 0) {
- // Invalid
- return QVariant();
- } else if (node->values.count() == 1) {
- // Value
- QVariant &var = node->values[0];
- ModelNode *valueNode = qvariant_cast<ModelNode *>(var);
- if (valueNode) {
- if (!valueNode->properties.isEmpty())
- rv = valueNode->object(this);
- else
- rv = valueNode->model(this);
- } else {
- return var;
- }
- }
- }
-
- if (rv)
- return QVariant::fromValue(rv);
- else
- return QVariant();
+ return m_flat ? m_flat->toString(role) : m_nested->toString(role);
}
QHash<int,QVariant> QDeclarativeListModel::data(int index, const QList<int> &roles) const
{
- checkRoles();
- QHash<int, QVariant> rv;
if (index >= count() || index < 0)
- return rv;
-
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
- if (!node)
- return rv;
-
- for (int ii = 0; ii < roles.count(); ++ii) {
- const QString &roleString = roleStrings.at(roles.at(ii));
+ return QHash<int, QVariant>();
- QHash<QString, ModelNode *>::ConstIterator iter =
- node->properties.find(roleString);
- if (iter != node->properties.end()) {
- ModelNode *row = *iter;
- rv.insert(roles.at(ii), valueForNode(row));
- }
- }
-
- return rv;
+ return m_flat ? m_flat->data(index, roles) : m_nested->data(index, roles);
}
QVariant QDeclarativeListModel::data(int index, int role) const
{
- checkRoles();
- QVariant rv;
if (index >= count() || index < 0)
- return rv;
-
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
- if (!node)
- return rv;
-
- const QString &roleString = roleStrings.at(role);
-
- QHash<QString, ModelNode *>::ConstIterator iter =
- node->properties.find(roleString);
- if (iter != node->properties.end()) {
- ModelNode *row = *iter;
- rv = valueForNode(row);
- }
+ return QVariant();
- return rv;
+ return m_flat ? m_flat->data(index, role) : m_nested->data(index, role);
}
/*!
@@ -448,8 +339,7 @@ QVariant QDeclarativeListModel::data(int index, int role) const
*/
int QDeclarativeListModel::count() const
{
- if (!_root) return 0;
- return _root->values.count();
+ return m_flat ? m_flat->count() : m_nested->count();
}
/*!
@@ -463,12 +353,15 @@ int QDeclarativeListModel::count() const
void QDeclarativeListModel::clear()
{
int cleared = count();
- _rolesOk = false;
- delete _root;
- _root = 0;
- roleStrings.clear();
- emit itemsRemoved(0,cleared);
- emit countChanged(0);
+ if (m_flat)
+ m_flat->clear();
+ else
+ m_nested->clear();
+
+ if (!m_isWorkerCopy) {
+ emit itemsRemoved(0, cleared);
+ emit countChanged(0);
+ }
}
/*!
@@ -480,17 +373,18 @@ void QDeclarativeListModel::clear()
*/
void QDeclarativeListModel::remove(int index)
{
- if (!_root || index < 0 || index >= _root->values.count()) {
+ if (index < 0 || index >= count()) {
qmlInfo(this) << tr("remove: index %1 out of range").arg(index);
return;
}
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
- _root->values.removeAt(index);
- if (node)
- delete node;
- emit itemsRemoved(index,1);
- emit countChanged(_root->values.count());
+ if (m_flat)
+ m_flat->remove(index);
+ else
+ m_nested->remove(index);
+
+ if (!m_isWorkerCopy)
+ emit countChanged(this->count());
}
/*!
@@ -514,20 +408,17 @@ void QDeclarativeListModel::insert(int index, const QScriptValue& valuemap)
qmlInfo(this) << tr("insert: value is not an object");
return;
}
- if (!_root)
- _root = new ModelNode;
- if (index >= _root->values.count() || index<0) {
- if (index == _root->values.count())
- append(valuemap);
- else
- qmlInfo(this) << tr("insert: index %1 out of range").arg(index);
+
+ if (index < 0 || index > count()) {
+ qmlInfo(this) << tr("insert: index %1 out of range").arg(index);
return;
}
- ModelNode *mn = new ModelNode;
- mn->setObjectValue(valuemap);
- _root->values.insert(index,qVariantFromValue(mn));
- emit itemsInserted(index,1);
- emit countChanged(_root->values.count());
+
+ bool ok = m_flat ? m_flat->insert(index, valuemap) : m_nested->insert(index, valuemap);
+ if (ok && !m_isWorkerCopy) {
+ emit itemsInserted(index, 1);
+ emit countChanged(this->count());
+ }
}
/*!
@@ -552,9 +443,10 @@ void QDeclarativeListModel::move(int from, int to, int n)
qmlInfo(this) << tr("move: out of range");
return;
}
- int origfrom=from; // preserve actual move, so any animations are correct
- int origto=to;
- int orign=n;
+
+ int origfrom = from;
+ int origto = to;
+ int orign = n;
if (from > to) {
// Only move forwards - flip if backwards moving
int tfrom = from;
@@ -563,24 +455,14 @@ void QDeclarativeListModel::move(int from, int to, int n)
to = tto+n;
n = tfrom-tto;
}
- if (n==1) {
- _root->values.move(from,to);
- } else {
- QList<QVariant> replaced;
- int i=0;
- QVariantList::const_iterator it=_root->values.begin(); it += from+n;
- for (; i<to-from; ++i,++it)
- replaced.append(*it);
- i=0;
- it=_root->values.begin(); it += from;
- for (; i<n; ++i,++it)
- replaced.append(*it);
- QVariantList::const_iterator f=replaced.begin();
- QVariantList::iterator t=_root->values.begin(); t += from;
- for (; f != replaced.end(); ++f, ++t)
- *t = *f;
- }
- emit itemsMoved(origfrom,origto,orign);
+
+ if (m_flat)
+ m_flat->move(from, to, n);
+ else
+ m_nested->move(from, to, n);
+
+ if (!m_isWorkerCopy)
+ emit itemsMoved(origfrom, origto, orign);
}
/*!
@@ -601,13 +483,8 @@ void QDeclarativeListModel::append(const QScriptValue& valuemap)
qmlInfo(this) << tr("append: value is not an object");
return;
}
- if (!_root)
- _root = new ModelNode;
- ModelNode *mn = new ModelNode;
- mn->setObjectValue(valuemap);
- _root->values << qVariantFromValue(mn);
- emit itemsInserted(count()-1,1);
- emit countChanged(_root->values.count());
+
+ insert(count(), valuemap);
}
/*!
@@ -641,15 +518,7 @@ QScriptValue QDeclarativeListModel::get(int index) const
return 0;
}
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
- if (!node)
- return 0;
- QDeclarativeEngine *eng = qmlEngine(this);
- if (!eng) {
- qWarning("Cannot call QDeclarativeListModel::get() without a QDeclarativeEngine");
- return 0;
- }
- return QDeclarativeEnginePrivate::qmlScriptObject(node->object(this), eng);
+ return m_flat ? m_flat->get(index) : m_nested->get(index);
}
/*!
@@ -673,27 +542,22 @@ void QDeclarativeListModel::set(int index, const QScriptValue& valuemap)
qmlInfo(this) << tr("set: value is not an object");
return;
}
- if ( !_root || index > _root->values.count() || index < 0) {
+ if (count() == 0 || index > count() || index < 0) {
qmlInfo(this) << tr("set: index %1 out of range").arg(index);
return;
}
- if (index == _root->values.count())
+
+ if (index == count()) {
append(valuemap);
- else {
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
+ } else {
QList<int> roles;
- node->setObjectValue(valuemap);
- QScriptValueIterator it(valuemap);
- while (it.hasNext()) {
- it.next();
- int r = roleStrings.indexOf(it.name());
- if (r<0) {
- r = roleStrings.count();
- roleStrings << it.name();
- }
- roles.append(r);
- }
- emit itemsChanged(index,1,roles);
+ if (m_flat)
+ m_flat->set(index, valuemap, &roles);
+ else
+ m_nested->set(index, valuemap, &roles);
+
+ if (!m_isWorkerCopy)
+ emit itemsChanged(index, 1, roles);
}
}
@@ -712,22 +576,33 @@ void QDeclarativeListModel::set(int index, const QScriptValue& valuemap)
*/
void QDeclarativeListModel::setProperty(int index, const QString& property, const QVariant& value)
{
- if ( !_root || index >= _root->values.count() || index < 0) {
+ if (count() == 0 || index >= count() || index < 0) {
qmlInfo(this) << tr("set: index %1 out of range").arg(index);
return;
}
- ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
- int r = roleStrings.indexOf(property);
- if (r<0) {
- r = roleStrings.count();
- roleStrings << property;
- }
+
QList<int> roles;
- roles.append(r);
+ if (m_flat)
+ m_flat->setProperty(index, property, value, &roles);
+ else
+ m_nested->setProperty(index, property, value, &roles);
- if (node)
- node->setProperty(property,value);
- emit itemsChanged(index,1,roles);
+ if (!m_isWorkerCopy)
+ emit itemsChanged(index, 1, roles);
+}
+
+/*!
+ \qmlmethod ListModel::sync()
+
+ Writes any unsaved changes to the list model after it has been modified
+ from a worker script.
+*/
+void QDeclarativeListModel::sync()
+{
+ // This is just a dummy method to make it look like sync() exists in
+ // ListModel (and not just QDeclarativeListModelWorkerAgent) and to let
+ // us document sync().
+ qmlInfo(this) << "List sync() can only be called from a WorkerScript";
}
bool QDeclarativeListModelParser::compileProperty(const QDeclarativeCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data)
@@ -858,7 +733,7 @@ void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray &
QDeclarativeListModel *rv = static_cast<QDeclarativeListModel *>(obj);
ModelNode *root = new ModelNode;
- rv->_root = root;
+ rv->m_nested->_root = root;
QStack<ModelNode *> nodes;
nodes << root;
@@ -943,7 +818,491 @@ bool QDeclarativeListModelParser::definesEmptyList(const QString &s)
\sa ListModel
*/
-static void dump(ModelNode *node, int ind)
+FlatListModel::FlatListModel(QDeclarativeListModel *base)
+ : m_scriptEngine(0), m_listModel(base)
+{
+}
+
+FlatListModel::~FlatListModel()
+{
+}
+
+QHash<int,QVariant> FlatListModel::data(int index, const QList<int> &roles) const
+{
+ Q_ASSERT(index >= 0 && index < m_values.count());
+
+ QHash<int, QVariant> row;
+ for (int i=0; i<roles.count(); i++) {
+ int role = roles[i];
+ if (m_values[index].contains(role))
+ row.insert(role, m_values[index][role]);
+ }
+ return row;
+}
+
+QVariant FlatListModel::data(int index, int role) const
+{
+ Q_ASSERT(index >= 0 && index < m_values.count());
+ if (m_values[index].contains(role))
+ return m_values[index][role];
+ return QVariant();
+}
+
+QList<int> FlatListModel::roles() const
+{
+ return m_roles.keys();
+}
+
+QString FlatListModel::toString(int role) const
+{
+ if (m_roles.contains(role))
+ return m_roles[role];
+ return QString();
+}
+
+int FlatListModel::count() const
+{
+ return m_values.count();
+}
+
+void FlatListModel::clear()
+{
+ m_values.clear();
+}
+
+void FlatListModel::remove(int index)
+{
+ m_values.removeAt(index);
+}
+
+bool FlatListModel::append(const QScriptValue &value)
+{
+ return insert(m_values.count(), value);
+}
+
+bool FlatListModel::insert(int index, const QScriptValue &value)
+{
+ Q_ASSERT(index >= 0 && index <= m_values.count());
+
+ QHash<int, QVariant> row;
+ if (!addValue(value, &row, 0))
+ return false;
+
+ m_values.insert(index, row);
+ return true;
+}
+
+QScriptValue FlatListModel::get(int index) const
+{
+ Q_ASSERT(index >= 0 && index < m_values.count());
+
+ QScriptEngine *scriptEngine = m_scriptEngine ? m_scriptEngine : QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(m_listModel));
+
+ if (!scriptEngine)
+ return 0;
+
+ QScriptValue rv = scriptEngine->newObject();
+
+ QHash<int, QVariant> row = m_values.at(index);
+ for (QHash<int, QVariant>::ConstIterator iter = row.begin(); iter != row.end(); ++iter)
+ rv.setProperty(m_roles.value(iter.key()), qScriptValueFromValue(scriptEngine, iter.value()));
+
+ return rv;
+}
+
+void FlatListModel::set(int index, const QScriptValue &value, QList<int> *roles)
+{
+ Q_ASSERT(index >= 0 && index < m_values.count());
+
+ QHash<int, QVariant> row = m_values[index];
+ if (addValue(value, &row, roles))
+ m_values[index] = row;
+}
+
+void FlatListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles)
+{
+ Q_ASSERT(index >= 0 && index < m_values.count());
+
+ QHash<QString, int>::Iterator iter = m_strings.find(property);
+ int role;
+ if (iter == m_strings.end()) {
+ role = m_roles.count();
+ m_roles.insert(role, property);
+ m_strings.insert(property, role);
+ } else {
+ role = iter.value();
+ }
+ roles->append(role);
+
+ m_values[index][role] = value;
+}
+
+void FlatListModel::move(int from, int to, int n)
+{
+ if (n == 1) {
+ m_values.move(from, to);
+ } else {
+ QList<QHash<int, QVariant> > replaced;
+ int i=0;
+ QList<QHash<int, QVariant> >::ConstIterator it=m_values.begin(); it += from+n;
+ for (; i<to-from; ++i,++it)
+ replaced.append(*it);
+ i=0;
+ it=m_values.begin(); it += from;
+ for (; i<n; ++i,++it)
+ replaced.append(*it);
+ QList<QHash<int, QVariant> >::ConstIterator f=replaced.begin();
+ QList<QHash<int, QVariant> >::Iterator t=m_values.begin(); t += from;
+ for (; f != replaced.end(); ++f, ++t)
+ *t = *f;
+ }
+}
+
+bool FlatListModel::addValue(const QScriptValue &value, QHash<int, QVariant> *row, QList<int> *roles)
+{
+ QScriptValueIterator it(value);
+ while (it.hasNext()) {
+ it.next();
+ if (it.value().isObject()) {
+ qmlInfo(m_listModel) << "Cannot add nested list values when modifying or after modification from a worker script";
+ return false;
+ }
+
+ QString name = it.name();
+ QVariant v = it.value().toVariant();
+
+ QHash<QString, int>::Iterator iter = m_strings.find(name);
+ if (iter == m_strings.end()) {
+ int role = m_roles.count();
+ m_roles.insert(role, name);
+ iter = m_strings.insert(name, role);
+ if (roles)
+ roles->append(role);
+ }
+ row->insert(*iter, v);
+ }
+ return true;
+}
+
+NestedListModel::NestedListModel(QDeclarativeListModel *base)
+ : _root(0), m_listModel(base), _rolesOk(false)
+{
+}
+
+NestedListModel::~NestedListModel()
+{
+ delete _root;
+}
+
+QVariant NestedListModel::valueForNode(ModelNode *node, bool *hasNested) const
+{
+ QObject *rv = 0;
+ if (hasNested)
+ *hasNested = false;
+
+ if (node->isArray) {
+ // List
+ rv = node->model(this);
+ if (hasNested)
+ *hasNested = true;
+ } else {
+ if (!node->properties.isEmpty()) {
+ // Object
+ rv = node->object(this);
+ } else if (node->values.count() == 0) {
+ // Invalid
+ return QVariant();
+ } else if (node->values.count() == 1) {
+ // Value
+ QVariant &var = node->values[0];
+ ModelNode *valueNode = qvariant_cast<ModelNode *>(var);
+ if (valueNode) {
+ if (!valueNode->properties.isEmpty())
+ rv = valueNode->object(this);
+ else
+ rv = valueNode->model(this);
+ } else {
+ return var;
+ }
+ }
+ }
+
+ if (rv) {
+ return QVariant::fromValue(rv);
+ } else {
+ return QVariant();
+ }
+}
+
+QHash<int,QVariant> NestedListModel::data(int index, const QList<int> &roles, bool *hasNested) const
+{
+ Q_ASSERT(_root && index >= 0 && index < _root->values.count());
+ checkRoles();
+ QHash<int, QVariant> rv;
+
+ ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
+ if (!node)
+ return rv;
+
+ for (int ii = 0; ii < roles.count(); ++ii) {
+ const QString &roleString = roleStrings.at(roles.at(ii));
+
+ QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString);
+ if (iter != node->properties.end()) {
+ ModelNode *row = *iter;
+ rv.insert(roles.at(ii), valueForNode(row, hasNested));
+ }
+ }
+
+ return rv;
+}
+
+QVariant NestedListModel::data(int index, int role) const
+{
+ Q_ASSERT(_root && index >= 0 && index < _root->values.count());
+ checkRoles();
+ QVariant rv;
+
+ ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
+ if (!node)
+ return rv;
+
+ const QString &roleString = roleStrings.at(role);
+
+ QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString);
+ if (iter != node->properties.end()) {
+ ModelNode *row = *iter;
+ rv = valueForNode(row);
+ }
+
+ return rv;
+}
+
+int NestedListModel::count() const
+{
+ if (!_root) return 0;
+ return _root->values.count();
+}
+
+void NestedListModel::clear()
+{
+ _rolesOk = false;
+ roleStrings.clear();
+
+ delete _root;
+ _root = 0;
+}
+
+void NestedListModel::remove(int index)
+{
+ if (!_root)
+ return;
+ ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
+ _root->values.removeAt(index);
+ if (node)
+ delete node;
+}
+
+bool NestedListModel::insert(int index, const QScriptValue& valuemap)
+{
+ if (!_root)
+ _root = new ModelNode;
+
+ ModelNode *mn = new ModelNode;
+ mn->setObjectValue(valuemap);
+ _root->values.insert(index,qVariantFromValue(mn));
+ return true;
+}
+
+void NestedListModel::move(int from, int to, int n)
+{
+ if (n==1) {
+ _root->values.move(from,to);
+ } else {
+ QList<QVariant> replaced;
+ int i=0;
+ QVariantList::const_iterator it=_root->values.begin(); it += from+n;
+ for (; i<to-from; ++i,++it)
+ replaced.append(*it);
+ i=0;
+ it=_root->values.begin(); it += from;
+ for (; i<n; ++i,++it)
+ replaced.append(*it);
+ QVariantList::const_iterator f=replaced.begin();
+ QVariantList::iterator t=_root->values.begin(); t += from;
+ for (; f != replaced.end(); ++f, ++t)
+ *t = *f;
+ }
+}
+
+bool NestedListModel::append(const QScriptValue& valuemap)
+{
+ if (!_root)
+ _root = new ModelNode;
+ ModelNode *mn = new ModelNode;
+ mn->setObjectValue(valuemap);
+ _root->values << qVariantFromValue(mn);
+ return true;
+}
+
+QScriptValue NestedListModel::get(int index) const
+{
+ ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
+ if (!node)
+ return 0;
+ QDeclarativeEngine *eng = qmlEngine(m_listModel);
+ if (!eng) {
+ qWarning("Cannot call QDeclarativeListModel::get() without a QDeclarativeEngine");
+ return 0;
+ }
+ return QDeclarativeEnginePrivate::qmlScriptObject(node->object(this), eng);
+}
+
+void NestedListModel::set(int index, const QScriptValue& valuemap, QList<int> *roles)
+{
+ Q_ASSERT(index >=0 && index < count());
+
+ ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
+ node->setObjectValue(valuemap);
+
+ QScriptValueIterator it(valuemap);
+ while (it.hasNext()) {
+ it.next();
+ int r = roleStrings.indexOf(it.name());
+ if (r < 0) {
+ r = roleStrings.count();
+ roleStrings << it.name();
+ }
+ roles->append(r);
+ }
+}
+
+void NestedListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles)
+{
+ Q_ASSERT(index >=0 && index < count());
+
+ ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
+ node->setProperty(property, value);
+
+ int r = roleStrings.indexOf(property);
+ if (r < 0) {
+ r = roleStrings.count();
+ roleStrings << property;
+ }
+ roles->append(r);
+}
+
+void NestedListModel::checkRoles() const
+{
+ if (_rolesOk || !_root)
+ return;
+
+ for (int i = 0; i<_root->values.count(); ++i) {
+ ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(i));
+ if (node) {
+ foreach (const QString &role, node->properties.keys()) {
+ if (!roleStrings.contains(role))
+ roleStrings.append(role);
+ }
+ }
+ }
+
+ _rolesOk = true;
+}
+
+QList<int> NestedListModel::roles() const
+{
+ checkRoles();
+ QList<int> rv;
+ for (int ii = 0; ii < roleStrings.count(); ++ii)
+ rv << ii;
+ return rv;
+}
+
+QString NestedListModel::toString(int role) const
+{
+ checkRoles();
+ if (role < roleStrings.count())
+ return roleStrings.at(role);
+ else
+ return QString();
+}
+
+
+ModelNode::ModelNode()
+: modelCache(0), objectCache(0), isArray(false)
+{
+}
+
+ModelNode::~ModelNode()
+{
+ ModelNode *node;
+
+ QList<ModelNode *> nodeValues = properties.values();
+ for (int ii = 0; ii < nodeValues.count(); ++ii) {
+ node = nodeValues[ii];
+ if (node) { delete node; node = 0; }
+ }
+
+ for (int ii = 0; ii < values.count(); ++ii) {
+ node = qvariant_cast<ModelNode *>(values.at(ii));
+ if (node) { delete node; node = 0; }
+ }
+
+ if (modelCache) { modelCache->m_nested->_root = 0/* ==this */; delete modelCache; modelCache = 0; }
+ if (objectCache) { delete objectCache; objectCache = 0; }
+}
+
+void ModelNode::setObjectValue(const QScriptValue& valuemap) {
+ QScriptValueIterator it(valuemap);
+ while (it.hasNext()) {
+ it.next();
+ ModelNode *value = new ModelNode;
+ QScriptValue v = it.value();
+ if (v.isArray()) {
+ value->isArray = true;
+ value->setListValue(v);
+ } else {
+ value->values << v.toVariant();
+ }
+ properties.insert(it.name(),value);
+ }
+}
+
+void ModelNode::setListValue(const QScriptValue& valuelist) {
+ QScriptValueIterator it(valuelist);
+ values.clear();
+ while (it.hasNext()) {
+ it.next();
+ ModelNode *value = new ModelNode;
+ QScriptValue v = it.value();
+ if (v.isArray()) {
+ value->isArray = true;
+ value->setListValue(v);
+ } else if (v.isObject()) {
+ value->setObjectValue(v);
+ } else {
+ value->values << v.toVariant();
+ }
+ values.append(qVariantFromValue(value));
+
+ }
+}
+
+void ModelNode::setProperty(const QString& prop, const QVariant& val) {
+ QHash<QString, ModelNode *>::const_iterator it = properties.find(prop);
+ if (it != properties.end()) {
+ (*it)->values[0] = val;
+ } else {
+ ModelNode *n = new ModelNode;
+ n->values << val;
+ properties.insert(prop,n);
+ }
+ if (objectCache)
+ objectCache->setValue(prop.toUtf8(), val);
+}
+
+void ModelNode::dump(ModelNode *node, int ind)
{
QByteArray indentBa(ind * 4, ' ');
const char *indent = indentBa.constData();
@@ -964,22 +1323,17 @@ static void dump(ModelNode *node, int ind)
}
}
-ModelNode::ModelNode()
-: modelCache(0), objectCache(0), isArray(false)
+ModelObject::ModelObject()
+: _mo(new QDeclarativeOpenMetaObject(this))
{
}
-ModelNode::~ModelNode()
+void ModelObject::setValue(const QByteArray &name, const QVariant &val)
{
- qDeleteAll(properties);
- for (int ii = 0; ii < values.count(); ++ii) {
- ModelNode *node = qvariant_cast<ModelNode *>(values.at(ii));
- if (node) { delete node; node = 0; }
- }
- if (modelCache) { modelCache->_root = 0/* ==this */; delete modelCache; modelCache = 0; }
- if (objectCache) { delete objectCache; }
+ _mo->setValue(name, val);
}
+
QT_END_NAMESPACE
#include <qdeclarativelistmodel.moc>
diff --git a/src/declarative/util/qdeclarativelistmodel_p.h b/src/declarative/util/qdeclarativelistmodel_p.h
index 8eb6583..6a0426b 100644
--- a/src/declarative/util/qdeclarativelistmodel_p.h
+++ b/src/declarative/util/qdeclarativelistmodel_p.h
@@ -59,6 +59,9 @@ QT_BEGIN_NAMESPACE
QT_MODULE(Declarative)
+class FlatListModel;
+class NestedListModel;
+class QDeclarativeListModelWorkerAgent;
struct ModelNode;
class Q_DECLARATIVE_EXPORT QDeclarativeListModel : public QListModelInterface
{
@@ -83,20 +86,26 @@ public:
Q_INVOKABLE void set(int index, const QScriptValue&);
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();
+
+ QDeclarativeListModelWorkerAgent *agent();
Q_SIGNALS:
void countChanged(int);
private:
- QVariant valueForNode(ModelNode *) const;
- mutable QStringList roleStrings;
friend class QDeclarativeListModelParser;
+ friend class QDeclarativeListModelWorkerAgent;
friend struct ModelNode;
- void checkRoles() const;
- void addRole(const QString &) const;
- mutable bool _rolesOk;
- ModelNode *_root;
+ QDeclarativeListModel(bool workerCopy, QObject *parent=0);
+ bool flatten();
+ bool modifyCheck();
+
+ QDeclarativeListModelWorkerAgent *m_agent;
+ NestedListModel *m_nested;
+ FlatListModel *m_flat;
+ bool m_isWorkerCopy;
};
// ### FIXME
diff --git a/src/declarative/util/qdeclarativelistmodel_p_p.h b/src/declarative/util/qdeclarativelistmodel_p_p.h
new file mode 100644
index 0000000..8041561
--- /dev/null
+++ b/src/declarative/util/qdeclarativelistmodel_p_p.h
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVELISTMODEL_P_P_H
+#define QDECLARATIVELISTMODEL_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 "qdeclarativelistmodel_p.h"
+
+#include "qdeclarative.h"
+#include "qdeclarativeengine_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QDeclarativeOpenMetaObject;
+class QScriptEngine;
+class QDeclarativeListModelWorkerAgent;
+struct ModelNode;
+
+class FlatListModel
+{
+public:
+ FlatListModel(QDeclarativeListModel *base);
+ ~FlatListModel();
+
+ QHash<int,QVariant> data(int index, const QList<int> &roles) const;
+ QVariant data(int index, int role) const;
+
+ QList<int> roles() const;
+ QString toString(int role) const;
+
+ int count() const;
+ void clear();
+ void remove(int index);
+ bool append(const QScriptValue&);
+ bool insert(int index, const QScriptValue&);
+ QScriptValue get(int index) const;
+ void set(int index, const QScriptValue&, QList<int> *roles);
+ void setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles);
+ void move(int from, int to, int count);
+
+private:
+ friend class QDeclarativeListModelWorkerAgent;
+ friend class QDeclarativeListModel;
+
+ bool addValue(const QScriptValue &value, QHash<int, QVariant> *row, QList<int> *roles);
+
+ QScriptEngine *m_scriptEngine;
+ QHash<int, QString> m_roles;
+ QHash<QString, int> m_strings;
+ QList<QHash<int, QVariant> > m_values;
+ QDeclarativeListModel *m_listModel;
+};
+
+class NestedListModel
+{
+public:
+ NestedListModel(QDeclarativeListModel *base);
+ ~NestedListModel();
+
+ QHash<int,QVariant> data(int index, const QList<int> &roles, bool *hasNested = 0) const;
+ QVariant data(int index, int role) const;
+
+ QList<int> roles() const;
+ QString toString(int role) const;
+
+ int count() const;
+ void clear();
+ void remove(int index);
+ bool append(const QScriptValue&);
+ bool insert(int index, const QScriptValue&);
+ QScriptValue get(int index) const;
+ void set(int index, const QScriptValue&, QList<int> *roles);
+ void setProperty(int index, const QString& property, const QVariant& value, QList<int> *role);
+ void move(int from, int to, int count);
+
+ QVariant valueForNode(ModelNode *, bool *hasNested = 0) const;
+ void checkRoles() const;
+
+ ModelNode *_root;
+ QDeclarativeListModel *m_listModel;
+
+private:
+ mutable QStringList roleStrings;
+ mutable bool _rolesOk;
+};
+
+
+class ModelObject : public QObject
+{
+ Q_OBJECT
+public:
+ ModelObject();
+ void setValue(const QByteArray &name, const QVariant &val);
+
+private:
+ QDeclarativeOpenMetaObject *_mo;
+};
+
+struct ModelNode
+{
+ ModelNode();
+ ~ModelNode();
+
+ QList<QVariant> values;
+ QHash<QString, ModelNode *> properties;
+
+ QDeclarativeListModel *model(const NestedListModel *model) {
+ if (!modelCache) {
+ modelCache = new QDeclarativeListModel;
+ QDeclarativeEngine::setContextForObject(modelCache,QDeclarativeEngine::contextForObject(model->m_listModel));
+ modelCache->m_nested->_root = this; // ListModel defaults to nestable model
+ }
+ return modelCache;
+ }
+
+ ModelObject *object(const NestedListModel *model) {
+ if (!objectCache) {
+ objectCache = new ModelObject();
+ QHash<QString, ModelNode *>::iterator it;
+ for (it = properties.begin(); it != properties.end(); ++it) {
+ objectCache->setValue(it.key().toUtf8(), model->valueForNode(*it));
+ }
+ }
+ return objectCache;
+ }
+
+
+ void setObjectValue(const QScriptValue& valuemap);
+ void setListValue(const QScriptValue& valuelist);
+ void setProperty(const QString& prop, const QVariant& val);
+ static void dump(ModelNode *node, int ind);
+
+ QDeclarativeListModel *modelCache;
+ ModelObject *objectCache;
+ bool isArray;
+};
+
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(ModelNode *)
+
+QT_END_HEADER
+
+#endif // QDECLARATIVELISTMODEL_P_P_H
+
diff --git a/src/declarative/util/qdeclarativelistmodelworkeragent.cpp b/src/declarative/util/qdeclarativelistmodelworkeragent.cpp
new file mode 100644
index 0000000..ee43447
--- /dev/null
+++ b/src/declarative/util/qdeclarativelistmodelworkeragent.cpp
@@ -0,0 +1,245 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdeclarativelistmodelworkeragent_p.h"
+#include "qdeclarativelistmodel_p_p.h"
+#include "qdeclarativedeclarativedata_p.h"
+#include "qdeclarativeengine_p.h"
+#include "qdeclarativeinfo.h"
+
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+
+
+QT_BEGIN_NAMESPACE
+
+
+void QDeclarativeListModelWorkerAgent::Data::clearChange()
+{
+ changes.clear();
+}
+
+void QDeclarativeListModelWorkerAgent::Data::insertChange(int index, int count)
+{
+ Change c = { Change::Inserted, index, count, 0 };
+ changes << c;
+}
+
+void QDeclarativeListModelWorkerAgent::Data::removeChange(int index, int count)
+{
+ Change c = { Change::Removed, index, count, 0 };
+ changes << c;
+}
+
+void QDeclarativeListModelWorkerAgent::Data::moveChange(int index, int count, int to)
+{
+ Change c = { Change::Moved, index, count, to };
+ changes << c;
+}
+
+void QDeclarativeListModelWorkerAgent::Data::changedChange(int index, int count)
+{
+ Change c = { Change::Changed, index, count, 0 };
+ changes << c;
+}
+
+QDeclarativeListModelWorkerAgent::QDeclarativeListModelWorkerAgent(QDeclarativeListModel *model)
+: m_engine(0), m_ref(1), m_orig(model), m_copy(new QDeclarativeListModel(true, this))
+{
+ m_copy->m_flat->m_roles = m_orig->m_flat->m_roles;
+ m_copy->m_flat->m_strings = m_orig->m_flat->m_strings;
+ m_copy->m_flat->m_values = m_orig->m_flat->m_values;
+}
+
+QDeclarativeListModelWorkerAgent::~QDeclarativeListModelWorkerAgent()
+{
+}
+
+void QDeclarativeListModelWorkerAgent::setScriptEngine(QScriptEngine *eng)
+{
+ m_engine = eng;
+ if (m_copy->m_flat)
+ m_copy->m_flat->m_scriptEngine = eng;
+}
+
+QScriptEngine *QDeclarativeListModelWorkerAgent::scriptEngine() const
+{
+ return m_engine;
+}
+
+void QDeclarativeListModelWorkerAgent::addref()
+{
+ m_ref.ref();
+}
+
+void QDeclarativeListModelWorkerAgent::release()
+{
+ bool del = !m_ref.deref();
+
+ if (del)
+ delete this;
+}
+
+int QDeclarativeListModelWorkerAgent::count() const
+{
+ return m_copy->count();
+}
+
+void QDeclarativeListModelWorkerAgent::clear()
+{
+ data.clearChange();
+ data.removeChange(0, m_copy->count());
+ m_copy->clear();
+}
+
+void QDeclarativeListModelWorkerAgent::remove(int index)
+{
+ int count = m_copy->count();
+ m_copy->remove(index);
+
+ if (m_copy->count() != count)
+ data.removeChange(index, 1);
+}
+
+void QDeclarativeListModelWorkerAgent::append(const QScriptValue &value)
+{
+ int count = m_copy->count();
+ m_copy->append(value);
+
+ if (m_copy->count() != count)
+ data.insertChange(m_copy->count() - 1, 1);
+}
+
+void QDeclarativeListModelWorkerAgent::insert(int index, const QScriptValue &value)
+{
+ int count = m_copy->count();
+ m_copy->insert(index, value);
+
+ if (m_copy->count() != count)
+ data.insertChange(index, 1);
+}
+
+QScriptValue QDeclarativeListModelWorkerAgent::get(int index) const
+{
+ if (index < 0 || index >= count())
+ return m_engine->undefinedValue();
+
+ return m_copy->get(index);
+}
+
+void QDeclarativeListModelWorkerAgent::set(int index, const QScriptValue &value)
+{
+ m_copy->set(index, value);
+ data.changedChange(index, 1);
+}
+
+void QDeclarativeListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value)
+{
+ m_copy->setProperty(index, property, value);
+ data.changedChange(index, 1);
+}
+
+void QDeclarativeListModelWorkerAgent::move(int from, int to, int count)
+{
+ m_copy->move(from, to, count);
+ data.moveChange(from, to, count);
+}
+
+void QDeclarativeListModelWorkerAgent::sync()
+{
+ Sync *s = new Sync;
+ s->data = data;
+ s->list = m_copy;
+ data.changes.clear();
+ QCoreApplication::postEvent(this, s);
+}
+
+bool QDeclarativeListModelWorkerAgent::event(QEvent *e)
+{
+ if (e->type() == QEvent::User) {
+ Sync *s = static_cast<Sync *>(e);
+
+ const QList<Change> &changes = s->data.changes;
+
+ if (m_copy) {
+ bool cc = m_copy->count() != s->list->count();
+
+ FlatListModel *orig = m_orig->m_flat;
+ FlatListModel *copy = s->list->m_flat;
+ if (!orig || !copy) {
+ qWarning("QML List worker: cannot synchronize lists");
+ return QObject::event(e);
+ }
+ orig->m_roles = copy->m_roles;
+ orig->m_strings = copy->m_strings;
+ orig->m_values = copy->m_values;
+
+ for (int ii = 0; ii < changes.count(); ++ii) {
+ const Change &change = changes.at(ii);
+ switch (change.type) {
+ case Change::Inserted:
+ emit m_orig->itemsInserted(change.index, change.count);
+ break;
+ case Change::Removed:
+ emit m_orig->itemsRemoved(change.index, change.count);
+ break;
+ case Change::Moved:
+ emit m_orig->itemsMoved(change.index, change.to, change.count);
+ break;
+ case Change::Changed:
+ emit m_orig->itemsMoved(change.index, change.to, change.count);
+ break;
+ }
+ }
+
+ if (cc)
+ emit m_orig->countChanged(m_copy->count());
+
+ //qDebug() << "------sync():" << m_copy->count() << s->list->count() << m_orig->count();
+ }
+ }
+
+ return QObject::event(e);
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/declarative/util/qdeclarativelistmodelworkeragent_p.h b/src/declarative/util/qdeclarativelistmodelworkeragent_p.h
new file mode 100644
index 0000000..b6a643b
--- /dev/null
+++ b/src/declarative/util/qdeclarativelistmodelworkeragent_p.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** 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, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVELISTMODELWORKERAGENT_P_H
+#define QDECLARATIVELISTMODELWORKERAGENT_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 "qdeclarative.h"
+
+#include <QtScript/qscriptvalue.h>
+#include <QtGui/qevent.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QDeclarativeListModel;
+
+// Currently this will leak as no-one releases it in the worker thread
+class QDeclarativeListModelWorkerAgent : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int count READ count)
+
+public:
+ QDeclarativeListModelWorkerAgent(QDeclarativeListModel *);
+ ~QDeclarativeListModelWorkerAgent();
+
+ void setScriptEngine(QScriptEngine *eng);
+ QScriptEngine *scriptEngine() const;
+
+ void addref();
+ void release();
+
+ int count() const;
+
+ Q_INVOKABLE void clear();
+ Q_INVOKABLE void remove(int index);
+ Q_INVOKABLE void append(const QScriptValue &);
+ Q_INVOKABLE void insert(int index, const QScriptValue&);
+ Q_INVOKABLE QScriptValue get(int index) const;
+ Q_INVOKABLE void set(int index, const QScriptValue &);
+ 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(QDeclarativeListModelWorkerAgent *_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;
+ }
+
+ QDeclarativeListModelWorkerAgent *a;
+ };
+protected:
+ virtual bool event(QEvent *);
+
+private:
+ friend class QDeclarativeWorkerScriptEnginePrivate;
+ QScriptEngine *m_engine;
+
+ struct Change {
+ enum { Inserted, Removed, Moved, Changed } type;
+ int index; // Inserted/Removed/Moved/Changed
+ int count; // Inserted/Removed/Moved/Changed
+ int to; // Moved
+ };
+
+ struct Data {
+ QList<Change> changes;
+
+ void clearChange();
+ void insertChange(int index, int count);
+ void removeChange(int index, int count);
+ void moveChange(int index, int count, int to);
+ void changedChange(int index, int count);
+ };
+ Data data;
+
+ struct Sync : public QEvent {
+ Sync() : QEvent(QEvent::User) {}
+ Data data;
+ QDeclarativeListModel *list;
+ };
+
+ QAtomicInt m_ref;
+ QDeclarativeListModel *m_orig;
+ QDeclarativeListModel *m_copy;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QDeclarativeListModelWorkerAgent::VariantRef);
+
+QT_END_HEADER
+
+#endif // QDECLARATIVEWORKERSCRIPT_P_H
+
diff --git a/src/declarative/util/util.pri b/src/declarative/util/util.pri
index 26edecc..041aa13 100644
--- a/src/declarative/util/util.pri
+++ b/src/declarative/util/util.pri
@@ -27,7 +27,8 @@ SOURCES += \
$$PWD/qdeclarativepixmapcache.cpp \
$$PWD/qdeclarativebehavior.cpp \
$$PWD/qdeclarativefontloader.cpp \
- $$PWD/qdeclarativestyledtext.cpp
+ $$PWD/qdeclarativestyledtext.cpp \
+ $$PWD/qdeclarativelistmodelworkeragent.cpp
HEADERS += \
$$PWD/qdeclarativeutilmodule_p.h\
@@ -59,7 +60,9 @@ HEADERS += \
$$PWD/qdeclarativepixmapcache_p.h \
$$PWD/qdeclarativebehavior_p.h \
$$PWD/qdeclarativefontloader_p.h \
- $$PWD/qdeclarativestyledtext_p.h
+ $$PWD/qdeclarativestyledtext_p.h \
+ $$PWD/qdeclarativelistmodelworkeragent_p.h \
+ $$PWD/qdeclarativelistmodelworkeragent_p_p.h
contains(QT_CONFIG, xmlpatterns) {
QT+=xmlpatterns