diff options
-rw-r--r-- | src/declarative/qml/qmlworkerscript.cpp | 487 | ||||
-rw-r--r-- | src/declarative/qml/qmlworkerscript_p.h | 41 |
2 files changed, 527 insertions, 1 deletions
diff --git a/src/declarative/qml/qmlworkerscript.cpp b/src/declarative/qml/qmlworkerscript.cpp index a0d6ef0..8ebf841 100644 --- a/src/declarative/qml/qmlworkerscript.cpp +++ b/src/declarative/qml/qmlworkerscript.cpp @@ -51,6 +51,9 @@ #include <QtCore/qwaitcondition.h> #include <QtScript/qscriptvalueiterator.h> #include <QtCore/qfile.h> +#include <QtDeclarative/qmlinfo.h> + +QT_BEGIN_NAMESPACE class WorkerDataEvent : public QEvent { @@ -144,6 +147,76 @@ private: void processLoad(int, const QUrl &); }; +// Currently this will leak as no-one releases it in the worker thread +class QmlWorkerListModelAgent : public QObject +{ + Q_OBJECT + Q_PROPERTY(int count READ count); + +public: + QmlWorkerListModelAgent(QmlWorkerListModel *); + ~QmlWorkerListModelAgent(); + + 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(QmlWorkerListModelAgent *_a) : a(_a) { if (a) a->addref(); } + ~VariantRef() { if (a) a->release(); } + + QmlWorkerListModelAgent *a; + }; +protected: + virtual bool event(QEvent *); + +private: + friend class QmlWorkerScriptEnginePrivate; + friend class QmlWorkerListModel; + 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; + QmlWorkerListModel *m_model; +}; +Q_DECLARE_METATYPE(QmlWorkerListModelAgent::VariantRef); + QmlWorkerScriptEnginePrivate::QmlWorkerScriptEnginePrivate() : workerEngine(0), m_nextId(0) { @@ -285,6 +358,15 @@ QVariant QmlWorkerScriptEnginePrivate::scriptValueToVariant(const QScriptValue & } return QVariant(list); + } else if (value.isQObject()) { + QmlWorkerListModel *lm = qobject_cast<QmlWorkerListModel *>(value.toQObject()); + if (lm) { + QmlWorkerListModelAgent::VariantRef v(lm->agent()); + return qVariantFromValue(v); + } else { + // No other QObject's are allowed to be sent + return QVariant(); + } } else if (value.isObject()) { QVariantHash hash; @@ -310,6 +392,15 @@ QScriptValue QmlWorkerScriptEnginePrivate::variantToScriptValue(const QVariant & return QScriptValue(value.toString()); } else if (value.userType() == QMetaType::QReal) { return QScriptValue(value.toReal()); + } else if (value.userType() == qMetaTypeId<QmlWorkerListModelAgent::VariantRef>()) { + QmlWorkerListModelAgent::VariantRef vr = qvariant_cast<QmlWorkerListModelAgent::VariantRef>(value); + if (vr.a->m_engine == 0) + vr.a->m_engine = engine; + else if (vr.a->m_engine != 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 + return o; } else if (value.userType() == QMetaType::QVariantList) { QVariantList list = qvariant_cast<QVariantList>(value); QScriptValue rv = engine->newArray(list.count()); @@ -513,5 +604,399 @@ bool QmlWorkerScript::event(QEvent *event) } } - QML_DEFINE_TYPE(Qt, 4, 6, WorkerScript, QmlWorkerScript); + +void QmlWorkerListModelAgent::Data::clearChange() +{ + changes.clear(); +} + +void QmlWorkerListModelAgent::Data::insertChange(int index, int count) +{ + Change c = { Change::Inserted, index, count, 0 }; + changes << c; +} + +void QmlWorkerListModelAgent::Data::removeChange(int index, int count) +{ + Change c = { Change::Removed, index, count, 0 }; + changes << c; +} + +void QmlWorkerListModelAgent::Data::changedChange(int index, int count) +{ + Change c = { Change::Changed, index, count, 0 }; + changes << c; +} + +QmlWorkerListModelAgent::QmlWorkerListModelAgent(QmlWorkerListModel *m) +: m_engine(0), m_model(m) +{ + data.roles = m_model->m_roles; + data.strings = m_model->m_strings; + data.values = m_model->m_values; +} + +QmlWorkerListModelAgent::~QmlWorkerListModelAgent() +{ +} + +void QmlWorkerListModelAgent::addref() +{ + m_ref.ref(); +} + +void QmlWorkerListModelAgent::release() +{ + if (!m_ref.deref()) + delete this; +} + +int QmlWorkerListModelAgent::count() const +{ + return data.values.count(); +} + +void QmlWorkerListModelAgent::clear() +{ + data.clearChange(); + data.insertChange(0, data.values.count()); + data.values.clear(); +} + +void QmlWorkerListModelAgent::remove(int index) +{ + if (data.values.count() <= index) + return; + + data.values.removeAt(index); + data.removeChange(index, 1); +} + +void QmlWorkerListModelAgent::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 QmlWorkerListModelAgent::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 QmlWorkerListModelAgent::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 QmlWorkerListModelAgent::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 QmlWorkerListModelAgent::sync() +{ + Sync *s = new Sync; + s->data = data; + data.changes.clear(); + QCoreApplication::postEvent(this, s); +} + +bool QmlWorkerListModelAgent::event(QEvent *e) +{ + if (e->type() == QEvent::User) { + Sync *s = static_cast<Sync *>(e); + + const QList<Change> &changes = s->data.changes; + + if (m_model) { + 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; + } + } + } + } + + return QObject::event(e); +} + +QmlWorkerListModel::QmlWorkerListModel(QObject *parent) +: QListModelInterface(parent), m_agent(0) +{ +} + +QmlWorkerListModel::~QmlWorkerListModel() +{ + if (m_agent) { + m_agent->m_model = 0; + m_agent->release(); + } +} + +void QmlWorkerListModel::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); +} + +void QmlWorkerListModel::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); +} + +void QmlWorkerListModel::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); +} + +void QmlWorkerListModel::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); +} + +QScriptValue QmlWorkerListModel::get(int index) const +{ + QmlEngine *engine = qmlEngine(this); + if (!engine || m_values.count() <= index) + return QScriptValue(); + + QScriptEngine *scriptEngine = QmlEnginePrivate::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; +} + +void QmlWorkerListModel::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()); + } +} + +QmlWorkerListModelAgent *QmlWorkerListModel::agent() +{ + if (!m_agent) + m_agent = new QmlWorkerListModelAgent(this); + + return m_agent; +} + +QList<int> QmlWorkerListModel::roles() const +{ + return m_roles.keys(); +} + +QString QmlWorkerListModel::toString(int role) const +{ + return m_roles.value(role); +} + +int QmlWorkerListModel::count() const +{ + return m_values.count(); +} + +QHash<int,QVariant> QmlWorkerListModel::data(int index, const QList<int> &) const +{ + if (m_values.count() <= index) + return QHash<int, QVariant>(); + else + return m_values.at(index); +} + +QVariant QmlWorkerListModel::data(int index, int role) const +{ + if (m_values.count() <= index) + return QVariant(); + else + return m_values.at(index).value(role); +} + +QML_DEFINE_TYPE(Qt,4,6,WorkerListModel,QmlWorkerListModel) + +#include "qmlworkerscript.moc" + +QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlworkerscript_p.h b/src/declarative/qml/qmlworkerscript_p.h index 1ec7af3..fdc726e 100644 --- a/src/declarative/qml/qmlworkerscript_p.h +++ b/src/declarative/qml/qmlworkerscript_p.h @@ -55,11 +55,14 @@ #include "qml.h" #include "qmlparserstatus.h" +#include <private/qlistmodelinterface_p.h> #include <QtCore/qthread.h> #include <QtScript/qscriptvalue.h> #include <QtCore/qurl.h> +QT_BEGIN_NAMESPACE + class QmlWorkerScript; class QmlWorkerScriptEnginePrivate; class QmlWorkerScriptEngine : public QThread @@ -110,6 +113,44 @@ private: int m_scriptId; QUrl m_source; }; + +class QmlWorkerListModelAgent; +class QmlWorkerListModel : public QListModelInterface +{ + Q_OBJECT + +public: + QmlWorkerListModel(QObject * = 0); + virtual ~QmlWorkerListModel(); + + 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 &); + + QmlWorkerListModelAgent *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; + +private: + friend class QmlWorkerListModelAgent; + + QHash<int, QString> m_roles; + QHash<QString, int> m_strings; + QList<QHash<int, QVariant> > m_values; + + QmlWorkerListModelAgent *m_agent; +}; + +QT_END_NAMESPACE + QML_DECLARE_TYPE(QmlWorkerScript); +QML_DECLARE_TYPE(QmlWorkerListModel); #endif // QMLWORKERSCRIPT_P_H |