Merge branch 4.7 into qt-4.8-from-4.7
[qt:qt.git] / src / declarative / util / qdeclarativelistmodel.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "private/qdeclarativelistmodel_p_p.h"
43 #include "private/qdeclarativelistmodelworkeragent_p.h"
44 #include "private/qdeclarativeopenmetaobject_p.h"
45
46 #include <qdeclarativecustomparser_p.h>
47 #include <qdeclarativeparser_p.h>
48 #include <qdeclarativeengine_p.h>
49 #include <qdeclarativecontext.h>
50 #include <qdeclarativeinfo.h>
51
52 #include <QtCore/qdebug.h>
53 #include <QtCore/qstack.h>
54 #include <QXmlStreamReader>
55 #include <QtScript/qscriptvalueiterator.h>
56
57 Q_DECLARE_METATYPE(QListModelInterface *)
58
59 QT_BEGIN_NAMESPACE
60
61 template<typename T>
62 void qdeclarativelistmodel_move(int from, int to, int n, T *items)
63 {
64     if (n == 1) {
65         items->move(from, to);
66     } else {
67         T replaced;
68         int i=0;
69         typename T::ConstIterator it=items->begin(); it += from+n;
70         for (; i<to-from; ++i,++it)
71             replaced.append(*it);
72         i=0;
73         it=items->begin(); it += from;
74         for (; i<n; ++i,++it)
75             replaced.append(*it);
76         typename T::ConstIterator f=replaced.begin();
77         typename T::Iterator t=items->begin(); t += from;
78         for (; f != replaced.end(); ++f, ++t)
79             *t = *f;
80     }
81 }
82
83 QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListModelData::instructions() const
84 {
85     return (QDeclarativeListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData));
86 }
87
88 /*!
89     \qmlclass ListModel QDeclarativeListModel
90     \ingroup qml-working-with-data
91     \since 4.7
92     \brief The ListModel element defines a free-form list data source.
93
94     The ListModel is a simple container of ListElement definitions, each containing data roles.
95     The contents can be defined dynamically, or explicitly in QML.
96
97     The number of elements in the model can be obtained from its \l count property.
98     A number of familiar methods are also provided to manipulate the contents of the
99     model, including append(), insert(), move(), remove() and set(). These methods
100     accept dictionaries as their arguments; these are translated to ListElement objects
101     by the model.
102
103     Elements can be manipulated via the model using the setProperty() method, which
104     allows the roles of the specified element to be set and changed.
105
106     \section1 Example Usage
107
108     The following example shows a ListModel containing three elements, with the roles
109     "name" and "cost".
110
111     \div {class="float-right"}
112     \inlineimage listmodel.png
113     \enddiv
114
115     \snippet doc/src/snippets/declarative/listmodel.qml 0
116
117     \clearfloat
118     Roles (properties) in each element must begin with a lower-case letter and
119     should be common to all elements in a model. The ListElement documentation
120     provides more guidelines for how elements should be defined.
121
122     Since the example model contains an \c id property, it can be referenced
123     by views, such as the ListView in this example:
124
125     \snippet doc/src/snippets/declarative/listmodel-simple.qml 0
126     \dots 8
127     \snippet doc/src/snippets/declarative/listmodel-simple.qml 1
128
129     It is possible for roles to contain list data.  In the following example we
130     create a list of fruit attributes:
131
132     \snippet doc/src/snippets/declarative/listmodel-nested.qml model
133
134     The delegate displays all the fruit attributes:
135
136     \div {class="float-right"}
137     \inlineimage listmodel-nested.png
138     \enddiv
139
140     \snippet doc/src/snippets/declarative/listmodel-nested.qml delegate
141
142     \clearfloat
143     \section1 Modifying List Models
144
145     The content of a ListModel may be created and modified using the clear(),
146     append(), set(), insert() and setProperty() methods.  For example:
147
148     \snippet doc/src/snippets/declarative/listmodel-modify.qml delegate
149
150     Note that when creating content dynamically the set of available properties
151     cannot be changed once set. Whatever properties are first added to the model
152     are the only permitted properties in the model.
153
154     \section1 Using Threaded List Models with WorkerScript
155
156     ListModel can be used together with WorkerScript access a list model
157     from multiple threads. This is useful if list modifications are
158     synchronous and take some time: the list operations can be moved to a
159     different thread to avoid blocking of the main GUI thread.
160
161     Here is an example that uses WorkerScript to periodically append the
162     current time to a list model:
163
164     \snippet examples/declarative/threading/threadedlistmodel/timedisplay.qml 0
165
166     The included file, \tt dataloader.js, looks like this:
167
168     \snippet examples/declarative/threading/threadedlistmodel/dataloader.js 0
169
170     The timer in the main example sends messages to the worker script by calling
171     \l WorkerScript::sendMessage(). When this message is received,
172     \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js,
173     which appends the current time to the list model.
174
175     Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()}
176     handler. You must call sync() or else the changes made to the list from the external
177     thread will not be reflected in the list model in the main thread.
178
179     \section1 Restrictions
180
181     If a list model is to be accessed from a WorkerScript, it cannot
182     contain list-type data. So, the following model cannot be used from a WorkerScript
183     because of the list contained in the "attributes" property:
184
185     \code
186     ListModel {
187         id: fruitModel
188         ListElement {
189             name: "Apple"
190             cost: 2.45
191             attributes: [
192                 ListElement { description: "Core" },
193                 ListElement { description: "Deciduous" }
194             ]
195         }
196     }
197     \endcode
198
199     In addition, the WorkerScript cannot add list-type data to the model.
200
201     \sa {qmlmodels}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtDeclarative
202 */
203
204
205 /*
206     A ListModel internally uses either a NestedListModel or FlatListModel.
207
208     A NestedListModel can contain lists of ListElements (which
209     when retrieved from get() is accessible as a list model within the list
210     model) whereas a FlatListModel cannot.
211
212     ListModel uses a NestedListModel to begin with, and if the model is later 
213     used from a WorkerScript, it changes to use a FlatListModel instead. This
214     is because ModelNode (which abstracts the nested list model data) needs
215     access to the declarative engine and script engine, which cannot be
216     safely used from outside of the main thread.
217 */
218
219 QDeclarativeListModel::QDeclarativeListModel(QObject *parent)
220 : QListModelInterface(parent), m_agent(0), m_nested(new NestedListModel(this)), m_flat(0)
221 {
222 }
223
224 QDeclarativeListModel::QDeclarativeListModel(const QDeclarativeListModel *orig, QDeclarativeListModelWorkerAgent *parent)
225 : QListModelInterface(parent), m_agent(0), m_nested(0), m_flat(0)
226 {
227     m_flat = new FlatListModel(this);
228     m_flat->m_parentAgent = parent;
229
230     if (orig->m_flat) {
231         m_flat->m_roles = orig->m_flat->m_roles;
232         m_flat->m_strings = orig->m_flat->m_strings;
233         m_flat->m_values = orig->m_flat->m_values;
234
235         m_flat->m_nodeData.reserve(m_flat->m_values.count());
236         for (int i=0; i<m_flat->m_values.count(); i++)
237             m_flat->m_nodeData << 0;
238     }
239 }
240
241 QDeclarativeListModel::~QDeclarativeListModel()
242 {
243     if (m_agent)
244         m_agent->release();
245
246     delete m_nested;
247     delete m_flat;
248 }
249
250 bool QDeclarativeListModel::flatten()
251 {
252     if (m_flat)
253         return true;
254
255     QList<int> roles = m_nested->roles();
256
257     QList<QHash<int, QVariant> > values;
258     bool hasNested = false;
259     for (int i=0; i<m_nested->count(); i++) {
260         values.append(m_nested->data(i, roles, &hasNested));
261         if (hasNested)
262             return false;
263     }
264
265     FlatListModel *flat = new FlatListModel(this);
266     flat->m_values = values;
267
268     for (int i=0; i<roles.count(); i++) {
269         QString s = m_nested->toString(roles[i]);
270         flat->m_roles.insert(roles[i], s);
271         flat->m_strings.insert(s, roles[i]);
272     }
273
274     flat->m_nodeData.reserve(flat->m_values.count());
275     for (int i=0; i<flat->m_values.count(); i++)
276         flat->m_nodeData << 0;
277
278     m_flat = flat;
279     delete m_nested;
280     m_nested = 0;
281     return true;
282 }
283
284 bool QDeclarativeListModel::inWorkerThread() const
285 {
286     return m_flat && m_flat->m_parentAgent;
287 }
288
289 QDeclarativeListModelWorkerAgent *QDeclarativeListModel::agent()
290 {
291     if (m_agent)
292         return m_agent;
293
294     if (!flatten()) {
295         qmlInfo(this) << "List contains list-type data and cannot be used from a worker script";
296         return 0;
297     }
298
299     m_agent = new QDeclarativeListModelWorkerAgent(this);
300     return m_agent;
301 }
302
303 QList<int> QDeclarativeListModel::roles() const
304 {
305     return m_flat ? m_flat->roles() : m_nested->roles();
306 }
307
308 QString QDeclarativeListModel::toString(int role) const
309 {
310     return m_flat ? m_flat->toString(role) : m_nested->toString(role);
311 }
312
313 QVariant QDeclarativeListModel::data(int index, int role) const
314 {
315     if (index >= count() || index < 0)
316         return QVariant();
317
318     return m_flat ? m_flat->data(index, role) : m_nested->data(index, role);
319 }
320
321 /*!
322     \qmlproperty int ListModel::count
323     The number of data entries in the model.
324 */
325 int QDeclarativeListModel::count() const
326 {
327     return m_flat ? m_flat->count() : m_nested->count();
328 }
329
330 /*!
331     \qmlmethod ListModel::clear()
332
333     Deletes all content from the model.
334
335     \sa append() remove()
336 */
337 void QDeclarativeListModel::clear()
338 {
339     int cleared = count();
340     if (m_flat)
341         m_flat->clear();
342     else
343         m_nested->clear();
344
345     if (!inWorkerThread()) {
346         emit itemsRemoved(0, cleared);
347         emit countChanged();
348     }
349 }
350
351 QDeclarativeListModel *ModelNode::model(const NestedListModel *model)
352 {
353     if (!modelCache) { 
354         modelCache = new QDeclarativeListModel;
355         QDeclarativeEngine::setContextForObject(modelCache,QDeclarativeEngine::contextForObject(model->m_listModel));
356         modelCache->m_nested->_root = this;  // ListModel defaults to nestable model
357
358         for (int i=0; i<values.count(); ++i) {
359             ModelNode *subNode = qvariant_cast<ModelNode *>(values.at(i));
360             if (subNode)
361                 subNode->m_model = modelCache->m_nested;
362         }
363     }
364     return modelCache;
365 }
366
367 ModelObject *ModelNode::object(const NestedListModel *model)
368 {
369     if (!objectCache) {
370         objectCache = new ModelObject(this, 
371                 const_cast<NestedListModel*>(model), 
372                 QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(model->m_listModel)));
373         QHash<QString, ModelNode *>::iterator it;
374         for (it = properties.begin(); it != properties.end(); ++it) {
375             objectCache->setValue(it.key().toUtf8(), model->valueForNode(*it));
376         }
377         objectCache->setNodeUpdatesEnabled(true);
378     }
379     return objectCache;
380 }
381
382 /*!
383     \qmlmethod ListModel::remove(int index)
384
385     Deletes the content at \a index from the model.
386
387     \sa clear()
388 */
389 void QDeclarativeListModel::remove(int index)
390 {
391     if (index < 0 || index >= count()) {
392         qmlInfo(this) << tr("remove: index %1 out of range").arg(index);
393         return;
394     }
395
396     if (m_flat)
397         m_flat->remove(index);
398     else
399         m_nested->remove(index);
400
401     if (!inWorkerThread()) {
402         emit itemsRemoved(index, 1);
403         emit countChanged();
404     }
405 }
406
407 /*!
408     \qmlmethod ListModel::insert(int index, jsobject dict)
409
410     Adds a new item to the list model at position \a index, with the
411     values in \a dict.
412
413     \code
414         fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"})
415     \endcode
416
417     The \a index must be to an existing item in the list, or one past
418     the end of the list (equivalent to append).
419
420     \sa set() append()
421 */
422 void QDeclarativeListModel::insert(int index, const QScriptValue& valuemap)
423 {
424     if (!valuemap.isObject() || valuemap.isArray()) {
425         qmlInfo(this) << tr("insert: value is not an object");
426         return;
427     }
428
429     if (index < 0 || index > count()) {
430         qmlInfo(this) << tr("insert: index %1 out of range").arg(index);
431         return;
432     }
433
434     bool ok = m_flat ?  m_flat->insert(index, valuemap) : m_nested->insert(index, valuemap);
435     if (ok && !inWorkerThread()) {
436         emit itemsInserted(index, 1);
437         emit countChanged();
438     }
439 }
440
441 /*!
442     \qmlmethod ListModel::move(int from, int to, int n)
443
444     Moves \a n items \a from one position \a to another.
445
446     The from and to ranges must exist; for example, to move the first 3 items
447     to the end of the list:
448
449     \code
450         fruitModel.move(0, fruitModel.count - 3, 3)
451     \endcode
452
453     \sa append()
454 */
455 void QDeclarativeListModel::move(int from, int to, int n)
456 {
457     if (n==0 || from==to)
458         return;
459     if (!canMove(from, to, n)) {
460         qmlInfo(this) << tr("move: out of range");
461         return;
462     }
463
464     int origfrom = from;
465     int origto = to;
466     int orign = n;
467     if (from > to) {
468         // Only move forwards - flip if backwards moving
469         int tfrom = from;
470         int tto = to;
471         from = tto;
472         to = tto+n;
473         n = tfrom-tto;
474     }
475
476     if (m_flat)
477         m_flat->move(from, to, n);
478     else
479         m_nested->move(from, to, n);
480
481     if (!inWorkerThread())
482         emit itemsMoved(origfrom, origto, orign);
483 }
484
485 /*!
486     \qmlmethod ListModel::append(jsobject dict)
487
488     Adds a new item to the end of the list model, with the
489     values in \a dict.
490
491     \code
492         fruitModel.append({"cost": 5.95, "name":"Pizza"})
493     \endcode
494
495     \sa set() remove()
496 */
497 void QDeclarativeListModel::append(const QScriptValue& valuemap)
498 {
499     if (!valuemap.isObject() || valuemap.isArray()) {
500         qmlInfo(this) << tr("append: value is not an object");
501         return;
502     }
503
504     insert(count(), valuemap);
505 }
506
507 /*!
508     \qmlmethod object ListModel::get(int index)
509
510     Returns the item at \a index in the list model. This allows the item
511     data to be accessed or modified from JavaScript:
512
513     \code
514     Component.onCompleted: {
515         fruitModel.append({"cost": 5.95, "name":"Jackfruit"});
516         console.log(fruitModel.get(0).cost);
517         fruitModel.get(0).cost = 10.95;
518     }
519     \endcode
520
521     The \a index must be an element in the list.
522
523     Note that properties of the returned object that are themselves objects
524     will also be models, and this get() method is used to access elements:
525
526     \code
527         fruitModel.append(..., "attributes":
528             [{"name":"spikes","value":"7mm"},
529              {"name":"color","value":"green"}]);
530         fruitModel.get(0).attributes.get(1).value; // == "green"
531     \endcode
532
533     \warning The returned object is not guaranteed to remain valid. It
534     should not be used in \l{Property Binding}{property bindings}.
535
536     \sa append()
537 */
538 QScriptValue QDeclarativeListModel::get(int index) const
539 {
540     // the internal flat/nested class checks for bad index
541     return m_flat ? m_flat->get(index) : m_nested->get(index);
542 }
543
544 /*!
545     \qmlmethod ListModel::set(int index, jsobject dict)
546
547     Changes the item at \a index in the list model with the
548     values in \a dict. Properties not appearing in \a dict
549     are left unchanged.
550
551     \code
552         fruitModel.set(3, {"cost": 5.95, "name":"Pizza"})
553     \endcode
554
555     If \a index is equal to count() then a new item is appended to the
556     list. Otherwise, \a index must be an element in the list.
557
558     \sa append()
559 */
560 void QDeclarativeListModel::set(int index, const QScriptValue& valuemap)
561 {
562     QList<int> roles;
563     set(index, valuemap, &roles);
564     if (!roles.isEmpty() && !inWorkerThread())
565         emit itemsChanged(index, 1, roles);
566 }
567
568 void QDeclarativeListModel::set(int index, const QScriptValue& valuemap, QList<int> *roles)
569 {
570     if (!valuemap.isObject() || valuemap.isArray()) {
571         qmlInfo(this) << tr("set: value is not an object");
572         return;
573     }
574     if (index > count() || index < 0) {
575         qmlInfo(this) << tr("set: index %1 out of range").arg(index);
576         return;
577     }
578
579     if (index == count()) {
580         append(valuemap);
581     } else {
582         if (m_flat)
583             m_flat->set(index, valuemap, roles);
584         else
585             m_nested->set(index, valuemap, roles);
586     }
587 }
588
589 /*!
590     \qmlmethod ListModel::setProperty(int index, string property, variant value)
591
592     Changes the \a property of the item at \a index in the list model to \a value.
593
594     \code
595         fruitModel.setProperty(3, "cost", 5.95)
596     \endcode
597
598     The \a index must be an element in the list.
599
600     \sa append()
601 */
602 void QDeclarativeListModel::setProperty(int index, const QString& property, const QVariant& value)
603 {
604     QList<int> roles;
605     setProperty(index, property, value, &roles);
606     if (!roles.isEmpty() && !inWorkerThread())
607         emit itemsChanged(index, 1, roles);
608 }
609
610 void QDeclarativeListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles)
611 {
612     if (count() == 0 || index >= count() || index < 0) {
613         qmlInfo(this) << tr("set: index %1 out of range").arg(index);
614         return;
615     }
616
617     if (m_flat)
618         m_flat->setProperty(index, property, value, roles);
619     else
620         m_nested->setProperty(index, property, value, roles);
621 }
622
623 /*!
624     \qmlmethod ListModel::sync()
625
626     Writes any unsaved changes to the list model after it has been modified
627     from a worker script.
628 */
629 void QDeclarativeListModel::sync()
630 {
631     // This is just a dummy method to make it look like sync() exists in
632     // ListModel (and not just QDeclarativeListModelWorkerAgent) and to let
633     // us document sync().
634     qmlInfo(this) << "List sync() can only be called from a WorkerScript";
635 }
636
637 bool QDeclarativeListModelParser::compileProperty(const QDeclarativeCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data)
638 {
639     QList<QVariant> values = prop.assignedValues();
640     for(int ii = 0; ii < values.count(); ++ii) {
641         const QVariant &value = values.at(ii);
642
643         if(value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) {
644             QDeclarativeCustomParserNode node =
645                 qvariant_cast<QDeclarativeCustomParserNode>(value);
646
647             if (node.name() != listElementTypeName) {
648                 const QMetaObject *mo = resolveType(node.name());
649                 if (mo != &QDeclarativeListElement::staticMetaObject) {
650                     error(node, QDeclarativeListModel::tr("ListElement: cannot contain nested elements"));
651                     return false;
652                 }
653                 listElementTypeName = node.name(); // cache right name for next time
654             }
655
656             {
657             ListInstruction li;
658             li.type = ListInstruction::Push;
659             li.dataIdx = -1;
660             instr << li;
661             }
662
663             QList<QDeclarativeCustomParserProperty> props = node.properties();
664             for(int jj = 0; jj < props.count(); ++jj) {
665                 const QDeclarativeCustomParserProperty &nodeProp = props.at(jj);
666                 if (nodeProp.name().isEmpty()) {
667                     error(nodeProp, QDeclarativeListModel::tr("ListElement: cannot contain nested elements"));
668                     return false;
669                 }
670                 if (nodeProp.name() == "id") {
671                     error(nodeProp, QDeclarativeListModel::tr("ListElement: cannot use reserved \"id\" property"));
672                     return false;
673                 }
674
675                 ListInstruction li;
676                 int ref = data.count();
677                 data.append(nodeProp.name());
678                 data.append('\0');
679                 li.type = ListInstruction::Set;
680                 li.dataIdx = ref;
681                 instr << li;
682
683                 if(!compileProperty(nodeProp, instr, data))
684                     return false;
685
686                 li.type = ListInstruction::Pop;
687                 li.dataIdx = -1;
688                 instr << li;
689             }
690
691             {
692             ListInstruction li;
693             li.type = ListInstruction::Pop;
694             li.dataIdx = -1;
695             instr << li;
696             }
697
698         } else {
699
700             QDeclarativeParser::Variant variant =
701                 qvariant_cast<QDeclarativeParser::Variant>(value);
702
703             int ref = data.count();
704
705             QByteArray d;
706             d += char(variant.type()); // type tag
707             if (variant.isString()) {
708                 d += variant.asString().toUtf8();
709             } else if (variant.isNumber()) {
710                 d += QByteArray::number(variant.asNumber(),'g',20);
711             } else if (variant.isBoolean()) {
712                 d += char(variant.asBoolean());
713             } else if (variant.isScript()) {
714                 if (definesEmptyList(variant.asScript())) {
715                     d[0] = char(QDeclarativeParser::Variant::Invalid); // marks empty list
716                 } else {
717                     QByteArray script = variant.asScript().toUtf8();
718                     int v = evaluateEnum(script);
719                     if (v<0) {
720                         if (script.startsWith("QT_TR_NOOP(\"") && script.endsWith("\")")) {
721                             d[0] = char(QDeclarativeParser::Variant::String);
722                             d += script.mid(12,script.length()-14);
723                         } else {
724                             error(prop, QDeclarativeListModel::tr("ListElement: cannot use script for property value"));
725                             return false;
726                         }
727                     } else {
728                         d[0] = char(QDeclarativeParser::Variant::Number);
729                         d += QByteArray::number(v);
730                     }
731                 }
732             }
733             d.append('\0');
734             data.append(d);
735
736             ListInstruction li;
737             li.type = ListInstruction::Value;
738             li.dataIdx = ref;
739             instr << li;
740         }
741     }
742
743     return true;
744 }
745
746 QByteArray QDeclarativeListModelParser::compile(const QList<QDeclarativeCustomParserProperty> &customProps)
747 {
748     QList<ListInstruction> instr;
749     QByteArray data;
750     listElementTypeName = QByteArray(); // unknown
751
752     for(int ii = 0; ii < customProps.count(); ++ii) {
753         const QDeclarativeCustomParserProperty &prop = customProps.at(ii);
754         if(!prop.name().isEmpty()) { // isn't default property
755             error(prop, QDeclarativeListModel::tr("ListModel: undefined property '%1'").arg(QString::fromUtf8(prop.name())));
756             return QByteArray();
757         }
758
759         if(!compileProperty(prop, instr, data)) {
760             return QByteArray();
761         }
762     }
763
764     int size = sizeof(ListModelData) +
765                instr.count() * sizeof(ListInstruction) +
766                data.count();
767
768     QByteArray rv;
769     rv.resize(size);
770
771     ListModelData *lmd = (ListModelData *)rv.data();
772     lmd->dataOffset = sizeof(ListModelData) +
773                      instr.count() * sizeof(ListInstruction);
774     lmd->instrCount = instr.count();
775     for (int ii = 0; ii < instr.count(); ++ii)
776         lmd->instructions()[ii] = instr.at(ii);
777     ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count());
778
779     return rv;
780 }
781
782 void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray &d)
783 {
784     QDeclarativeListModel *rv = static_cast<QDeclarativeListModel *>(obj);
785
786     ModelNode *root = new ModelNode(rv->m_nested);
787     rv->m_nested->m_ownsRoot = true;
788     rv->m_nested->_root = root;
789     QStack<ModelNode *> nodes;
790     nodes << root;
791
792     bool processingSet = false;
793
794     const ListModelData *lmd = (const ListModelData *)d.constData();
795     const char *data = ((const char *)lmd) + lmd->dataOffset;
796
797     for (int ii = 0; ii < lmd->instrCount; ++ii) {
798         const ListInstruction &instr = lmd->instructions()[ii];
799
800         switch(instr.type) {
801         case ListInstruction::Push:
802             {
803                 ModelNode *n = nodes.top();
804                 ModelNode *n2 = new ModelNode(rv->m_nested);
805                 n->values << QVariant::fromValue(n2);
806                 nodes.push(n2);
807                 if (processingSet)
808                     n->isArray = true;
809             }
810             break;
811
812         case ListInstruction::Pop:
813             nodes.pop();
814             break;
815
816         case ListInstruction::Value:
817             {
818                 ModelNode *n = nodes.top();
819                 switch (QDeclarativeParser::Variant::Type(data[instr.dataIdx])) {
820                  case QDeclarativeParser::Variant::Invalid:
821                     n->isArray = true;
822                     break;
823                  case QDeclarativeParser::Variant::Boolean:
824                     n->values.append(bool(data[1 + instr.dataIdx]));
825                     break;
826                  case QDeclarativeParser::Variant::Number:
827                     n->values.append(QByteArray(data + 1 + instr.dataIdx).toDouble());
828                     break;
829                  case QDeclarativeParser::Variant::String:
830                     n->values.append(QString::fromUtf8(data + 1 + instr.dataIdx));
831                     break;
832                  default:
833                     Q_ASSERT("Format error in ListInstruction");
834                 }
835
836                 processingSet = false;
837             }
838             break;
839
840         case ListInstruction::Set:
841             {
842                 ModelNode *n = nodes.top();
843                 ModelNode *n2 = new ModelNode(rv->m_nested);
844                 n->properties.insert(QString::fromUtf8(data + instr.dataIdx), n2);
845                 nodes.push(n2);
846                 processingSet = true;
847             }
848             break;
849         }
850     }
851
852     ModelNode *rootNode = rv->m_nested->_root;
853     for (int i=0; i<rootNode->values.count(); ++i) {
854         ModelNode *node = qvariant_cast<ModelNode *>(rootNode->values[i]);
855         node->listIndex = i;
856         node->updateListIndexes();
857     }
858 }
859
860 bool QDeclarativeListModelParser::definesEmptyList(const QString &s)
861 {
862     if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) {
863         for (int i=1; i<s.length()-1; i++) {
864             if (!s[i].isSpace())
865                 return false;
866         }
867         return true;
868     }
869     return false;
870 }
871
872
873 /*!
874     \qmlclass ListElement QDeclarativeListElement
875     \ingroup qml-working-with-data
876     \since 4.7
877     \brief The ListElement element defines a data item in a ListModel.
878
879     List elements are defined inside ListModel definitions, and represent items in a
880     list that will be displayed using ListView or \l Repeater items.
881
882     List elements are defined like other QML elements except that they contain
883     a collection of \e role definitions instead of properties. Using the same
884     syntax as property definitions, roles both define how the data is accessed
885     and include the data itself.
886
887     The names used for roles must begin with a lower-case letter and should be
888     common to all elements in a given model. Values must be simple constants; either
889     strings (quoted and optionally within a call to QT_TR_NOOP), boolean values
890     (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter).
891
892     \section1 Referencing Roles
893
894     The role names are used by delegates to obtain data from list elements.
895     Each role name is accessible in the delegate's scope, and refers to the
896     corresponding role in the current element. Where a role name would be
897     ambiguous to use, it can be accessed via the \l{ListView::}{model}
898     property (e.g., \c{model.cost} instead of \c{cost}).
899
900     \section1 Example Usage
901
902     The following model defines a series of list elements, each of which
903     contain "name" and "cost" roles and their associated values.
904
905     \snippet doc/src/snippets/declarative/qml-data-models/listelements.qml model
906
907     The delegate obtains the name and cost for each element by simply referring
908     to \c name and \c cost:
909
910     \snippet doc/src/snippets/declarative/qml-data-models/listelements.qml view
911
912     \sa ListModel
913 */
914
915 FlatListModel::FlatListModel(QDeclarativeListModel *base)
916     : m_scriptEngine(0), m_listModel(base), m_scriptClass(0), m_parentAgent(0)
917 {
918 }
919
920 FlatListModel::~FlatListModel()
921 {
922     qDeleteAll(m_nodeData);
923 }
924
925 QVariant FlatListModel::data(int index, int role) const
926 {
927     Q_ASSERT(index >= 0 && index < m_values.count());
928     if (m_values[index].contains(role))
929         return m_values[index][role];
930     return QVariant();
931 }
932
933 QList<int> FlatListModel::roles() const
934 {
935     return m_roles.keys();
936 }
937
938 QString FlatListModel::toString(int role) const
939 {
940     if (m_roles.contains(role))
941         return m_roles[role];
942     return QString();
943 }
944
945 int FlatListModel::count() const
946 {
947     return m_values.count();
948 }
949
950 void FlatListModel::clear()
951 {
952     m_values.clear();
953
954     qDeleteAll(m_nodeData);
955     m_nodeData.clear();
956 }
957
958 void FlatListModel::remove(int index)
959 {
960     m_values.removeAt(index);
961     removedNode(index);
962 }
963
964 bool FlatListModel::insert(int index, const QScriptValue &value)
965 {
966     Q_ASSERT(index >= 0 && index <= m_values.count());
967
968     QHash<int, QVariant> row;
969     if (!addValue(value, &row, 0))
970         return false;
971
972     m_values.insert(index, row);
973     insertedNode(index);
974
975     return true;
976 }
977
978 QScriptValue FlatListModel::get(int index) const
979 {
980     QScriptEngine *scriptEngine = m_scriptEngine ? m_scriptEngine : QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(m_listModel));
981
982     if (!scriptEngine) 
983         return 0;
984
985     if (index < 0 || index >= m_values.count())
986         return scriptEngine->undefinedValue();
987
988     FlatListModel *that = const_cast<FlatListModel*>(this);
989     if (!m_scriptClass)
990         that->m_scriptClass = new FlatListScriptClass(that, scriptEngine);
991
992     FlatNodeData *data = m_nodeData.value(index);
993     if (!data) {
994         data = new FlatNodeData(index);
995         that->m_nodeData.replace(index, data);
996     }
997
998     return QScriptDeclarativeClass::newObject(scriptEngine, m_scriptClass, new FlatNodeObjectData(data));
999 }
1000
1001 void FlatListModel::set(int index, const QScriptValue &value, QList<int> *roles)
1002 {
1003     Q_ASSERT(index >= 0 && index < m_values.count());
1004
1005     QHash<int, QVariant> row = m_values[index];
1006     if (addValue(value, &row, roles))
1007         m_values[index] = row;
1008 }
1009
1010 void FlatListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles)
1011 {
1012     Q_ASSERT(index >= 0 && index < m_values.count());
1013
1014     QHash<QString, int>::Iterator iter = m_strings.find(property);
1015     int role;
1016     if (iter == m_strings.end()) {
1017         role = m_roles.count();
1018         m_roles.insert(role, property);
1019         m_strings.insert(property, role);
1020     } else {
1021         role = iter.value();
1022     }
1023
1024     if (m_values[index][role] != value) {
1025         roles->append(role);
1026         m_values[index][role] = value;
1027     }
1028 }
1029
1030 void FlatListModel::move(int from, int to, int n)
1031 {
1032     qdeclarativelistmodel_move<QList<QHash<int, QVariant> > >(from, to, n, &m_values);
1033     moveNodes(from, to, n);
1034 }
1035
1036 bool FlatListModel::addValue(const QScriptValue &value, QHash<int, QVariant> *row, QList<int> *roles)
1037 {
1038     QScriptValueIterator it(value);
1039     while (it.hasNext()) {
1040         it.next();
1041         QScriptValue value = it.value();
1042         if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) {
1043             qmlInfo(m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script";
1044             return false;
1045         }
1046
1047         QString name = it.name();
1048         QVariant v = it.value().toVariant();
1049
1050         QHash<QString, int>::Iterator iter = m_strings.find(name);
1051         if (iter == m_strings.end()) {
1052             int role = m_roles.count();
1053             m_roles.insert(role, name);
1054             iter = m_strings.insert(name, role);
1055             if (roles)
1056                 roles->append(role);
1057         } else {
1058             int role = iter.value();
1059             if (roles && row->contains(role) && row->value(role) != v)
1060                 roles->append(role);
1061         }
1062         row->insert(*iter, v);
1063     }
1064     return true;
1065 }
1066
1067 void FlatListModel::insertedNode(int index)
1068 {
1069     if (index >= 0 && index <= m_values.count()) {
1070         m_nodeData.insert(index, 0);
1071
1072         for (int i=index + 1; i<m_nodeData.count(); i++) {
1073             if (m_nodeData[i])
1074                 m_nodeData[i]->index = i;
1075         }
1076     }
1077 }
1078
1079 void FlatListModel::removedNode(int index)
1080 {
1081     if (index >= 0 && index < m_nodeData.count()) {
1082         delete m_nodeData.takeAt(index);
1083
1084         for (int i=index; i<m_nodeData.count(); i++) {
1085             if (m_nodeData[i])
1086                 m_nodeData[i]->index = i;
1087         }
1088     }
1089 }
1090
1091 void FlatListModel::moveNodes(int from, int to, int n)
1092 {
1093     if (!m_listModel->canMove(from, to, n))
1094         return;
1095
1096     qdeclarativelistmodel_move<QList<FlatNodeData *> >(from, to, n, &m_nodeData);
1097
1098     for (int i=from; i<from + (to-from); i++)  {
1099         if (m_nodeData[i]) 
1100             m_nodeData[i]->index = i;
1101     }
1102 }
1103
1104
1105
1106 FlatNodeData::~FlatNodeData()
1107 {
1108     for (QSet<FlatNodeObjectData *>::Iterator iter = objects.begin(); iter != objects.end(); ++iter) {
1109         FlatNodeObjectData *data = *iter;
1110         data->nodeData = 0;
1111     }
1112 }
1113
1114 void FlatNodeData::addData(FlatNodeObjectData *data) 
1115 {
1116     objects.insert(data);
1117 }
1118
1119 void FlatNodeData::removeData(FlatNodeObjectData *data)
1120 {
1121     objects.remove(data);
1122 }
1123
1124
1125 FlatListScriptClass::FlatListScriptClass(FlatListModel *model, QScriptEngine *seng)
1126     : QScriptDeclarativeClass(seng),
1127       m_model(model)
1128 {
1129 }
1130
1131 QScriptDeclarativeClass::Value FlatListScriptClass::property(Object *obj, const Identifier &name)
1132 {
1133     FlatNodeObjectData *objData = static_cast<FlatNodeObjectData*>(obj);
1134     if (!objData->nodeData) // item at this index has been deleted
1135         return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue());
1136
1137     int index = objData->nodeData->index;
1138     QString propName = toString(name);
1139     int role = m_model->m_strings.value(propName, -1);
1140
1141     if (role >= 0 && index >=0 ) {
1142         const QHash<int, QVariant> &row = m_model->m_values[index];
1143         QScriptValue sv = engine()->toScriptValue<QVariant>(row[role]);
1144         return QScriptDeclarativeClass::Value(engine(), sv);
1145     }
1146
1147     return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue());
1148 }
1149
1150 void FlatListScriptClass::setProperty(Object *obj, const Identifier &name, const QScriptValue &value)
1151 {
1152     if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) {
1153         qmlInfo(m_model->m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script";
1154         return;
1155     }
1156
1157     FlatNodeObjectData *objData = static_cast<FlatNodeObjectData*>(obj);
1158     if (!objData->nodeData) // item at this index has been deleted
1159         return;
1160
1161     int index = objData->nodeData->index;
1162     QString propName = toString(name);
1163
1164     int role = m_model->m_strings.value(propName, -1);
1165     if (role >= 0 && index >= 0) {
1166         QHash<int, QVariant> &row = m_model->m_values[index];
1167         row[role] = value.toVariant();
1168
1169         QList<int> roles;
1170         roles << role;
1171         if (m_model->m_parentAgent) {
1172             // This is the list in the worker thread, so tell the agent to
1173             // emit itemsChanged() later
1174             m_model->m_parentAgent->changedData(index, 1, roles);
1175         } else {
1176             // This is the list in the main thread, so emit itemsChanged()
1177             emit m_model->m_listModel->itemsChanged(index, 1, roles);
1178         }
1179     }
1180 }
1181
1182 QScriptClass::QueryFlags FlatListScriptClass::queryProperty(Object *, const Identifier &, QScriptClass::QueryFlags)
1183 {
1184     return (QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess);
1185 }
1186
1187 bool FlatListScriptClass::compare(Object *obj1, Object *obj2)
1188 {
1189     FlatNodeObjectData *data1 = static_cast<FlatNodeObjectData*>(obj1);
1190     FlatNodeObjectData *data2 = static_cast<FlatNodeObjectData*>(obj2);
1191
1192     if (!data1->nodeData || !data2->nodeData)
1193         return false;
1194
1195     return data1->nodeData->index == data2->nodeData->index;
1196 }
1197
1198
1199
1200 NestedListModel::NestedListModel(QDeclarativeListModel *base)
1201     : _root(0), m_ownsRoot(false), m_listModel(base), _rolesOk(false)
1202 {
1203 }
1204
1205 NestedListModel::~NestedListModel()
1206 {
1207     if (m_ownsRoot)
1208         delete _root;
1209 }
1210
1211 QVariant NestedListModel::valueForNode(ModelNode *node, bool *hasNested) const
1212 {
1213     QObject *rv = 0;
1214     if (hasNested)
1215         *hasNested = false;
1216
1217     if (node->isArray) {
1218         // List
1219         rv = node->model(this);
1220         if (hasNested)
1221             *hasNested = true;
1222     } else {
1223         if (!node->properties.isEmpty()) {
1224             // Object
1225             rv = node->object(this);
1226         } else if (node->values.count() == 0) {
1227             // Invalid
1228             return QVariant();
1229         } else if (node->values.count() == 1) {
1230             // Value
1231             QVariant &var = node->values[0];
1232             ModelNode *valueNode = qvariant_cast<ModelNode *>(var);
1233             if (valueNode) {
1234                 if (!valueNode->properties.isEmpty())
1235                     rv = valueNode->object(this);
1236                 else
1237                     rv = valueNode->model(this);
1238             } else {
1239                 return var;
1240             }
1241         }
1242     }
1243
1244     if (rv) {
1245         return QVariant::fromValue(rv);
1246     } else {
1247         return QVariant();
1248     }
1249 }
1250
1251 QHash<int,QVariant> NestedListModel::data(int index, const QList<int> &roles, bool *hasNested) const
1252 {
1253     Q_ASSERT(_root && index >= 0 && index < _root->values.count());
1254     checkRoles();
1255     QHash<int, QVariant> rv;
1256
1257     ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1258     if (!node)
1259         return rv;
1260
1261     for (int ii = 0; ii < roles.count(); ++ii) {
1262         const QString &roleString = roleStrings.at(roles.at(ii));
1263
1264         QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString);
1265         if (iter != node->properties.end()) {
1266             ModelNode *row = *iter;
1267             rv.insert(roles.at(ii), valueForNode(row, hasNested));
1268         }
1269     }
1270
1271     return rv;
1272 }
1273
1274 QVariant NestedListModel::data(int index, int role) const
1275 {
1276     Q_ASSERT(_root && index >= 0 && index < _root->values.count());
1277     checkRoles();
1278     QVariant rv;
1279     if (roleStrings.count() < role)
1280         return rv;
1281
1282     ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1283     if (!node)
1284         return rv;
1285
1286     const QString &roleString = roleStrings.at(role);
1287
1288     QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString);
1289     if (iter != node->properties.end()) {
1290         ModelNode *row = *iter;
1291         rv = valueForNode(row);
1292     }
1293
1294     return rv;
1295 }
1296
1297 int NestedListModel::count() const
1298 {
1299     if (!_root) return 0;
1300     return _root->values.count();
1301 }
1302
1303 void NestedListModel::clear()
1304 {
1305     if (_root)
1306         _root->clear();
1307 }
1308
1309 void NestedListModel::remove(int index)
1310 {
1311     if (!_root)
1312         return;
1313     ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1314     _root->values.removeAt(index);
1315     if (node)
1316         delete node;
1317 }
1318
1319 bool NestedListModel::insert(int index, const QScriptValue& valuemap)
1320 {
1321     if (!_root) {
1322         _root = new ModelNode(this);
1323         m_ownsRoot = true;
1324     }
1325
1326     ModelNode *mn = new ModelNode(this);
1327     mn->listIndex = index;
1328     mn->setObjectValue(valuemap);
1329     _root->values.insert(index,QVariant::fromValue(mn));
1330     return true;
1331 }
1332
1333 void NestedListModel::move(int from, int to, int n)
1334 {
1335     if (!_root)
1336         return;
1337     qdeclarativelistmodel_move<QVariantList>(from, to, n, &_root->values);
1338 }
1339
1340 QScriptValue NestedListModel::get(int index) const
1341 {   
1342     QDeclarativeEngine *eng = qmlEngine(m_listModel);
1343     if (!eng) 
1344         return 0;
1345
1346     if (index < 0 || index >= count()) {
1347         QScriptEngine *seng = QDeclarativeEnginePrivate::getScriptEngine(eng);
1348         if (seng)
1349             return seng->undefinedValue();
1350         return 0;
1351     }
1352
1353     ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1354     if (!node)
1355         return 0;
1356     
1357     return QDeclarativeEnginePrivate::qmlScriptObject(node->object(this), eng);
1358 }
1359
1360 void NestedListModel::set(int index, const QScriptValue& valuemap, QList<int> *roles)
1361 {
1362     Q_ASSERT(index >=0 && index < count());
1363
1364     ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1365     bool emitItemsChanged = node->setObjectValue(valuemap);
1366     if (!emitItemsChanged)
1367         return;
1368
1369     QScriptValueIterator it(valuemap);
1370     while (it.hasNext()) {
1371         it.next();
1372         int r = roleStrings.indexOf(it.name());
1373         if (r < 0) {
1374             r = roleStrings.count();
1375             roleStrings << it.name();
1376         }
1377         roles->append(r);
1378     }
1379 }
1380
1381 void NestedListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles)
1382 {
1383     Q_ASSERT(index >=0 && index < count());
1384
1385     ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1386     bool emitItemsChanged = node->setProperty(property, value);
1387     if (!emitItemsChanged)
1388         return;
1389
1390     int r = roleStrings.indexOf(property);
1391     if (r < 0) {
1392         r = roleStrings.count();
1393         roleStrings << property;
1394     }
1395     roles->append(r);
1396 }
1397
1398 void NestedListModel::checkRoles() const
1399 {
1400     if (_rolesOk || !_root)
1401         return;
1402
1403     for (int i = 0; i<_root->values.count(); ++i) {
1404         ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(i));
1405         if (node) {
1406             foreach (const QString &role, node->properties.keys()) {
1407                 if (!roleStrings.contains(role))
1408                     roleStrings.append(role);
1409             }
1410         }
1411     }
1412
1413     _rolesOk = true;
1414 }
1415
1416 QList<int> NestedListModel::roles() const
1417 {
1418     checkRoles();
1419     QList<int> rv;
1420     for (int ii = 0; ii < roleStrings.count(); ++ii)
1421         rv << ii;
1422     return rv;
1423 }
1424
1425 QString NestedListModel::toString(int role) const
1426 {
1427     checkRoles();
1428     if (role < roleStrings.count())
1429         return roleStrings.at(role);
1430     else
1431         return QString();
1432 }
1433
1434
1435 ModelNode::ModelNode(NestedListModel *model)
1436 : modelCache(0), objectCache(0), isArray(false), m_model(model), listIndex(-1)
1437 {
1438 }
1439
1440 ModelNode::~ModelNode()
1441 {
1442     clear();
1443     if (modelCache) { modelCache->m_nested->_root = 0/* ==this */; delete modelCache; modelCache = 0; }
1444     if (objectCache) { delete objectCache; objectCache = 0; }
1445 }
1446
1447 void ModelNode::clear()
1448 {
1449     ModelNode *node;
1450     for (int ii = 0; ii < values.count(); ++ii) {
1451         node = qvariant_cast<ModelNode *>(values.at(ii));
1452         if (node) { delete node; node = 0; }
1453     }
1454     values.clear();
1455
1456     qDeleteAll(properties.values());
1457     properties.clear();
1458 }
1459
1460 bool ModelNode::setObjectValue(const QScriptValue& valuemap, bool writeToCache)
1461 {
1462     bool emitItemsChanged = false;
1463
1464     QScriptValueIterator it(valuemap);
1465     while (it.hasNext()) {
1466         it.next();
1467         ModelNode *prev = properties.value(it.name());
1468         ModelNode *value = new ModelNode(m_model);
1469         QScriptValue v = it.value();
1470
1471         if (v.isArray()) {
1472             value->isArray = true;
1473             value->setListValue(v);
1474             if (writeToCache && objectCache)
1475                 objectCache->setValue(it.name().toUtf8(), QVariant::fromValue(value->model(m_model)));
1476             emitItemsChanged = true;    // for now, too inefficient to check whether list and sublists have changed
1477         } else {
1478             value->values << v.toVariant();
1479             if (writeToCache && objectCache)
1480                 objectCache->setValue(it.name().toUtf8(), value->values.last());
1481             if (!emitItemsChanged && prev && prev->values.count() == 1
1482                     && prev->values[0] != value->values.last()) {
1483                 emitItemsChanged = true;
1484             }
1485         }
1486         if (properties.contains(it.name()))
1487             delete properties[it.name()];
1488         properties.insert(it.name(), value);
1489     }
1490     return emitItemsChanged;
1491 }
1492
1493 void ModelNode::setListValue(const QScriptValue& valuelist) {
1494     values.clear();
1495     int size = valuelist.property(QLatin1String("length")).toInt32();
1496     for (int i=0; i<size; i++) {
1497         ModelNode *value = new ModelNode(m_model);
1498         QScriptValue v = valuelist.property(i);
1499         if (v.isArray()) {
1500             value->isArray = true;
1501             value->setListValue(v);
1502         } else if (v.isObject()) {
1503             value->listIndex = i;
1504             value->setObjectValue(v);
1505         } else {
1506             value->listIndex = i;
1507             value->values << v.toVariant();
1508         }
1509         values.append(QVariant::fromValue(value));
1510     }
1511 }
1512
1513 bool ModelNode::setProperty(const QString& prop, const QVariant& val) {
1514     QHash<QString, ModelNode *>::const_iterator it = properties.find(prop);
1515     bool emitItemsChanged = false;
1516     if (it != properties.end()) {
1517         if (val != (*it)->values[0])
1518             emitItemsChanged = true;
1519         (*it)->values[0] = val;
1520     } else {
1521         ModelNode *n = new ModelNode(m_model);
1522         n->values << val;
1523         properties.insert(prop,n);
1524     }
1525     if (objectCache)
1526         objectCache->setValue(prop.toUtf8(), val);
1527     return emitItemsChanged;
1528 }
1529
1530 void ModelNode::updateListIndexes()
1531 {
1532     for (QHash<QString, ModelNode *>::ConstIterator iter = properties.begin(); iter != properties.end(); ++iter) {
1533         ModelNode *node = iter.value();
1534         if (node->isArray) {
1535             for (int i=0; i<node->values.count(); ++i) {
1536                 ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(i));
1537                 if (subNode)
1538                     subNode->listIndex = i;
1539             }
1540         }
1541         node->updateListIndexes();
1542     }
1543 }
1544
1545 /*
1546     Need to call this to emit itemsChanged() for modifications outside of set()
1547     and setProperty(), i.e. if an item returned from get() is modified
1548 */
1549 void ModelNode::changedProperty(const QString &name) const
1550 {
1551     if (listIndex < 0)
1552         return;
1553
1554     m_model->checkRoles();
1555     QList<int> roles;
1556     int role = m_model->roleStrings.indexOf(name);
1557     if (role < 0)
1558         roles = m_model->roles();
1559     else
1560         roles << role;
1561     emit m_model->m_listModel->itemsChanged(listIndex, 1, roles);
1562 }
1563
1564 void ModelNode::dump(ModelNode *node, int ind)
1565 {
1566     QByteArray indentBa(ind * 4, ' ');
1567     const char *indent = indentBa.constData();
1568
1569     for (int ii = 0; ii < node->values.count(); ++ii) {
1570         ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(ii));
1571         if (subNode) {
1572             qWarning().nospace() << indent << "Sub-node " << ii;
1573             dump(subNode, ind + 1);
1574         } else {
1575             qWarning().nospace() << indent << "Sub-node " << ii << ": " << node->values.at(ii).toString();
1576         }
1577     }
1578
1579     for (QHash<QString, ModelNode *>::ConstIterator iter = node->properties.begin(); iter != node->properties.end(); ++iter) {
1580         qWarning().nospace() << indent << "Property " << iter.key() << ':';
1581         dump(iter.value(), ind + 1);
1582     }
1583 }
1584
1585 ModelObject::ModelObject(ModelNode *node, NestedListModel *model, QScriptEngine *seng)
1586     : m_model(model),
1587       m_node(node),
1588       m_meta(new ModelNodeMetaObject(seng, this))
1589 {
1590 }
1591
1592 void ModelObject::setValue(const QByteArray &name, const QVariant &val)
1593 {
1594     m_meta->setValue(name, val);
1595     //setProperty(name.constData(), val);
1596 }
1597
1598 void ModelObject::setNodeUpdatesEnabled(bool enable)
1599 {
1600     m_meta->m_enabled = enable;
1601 }
1602
1603
1604 ModelNodeMetaObject::ModelNodeMetaObject(QScriptEngine *seng, ModelObject *object)
1605     : QDeclarativeOpenMetaObject(object),
1606       m_enabled(false),
1607       m_seng(seng),
1608       m_obj(object)
1609 {
1610 }
1611
1612 void ModelNodeMetaObject::propertyWritten(int index)
1613 {
1614     if (!m_enabled)
1615         return;
1616
1617     QString propName = QString::fromUtf8(name(index));
1618     QVariant value = operator[](index);
1619
1620     QScriptValue sv = m_seng->newObject();
1621     sv.setProperty(propName, m_seng->newVariant(value));
1622     bool changed = m_obj->m_node->setObjectValue(sv, false);
1623     if (changed)
1624         m_obj->m_node->changedProperty(propName);
1625 }
1626
1627
1628 QT_END_NAMESPACE