summaryrefslogtreecommitdiffstats
path: root/src/declarative/util/qdeclarativelistmodel.cpp
diff options
context:
space:
mode:
authorGunnar Sletta <gunnar.sletta@nokia.com>2010-09-27 09:41:38 (GMT)
committerGunnar Sletta <gunnar.sletta@nokia.com>2010-09-27 09:41:38 (GMT)
commit2271315eb46665b0a9e61ff0525340713163c1b6 (patch)
treed0068e34924eca85eb6af4089b3f443a89c929f6 /src/declarative/util/qdeclarativelistmodel.cpp
parent33e7ee9d1866f12a9b92fe4b5549c31e30974d8e (diff)
parent53d010a989aed878c21522cbaf0d75c7cf821b42 (diff)
downloadQt-2271315eb46665b0a9e61ff0525340713163c1b6.zip
Qt-2271315eb46665b0a9e61ff0525340713163c1b6.tar.gz
Qt-2271315eb46665b0a9e61ff0525340713163c1b6.tar.bz2
Merge branch 'master' of scm.dev.nokia.troll.no:qt/qt
Conflicts: configure src/corelib/global/qglobal.h
Diffstat (limited to 'src/declarative/util/qdeclarativelistmodel.cpp')
-rw-r--r--src/declarative/util/qdeclarativelistmodel.cpp536
1 files changed, 427 insertions, 109 deletions
diff --git a/src/declarative/util/qdeclarativelistmodel.cpp b/src/declarative/util/qdeclarativelistmodel.cpp
index c7d5024..5ce95e9 100644
--- a/src/declarative/util/qdeclarativelistmodel.cpp
+++ b/src/declarative/util/qdeclarativelistmodel.cpp
@@ -58,6 +58,28 @@ Q_DECLARE_METATYPE(QListModelInterface *)
QT_BEGIN_NAMESPACE
+template<typename T>
+void qdeclarativelistmodel_move(int from, int to, int n, T *items)
+{
+ if (n == 1) {
+ items->move(from, to);
+ } else {
+ T replaced;
+ int i=0;
+ typename T::ConstIterator it=items->begin(); it += from+n;
+ for (; i<to-from; ++i,++it)
+ replaced.append(*it);
+ i=0;
+ it=items->begin(); it += from;
+ for (; i<n; ++i,++it)
+ replaced.append(*it);
+ typename T::ConstIterator f=replaced.begin();
+ typename T::Iterator t=items->begin(); t += from;
+ for (; f != replaced.end(); ++f, ++t)
+ *t = *f;
+ }
+}
+
QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListModelData::instructions() const
{
return (QDeclarativeListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData));
@@ -69,49 +91,67 @@ QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListM
\since 4.7
\brief The ListModel element defines a free-form list data source.
- The ListModel is a simple hierarchy of elements containing data roles. The contents can
- be defined dynamically, or explicitly in QML:
+ The ListModel is a simple container of ListElement definitions, each containing data roles.
+ The contents can be defined dynamically, or explicitly in QML.
- For example:
+ 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.
- \snippet doc/src/snippets/declarative/listmodel.qml 0
+ Elements can be manipulated via the model using the setProperty() method, which
+ allows the roles of the specified element to be set and changed.
- Roles (properties) must begin with a lower-case letter. The above example defines a
- ListModel containing three elements, with the roles "name" and "cost".
+ \section1 Example Usage
- Values must be simple constants - either strings (quoted and optionally within a call to QT_TR_NOOP),
- bools (true, false), numbers, or enum values (like Text.AlignHCenter).
+ The following example shows a ListModel containing three elements, with the roles
+ "name" and "cost".
+
+ \beginfloatright
+ \inlineimage listmodel.png
+ \endfloat
+
+ \snippet doc/src/snippets/declarative/listmodel.qml 0
- The defined model can be used in views such as ListView:
+ \clearfloat
+ 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 doc/src/snippets/declarative/listmodel-simple.qml 0
\dots 8
\snippet doc/src/snippets/declarative/listmodel-simple.qml 1
- \image listmodel.png
- It is possible for roles to contain list data. In the example below we create a list of fruit attributes:
+ It is possible for roles to contain list data. In the following example we
+ create a list of fruit attributes:
\snippet doc/src/snippets/declarative/listmodel-nested.qml model
- The delegate below displays all the fruit attributes:
+ The delegate displays all the fruit attributes:
- \snippet doc/src/snippets/declarative/listmodel-nested.qml delegate
- \image listmodel-nested.png
+ \beginfloatright
+ \inlineimage listmodel-nested.png
+ \endfloat
+ \snippet doc/src/snippets/declarative/listmodel-nested.qml delegate
- \section2 Modifying list models
+ \clearfloat
+ \section1 Modifying List Models
The content of a ListModel may be created and modified using the clear(),
append(), set() and setProperty() methods. For example:
-
- \snippet doc/src/snippets/declarative/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.
+ \snippet doc/src/snippets/declarative/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.
- \section2 Using threaded list models with WorkerScript
+ \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
@@ -127,19 +167,19 @@ QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListM
\snippet examples/declarative/threading/threadedlistmodel/dataloader.js 0
-working-with-data
- worker script by calling \l WorkerScript::sendMessage(). When this message
- is received, \l {WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in
- \tt dataloader.js, which appends the current time to the list model.
+ 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
+ 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.
- \section3 Limitations
+ \section1 Restrictions
- If a list model is to be accessed from a WorkerScript, it \bold cannot
- contain list data. So, the following model cannot be used from a WorkerScript
+ If a list model is to be accessed from a WorkerScript, it cannot
+ contain list-type data. So, the following model cannot be used from a WorkerScript
because of the list contained in the "attributes" property:
\code
@@ -156,7 +196,7 @@ working-with-data
}
\endcode
- In addition, the WorkerScript cannot add any list data to the model.
+ In addition, the WorkerScript cannot add list-type data to the model.
\sa {qmlmodels}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtDeclarative
*/
@@ -177,17 +217,25 @@ working-with-data
*/
QDeclarativeListModel::QDeclarativeListModel(QObject *parent)
-: QListModelInterface(parent), m_agent(0), m_nested(new NestedListModel(this)), m_flat(0), m_isWorkerCopy(false)
+: QListModelInterface(parent), m_agent(0), m_nested(new NestedListModel(this)), m_flat(0)
{
}
-QDeclarativeListModel::QDeclarativeListModel(bool workerCopy, QObject *parent)
-: QListModelInterface(parent), m_agent(0), m_nested(0), m_flat(0), m_isWorkerCopy(workerCopy)
+QDeclarativeListModel::QDeclarativeListModel(const QDeclarativeListModel *orig, QDeclarativeListModelWorkerAgent *parent)
+: QListModelInterface(parent), m_agent(0), m_nested(0), m_flat(0)
{
- if (workerCopy)
- m_flat = new FlatListModel(this);
- else
- m_nested = new NestedListModel(this);
+ m_flat = new FlatListModel(this);
+ m_flat->m_parentAgent = parent;
+
+ if (orig->m_flat) {
+ m_flat->m_roles = orig->m_flat->m_roles;
+ m_flat->m_strings = orig->m_flat->m_strings;
+ m_flat->m_values = orig->m_flat->m_values;
+
+ m_flat->m_nodeData.reserve(m_flat->m_values.count());
+ for (int i=0; i<m_flat->m_values.count(); i++)
+ m_flat->m_nodeData << 0;
+ }
}
QDeclarativeListModel::~QDeclarativeListModel()
@@ -223,19 +271,28 @@ bool QDeclarativeListModel::flatten()
flat->m_strings.insert(s, roles[i]);
}
+ flat->m_nodeData.reserve(flat->m_values.count());
+ for (int i=0; i<flat->m_values.count(); i++)
+ flat->m_nodeData << 0;
+
m_flat = flat;
delete m_nested;
m_nested = 0;
return true;
}
+bool QDeclarativeListModel::inWorkerThread() const
+{
+ return m_flat && m_flat->m_parentAgent;
+}
+
QDeclarativeListModelWorkerAgent *QDeclarativeListModel::agent()
{
if (m_agent)
return m_agent;
if (!flatten()) {
- qmlInfo(this) << "List contains nested list values and cannot be used from a worker script";
+ qmlInfo(this) << "List contains list-type data and cannot be used from a worker script";
return 0;
}
@@ -293,12 +350,43 @@ void QDeclarativeListModel::clear()
else
m_nested->clear();
- if (!m_isWorkerCopy) {
+ if (!inWorkerThread()) {
emit itemsRemoved(0, cleared);
emit countChanged();
}
}
+QDeclarativeListModel *ModelNode::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
+
+ for (int i=0; i<values.count(); ++i) {
+ ModelNode *subNode = qvariant_cast<ModelNode *>(values.at(i));
+ if (subNode)
+ subNode->m_model = modelCache->m_nested;
+ }
+ }
+ return modelCache;
+}
+
+ModelObject *ModelNode::object(const NestedListModel *model)
+{
+ if (!objectCache) {
+ objectCache = new ModelObject(this,
+ const_cast<NestedListModel*>(model),
+ QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(model->m_listModel)));
+ QHash<QString, ModelNode *>::iterator it;
+ for (it = properties.begin(); it != properties.end(); ++it) {
+ objectCache->setValue(it.key().toUtf8(), model->valueForNode(*it));
+ }
+ objectCache->setNodeUpdatesEnabled(true);
+ }
+ return objectCache;
+}
+
/*!
\qmlmethod ListModel::remove(int index)
@@ -318,7 +406,7 @@ void QDeclarativeListModel::remove(int index)
else
m_nested->remove(index);
- if (!m_isWorkerCopy) {
+ if (!inWorkerThread()) {
emit itemsRemoved(index, 1);
emit countChanged();
}
@@ -352,7 +440,7 @@ void QDeclarativeListModel::insert(int index, const QScriptValue& valuemap)
}
bool ok = m_flat ? m_flat->insert(index, valuemap) : m_nested->insert(index, valuemap);
- if (ok && !m_isWorkerCopy) {
+ if (ok && !inWorkerThread()) {
emit itemsInserted(index, 1);
emit countChanged();
}
@@ -376,7 +464,7 @@ void QDeclarativeListModel::move(int from, int to, int n)
{
if (n==0 || from==to)
return;
- if (from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0) {
+ if (!canMove(from, to, n)) {
qmlInfo(this) << tr("move: out of range");
return;
}
@@ -398,7 +486,7 @@ void QDeclarativeListModel::move(int from, int to, int n)
else
m_nested->move(from, to, n);
- if (!m_isWorkerCopy)
+ if (!inWorkerThread())
emit itemsMoved(origfrom, origto, orign);
}
@@ -427,11 +515,15 @@ void QDeclarativeListModel::append(const QScriptValue& valuemap)
/*!
\qmlmethod object ListModel::get(int index)
- Returns the item at \a index in the list model.
+ Returns the item at \a index in the list model. This allows the item
+ data to be accessed or modified from JavaScript:
\code
- fruitModel.append({"cost": 5.95, "name":"Jackfruit"})
- fruitModel.get(0).cost
+ 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.
@@ -446,6 +538,9 @@ void QDeclarativeListModel::append(const QScriptValue& valuemap)
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()
*/
QScriptValue QDeclarativeListModel::get(int index) const
@@ -489,7 +584,7 @@ void QDeclarativeListModel::set(int index, const QScriptValue& valuemap)
else
m_nested->set(index, valuemap, &roles);
- if (!m_isWorkerCopy)
+ if (!inWorkerThread())
emit itemsChanged(index, 1, roles);
}
}
@@ -520,7 +615,7 @@ void QDeclarativeListModel::setProperty(int index, const QString& property, cons
else
m_nested->setProperty(index, property, value, &roles);
- if (!m_isWorkerCopy)
+ if (!inWorkerThread())
emit itemsChanged(index, 1, roles);
}
@@ -687,7 +782,7 @@ void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray &
{
QDeclarativeListModel *rv = static_cast<QDeclarativeListModel *>(obj);
- ModelNode *root = new ModelNode;
+ ModelNode *root = new ModelNode(rv->m_nested);
rv->m_nested->_root = root;
QStack<ModelNode *> nodes;
nodes << root;
@@ -704,7 +799,7 @@ void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray &
case ListInstruction::Push:
{
ModelNode *n = nodes.top();
- ModelNode *n2 = new ModelNode;
+ ModelNode *n2 = new ModelNode(rv->m_nested);
n->values << QVariant::fromValue(n2);
nodes.push(n2);
if (processingSet)
@@ -743,7 +838,7 @@ void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray &
case ListInstruction::Set:
{
ModelNode *n = nodes.top();
- ModelNode *n2 = new ModelNode;
+ ModelNode *n2 = new ModelNode(rv->m_nested);
n->properties.insert(QString::fromUtf8(data + instr.dataIdx), n2);
nodes.push(n2);
processingSet = true;
@@ -751,6 +846,13 @@ void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray &
break;
}
}
+
+ ModelNode *rootNode = rv->m_nested->_root;
+ for (int i=0; i<rootNode->values.count(); ++i) {
+ ModelNode *node = qvariant_cast<ModelNode *>(rootNode->values[i]);
+ node->listIndex = i;
+ node->updateListIndexes();
+ }
}
bool QDeclarativeListModelParser::definesEmptyList(const QString &s)
@@ -765,22 +867,57 @@ bool QDeclarativeListModelParser::definesEmptyList(const QString &s)
return false;
}
+
/*!
\qmlclass ListElement QDeclarativeListElement
\ingroup qml-working-with-data
\since 4.7
\brief The ListElement element defines a data item in a ListModel.
+ 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 doc/src/snippets/declarative/qml-data-models/listelements.qml model
+
+ The delegate obtains the name and cost for each element by simply referring
+ to \c name and \c cost:
+
+ \snippet doc/src/snippets/declarative/qml-data-models/listelements.qml view
+
\sa ListModel
*/
FlatListModel::FlatListModel(QDeclarativeListModel *base)
- : m_scriptEngine(0), m_listModel(base)
+ : m_scriptEngine(0), m_listModel(base), m_scriptClass(0), m_parentAgent(0)
{
}
FlatListModel::~FlatListModel()
{
+ qDeleteAll(m_nodeData);
}
QHash<int,QVariant> FlatListModel::data(int index, const QList<int> &roles) const
@@ -824,11 +961,15 @@ int FlatListModel::count() const
void FlatListModel::clear()
{
m_values.clear();
+
+ qDeleteAll(m_nodeData);
+ m_nodeData.clear();
}
void FlatListModel::remove(int index)
{
m_values.removeAt(index);
+ removedNode(index);
}
bool FlatListModel::append(const QScriptValue &value)
@@ -845,6 +986,8 @@ bool FlatListModel::insert(int index, const QScriptValue &value)
return false;
m_values.insert(index, row);
+ insertedNode(index);
+
return true;
}
@@ -858,13 +1001,17 @@ QScriptValue FlatListModel::get(int index) const
if (index < 0 || index >= m_values.count())
return scriptEngine->undefinedValue();
- QScriptValue rv = scriptEngine->newObject();
+ FlatListModel *that = const_cast<FlatListModel*>(this);
+ if (!m_scriptClass)
+ that->m_scriptClass = new FlatListScriptClass(that, scriptEngine);
- 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()), scriptEngine->toScriptValue(iter.value()));
+ FlatNodeData *data = m_nodeData.value(index);
+ if (!data) {
+ data = new FlatNodeData(index);
+ that->m_nodeData.replace(index, data);
+ }
- return rv;
+ return QScriptDeclarativeClass::newObject(scriptEngine, m_scriptClass, new FlatNodeObjectData(data));
}
void FlatListModel::set(int index, const QScriptValue &value, QList<int> *roles)
@@ -896,23 +1043,8 @@ void FlatListModel::setProperty(int index, const QString& property, const QVaria
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;
- }
+ qdeclarativelistmodel_move<QList<QHash<int, QVariant> > >(from, to, n, &m_values);
+ moveNodes(from, to, n);
}
bool FlatListModel::addValue(const QScriptValue &value, QHash<int, QVariant> *row, QList<int> *roles)
@@ -922,7 +1054,7 @@ bool FlatListModel::addValue(const QScriptValue &value, QHash<int, QVariant> *ro
it.next();
QScriptValue value = it.value();
if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) {
- qmlInfo(m_listModel) << "Cannot add nested list values when modifying or after modification from a worker script";
+ qmlInfo(m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script";
return false;
}
@@ -942,6 +1074,139 @@ bool FlatListModel::addValue(const QScriptValue &value, QHash<int, QVariant> *ro
return true;
}
+void FlatListModel::insertedNode(int index)
+{
+ if (index >= 0 && index <= m_values.count()) {
+ m_nodeData.insert(index, 0);
+
+ for (int i=index + 1; i<m_nodeData.count(); i++) {
+ if (m_nodeData[i])
+ m_nodeData[i]->index = i;
+ }
+ }
+}
+
+void FlatListModel::removedNode(int index)
+{
+ if (index >= 0 && index < m_nodeData.count()) {
+ delete m_nodeData.takeAt(index);
+
+ for (int i=index; i<m_nodeData.count(); i++) {
+ if (m_nodeData[i])
+ m_nodeData[i]->index = i;
+ }
+ }
+}
+
+void FlatListModel::moveNodes(int from, int to, int n)
+{
+ if (!m_listModel->canMove(from, to, n))
+ return;
+
+ qdeclarativelistmodel_move<QList<FlatNodeData *> >(from, to, n, &m_nodeData);
+
+ for (int i=from; i<from + (to-from); i++) {
+ if (m_nodeData[i])
+ m_nodeData[i]->index = i;
+ }
+}
+
+
+
+FlatNodeData::~FlatNodeData()
+{
+ for (QSet<FlatNodeObjectData *>::Iterator iter = objects.begin(); iter != objects.end(); ++iter) {
+ FlatNodeObjectData *data = *iter;
+ data->nodeData = 0;
+ }
+}
+
+void FlatNodeData::addData(FlatNodeObjectData *data)
+{
+ objects.insert(data);
+}
+
+void FlatNodeData::removeData(FlatNodeObjectData *data)
+{
+ objects.remove(data);
+}
+
+
+FlatListScriptClass::FlatListScriptClass(FlatListModel *model, QScriptEngine *seng)
+ : QScriptDeclarativeClass(seng),
+ m_model(model)
+{
+}
+
+QScriptDeclarativeClass::Value FlatListScriptClass::property(Object *obj, const Identifier &name)
+{
+ FlatNodeObjectData *objData = static_cast<FlatNodeObjectData*>(obj);
+ if (!objData->nodeData) // item at this index has been deleted
+ return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue());
+
+ int index = objData->nodeData->index;
+ QString propName = toString(name);
+ int role = m_model->m_strings.value(propName, -1);
+
+ if (role >= 0 && index >=0 ) {
+ const QHash<int, QVariant> &row = m_model->m_values[index];
+ QScriptValue sv = engine()->toScriptValue<QVariant>(row[role]);
+ return QScriptDeclarativeClass::Value(engine(), sv);
+ }
+
+ return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue());
+}
+
+void FlatListScriptClass::setProperty(Object *obj, const Identifier &name, const QScriptValue &value)
+{
+ if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) {
+ qmlInfo(m_model->m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script";
+ return;
+ }
+
+ FlatNodeObjectData *objData = static_cast<FlatNodeObjectData*>(obj);
+ if (!objData->nodeData) // item at this index has been deleted
+ return;
+
+ int index = objData->nodeData->index;
+ QString propName = toString(name);
+
+ int role = m_model->m_strings.value(propName, -1);
+ if (role >= 0 && index >= 0) {
+ QHash<int, QVariant> &row = m_model->m_values[index];
+ row[role] = value.toVariant();
+
+ if (m_model->m_parentAgent) {
+ // This is the list in the worker thread, so tell the agent to
+ // emit itemsChanged() later
+ m_model->m_parentAgent->changedData(index, 1);
+ } else {
+ // This is the list in the main thread, so emit itemsChanged()
+ QList<int> roles;
+ roles << role;
+ emit m_model->m_listModel->itemsChanged(index, 1, roles);
+ }
+ }
+}
+
+QScriptClass::QueryFlags FlatListScriptClass::queryProperty(Object *, const Identifier &, QScriptClass::QueryFlags)
+{
+ return (QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess);
+}
+
+bool FlatListScriptClass::compare(Object *obj1, Object *obj2)
+{
+ FlatNodeObjectData *data1 = static_cast<FlatNodeObjectData*>(obj1);
+ FlatNodeObjectData *data2 = static_cast<FlatNodeObjectData*>(obj2);
+
+ if (!data1->nodeData || !data2->nodeData)
+ return false;
+
+ return data1->nodeData->index == data2->nodeData->index;
+}
+
+
+
NestedListModel::NestedListModel(QDeclarativeListModel *base)
: _root(0), m_ownsRoot(false), m_listModel(base), _rolesOk(false)
{
@@ -1067,11 +1332,12 @@ void NestedListModel::remove(int index)
bool NestedListModel::insert(int index, const QScriptValue& valuemap)
{
if (!_root) {
- _root = new ModelNode;
+ _root = new ModelNode(this);
m_ownsRoot = true;
}
- ModelNode *mn = new ModelNode;
+ ModelNode *mn = new ModelNode(this);
+ mn->listIndex = index;
mn->setObjectValue(valuemap);
_root->values.insert(index,QVariant::fromValue(mn));
return true;
@@ -1079,34 +1345,19 @@ bool NestedListModel::insert(int index, const QScriptValue& valuemap)
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;
- }
+ if (!_root)
+ return;
+ qdeclarativelistmodel_move<QVariantList>(from, to, n, &_root->values);
}
bool NestedListModel::append(const QScriptValue& valuemap)
{
if (!_root) {
- _root = new ModelNode;
+ _root = new ModelNode(this);
m_ownsRoot = true;
}
- ModelNode *mn = new ModelNode;
- mn->setObjectValue(valuemap);
- _root->values << QVariant::fromValue(mn);
+
+ insert(count(), valuemap);
return true;
}
@@ -1201,8 +1452,8 @@ QString NestedListModel::toString(int role) const
}
-ModelNode::ModelNode()
-: modelCache(0), objectCache(0), isArray(false)
+ModelNode::ModelNode(NestedListModel *model)
+: modelCache(0), objectCache(0), isArray(false), m_model(model), listIndex(-1)
{
}
@@ -1226,18 +1477,18 @@ void ModelNode::clear()
properties.clear();
}
-void ModelNode::setObjectValue(const QScriptValue& valuemap) {
+void ModelNode::setObjectValue(const QScriptValue& valuemap, bool writeToCache) {
QScriptValueIterator it(valuemap);
while (it.hasNext()) {
it.next();
- ModelNode *value = new ModelNode;
+ ModelNode *value = new ModelNode(m_model);
QScriptValue v = it.value();
if (v.isArray()) {
value->isArray = true;
value->setListValue(v);
} else {
value->values << v.toVariant();
- if (objectCache)
+ if (writeToCache && objectCache)
objectCache->setValue(it.name().toUtf8(), value->values.last());
}
if (properties.contains(it.name()))
@@ -1250,14 +1501,16 @@ void ModelNode::setListValue(const QScriptValue& valuelist) {
values.clear();
int size = valuelist.property(QLatin1String("length")).toInt32();
for (int i=0; i<size; i++) {
- ModelNode *value = new ModelNode;
+ ModelNode *value = new ModelNode(m_model);
QScriptValue v = valuelist.property(i);
if (v.isArray()) {
value->isArray = true;
value->setListValue(v);
} else if (v.isObject()) {
+ value->listIndex = i;
value->setObjectValue(v);
} else {
+ value->listIndex = i;
value->values << v.toVariant();
}
values.append(QVariant::fromValue(value));
@@ -1269,7 +1522,7 @@ void ModelNode::setProperty(const QString& prop, const QVariant& val) {
if (it != properties.end()) {
(*it)->values[0] = val;
} else {
- ModelNode *n = new ModelNode;
+ ModelNode *n = new ModelNode(m_model);
n->values << val;
properties.insert(prop,n);
}
@@ -1277,6 +1530,40 @@ void ModelNode::setProperty(const QString& prop, const QVariant& val) {
objectCache->setValue(prop.toUtf8(), val);
}
+void ModelNode::updateListIndexes()
+{
+ for (QHash<QString, ModelNode *>::ConstIterator iter = properties.begin(); iter != properties.end(); ++iter) {
+ ModelNode *node = iter.value();
+ if (node->isArray) {
+ for (int i=0; i<node->values.count(); ++i) {
+ ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(i));
+ if (subNode)
+ subNode->listIndex = i;
+ }
+ }
+ node->updateListIndexes();
+ }
+}
+
+/*
+ Need to call this to emit itemsChanged() for modifications outside of set()
+ and setProperty(), i.e. if an item returned from get() is modified
+*/
+void ModelNode::changedProperty(const QString &name) const
+{
+ if (listIndex < 0)
+ return;
+
+ m_model->checkRoles();
+ QList<int> roles;
+ int role = m_model->roleStrings.indexOf(name);
+ if (role < 0)
+ roles = m_model->roles();
+ else
+ roles << role;
+ emit m_model->m_listModel->itemsChanged(listIndex, 1, roles);
+}
+
void ModelNode::dump(ModelNode *node, int ind)
{
QByteArray indentBa(ind * 4, ' ');
@@ -1298,16 +1585,47 @@ void ModelNode::dump(ModelNode *node, int ind)
}
}
-ModelObject::ModelObject()
-: _mo(new QDeclarativeOpenMetaObject(this))
+ModelObject::ModelObject(ModelNode *node, NestedListModel *model, QScriptEngine *seng)
+ : m_model(model),
+ m_node(node),
+ m_meta(new ModelNodeMetaObject(seng, this))
{
}
void ModelObject::setValue(const QByteArray &name, const QVariant &val)
{
- _mo->setValue(name, val);
+ m_meta->setValue(name, val);
setProperty(name.constData(), val);
}
+void ModelObject::setNodeUpdatesEnabled(bool enable)
+{
+ m_meta->m_enabled = enable;
+}
+
+
+ModelNodeMetaObject::ModelNodeMetaObject(QScriptEngine *seng, ModelObject *object)
+ : QDeclarativeOpenMetaObject(object),
+ m_enabled(false),
+ m_seng(seng),
+ m_obj(object)
+{
+}
+
+void ModelNodeMetaObject::propertyWritten(int index)
+{
+ if (!m_enabled)
+ return;
+
+ QString propName = QString::fromUtf8(name(index));
+ QVariant value = operator[](index);
+
+ QScriptValue sv = m_seng->newObject();
+ sv.setProperty(propName, m_seng->newVariant(value));
+ m_obj->m_node->setObjectValue(sv, false);
+
+ m_obj->m_node->changedProperty(propName);
+}
+
QT_END_NAMESPACE