Initial import from the monolithic Qt.
[profile/ivi/qtdeclarative.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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
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->_root = root;
788     QStack<ModelNode *> nodes;
789     nodes << root;
790
791     bool processingSet = false;
792
793     const ListModelData *lmd = (const ListModelData *)d.constData();
794     const char *data = ((const char *)lmd) + lmd->dataOffset;
795
796     for (int ii = 0; ii < lmd->instrCount; ++ii) {
797         const ListInstruction &instr = lmd->instructions()[ii];
798
799         switch(instr.type) {
800         case ListInstruction::Push:
801             {
802                 ModelNode *n = nodes.top();
803                 ModelNode *n2 = new ModelNode(rv->m_nested);
804                 n->values << QVariant::fromValue(n2);
805                 nodes.push(n2);
806                 if (processingSet)
807                     n->isArray = true;
808             }
809             break;
810
811         case ListInstruction::Pop:
812             nodes.pop();
813             break;
814
815         case ListInstruction::Value:
816             {
817                 ModelNode *n = nodes.top();
818                 switch (QDeclarativeParser::Variant::Type(data[instr.dataIdx])) {
819                  case QDeclarativeParser::Variant::Invalid:
820                     n->isArray = true;
821                     break;
822                  case QDeclarativeParser::Variant::Boolean:
823                     n->values.append(bool(data[1 + instr.dataIdx]));
824                     break;
825                  case QDeclarativeParser::Variant::Number:
826                     n->values.append(QByteArray(data + 1 + instr.dataIdx).toDouble());
827                     break;
828                  case QDeclarativeParser::Variant::String:
829                     n->values.append(QString::fromUtf8(data + 1 + instr.dataIdx));
830                     break;
831                  default:
832                     Q_ASSERT("Format error in ListInstruction");
833                 }
834
835                 processingSet = false;
836             }
837             break;
838
839         case ListInstruction::Set:
840             {
841                 ModelNode *n = nodes.top();
842                 ModelNode *n2 = new ModelNode(rv->m_nested);
843                 n->properties.insert(QString::fromUtf8(data + instr.dataIdx), n2);
844                 nodes.push(n2);
845                 processingSet = true;
846             }
847             break;
848         }
849     }
850
851     ModelNode *rootNode = rv->m_nested->_root;
852     for (int i=0; i<rootNode->values.count(); ++i) {
853         ModelNode *node = qvariant_cast<ModelNode *>(rootNode->values[i]);
854         node->listIndex = i;
855         node->updateListIndexes();
856     }
857 }
858
859 bool QDeclarativeListModelParser::definesEmptyList(const QString &s)
860 {
861     if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) {
862         for (int i=1; i<s.length()-1; i++) {
863             if (!s[i].isSpace())
864                 return false;
865         }
866         return true;
867     }
868     return false;
869 }
870
871
872 /*!
873     \qmlclass ListElement QDeclarativeListElement
874     \ingroup qml-working-with-data
875     \since 4.7
876     \brief The ListElement element defines a data item in a ListModel.
877
878     List elements are defined inside ListModel definitions, and represent items in a
879     list that will be displayed using ListView or \l Repeater items.
880
881     List elements are defined like other QML elements except that they contain
882     a collection of \e role definitions instead of properties. Using the same
883     syntax as property definitions, roles both define how the data is accessed
884     and include the data itself.
885
886     The names used for roles must begin with a lower-case letter and should be
887     common to all elements in a given model. Values must be simple constants; either
888     strings (quoted and optionally within a call to QT_TR_NOOP), boolean values
889     (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter).
890
891     \section1 Referencing Roles
892
893     The role names are used by delegates to obtain data from list elements.
894     Each role name is accessible in the delegate's scope, and refers to the
895     corresponding role in the current element. Where a role name would be
896     ambiguous to use, it can be accessed via the \l{ListView::}{model}
897     property (e.g., \c{model.cost} instead of \c{cost}).
898
899     \section1 Example Usage
900
901     The following model defines a series of list elements, each of which
902     contain "name" and "cost" roles and their associated values.
903
904     \snippet doc/src/snippets/declarative/qml-data-models/listelements.qml model
905
906     The delegate obtains the name and cost for each element by simply referring
907     to \c name and \c cost:
908
909     \snippet doc/src/snippets/declarative/qml-data-models/listelements.qml view
910
911     \sa ListModel
912 */
913
914 FlatListModel::FlatListModel(QDeclarativeListModel *base)
915     : m_scriptEngine(0), m_listModel(base), m_scriptClass(0), m_parentAgent(0)
916 {
917 }
918
919 FlatListModel::~FlatListModel()
920 {
921     qDeleteAll(m_nodeData);
922 }
923
924 QVariant FlatListModel::data(int index, int role) const
925 {
926     Q_ASSERT(index >= 0 && index < m_values.count());
927     if (m_values[index].contains(role))
928         return m_values[index][role];
929     return QVariant();
930 }
931
932 QList<int> FlatListModel::roles() const
933 {
934     return m_roles.keys();
935 }
936
937 QString FlatListModel::toString(int role) const
938 {
939     if (m_roles.contains(role))
940         return m_roles[role];
941     return QString();
942 }
943
944 int FlatListModel::count() const
945 {
946     return m_values.count();
947 }
948
949 void FlatListModel::clear()
950 {
951     m_values.clear();
952
953     qDeleteAll(m_nodeData);
954     m_nodeData.clear();
955 }
956
957 void FlatListModel::remove(int index)
958 {
959     m_values.removeAt(index);
960     removedNode(index);
961 }
962
963 bool FlatListModel::insert(int index, const QScriptValue &value)
964 {
965     Q_ASSERT(index >= 0 && index <= m_values.count());
966
967     QHash<int, QVariant> row;
968     if (!addValue(value, &row, 0))
969         return false;
970
971     m_values.insert(index, row);
972     insertedNode(index);
973
974     return true;
975 }
976
977 QScriptValue FlatListModel::get(int index) const
978 {
979     QScriptEngine *scriptEngine = m_scriptEngine ? m_scriptEngine : QDeclarativeEnginePrivate::getScriptEngine(qmlEngine(m_listModel));
980
981     if (!scriptEngine) 
982         return 0;
983
984     if (index < 0 || index >= m_values.count())
985         return scriptEngine->undefinedValue();
986
987     FlatListModel *that = const_cast<FlatListModel*>(this);
988     if (!m_scriptClass)
989         that->m_scriptClass = new FlatListScriptClass(that, scriptEngine);
990
991     FlatNodeData *data = m_nodeData.value(index);
992     if (!data) {
993         data = new FlatNodeData(index);
994         that->m_nodeData.replace(index, data);
995     }
996
997     return QScriptDeclarativeClass::newObject(scriptEngine, m_scriptClass, new FlatNodeObjectData(data));
998 }
999
1000 void FlatListModel::set(int index, const QScriptValue &value, QList<int> *roles)
1001 {
1002     Q_ASSERT(index >= 0 && index < m_values.count());
1003
1004     QHash<int, QVariant> row = m_values[index];
1005     if (addValue(value, &row, roles))
1006         m_values[index] = row;
1007 }
1008
1009 void FlatListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles)
1010 {
1011     Q_ASSERT(index >= 0 && index < m_values.count());
1012
1013     QHash<QString, int>::Iterator iter = m_strings.find(property);
1014     int role;
1015     if (iter == m_strings.end()) {
1016         role = m_roles.count();
1017         m_roles.insert(role, property);
1018         m_strings.insert(property, role);
1019     } else {
1020         role = iter.value();
1021     }
1022
1023     if (m_values[index][role] != value) {
1024         roles->append(role);
1025         m_values[index][role] = value;
1026     }
1027 }
1028
1029 void FlatListModel::move(int from, int to, int n)
1030 {
1031     qdeclarativelistmodel_move<QList<QHash<int, QVariant> > >(from, to, n, &m_values);
1032     moveNodes(from, to, n);
1033 }
1034
1035 bool FlatListModel::addValue(const QScriptValue &value, QHash<int, QVariant> *row, QList<int> *roles)
1036 {
1037     QScriptValueIterator it(value);
1038     while (it.hasNext()) {
1039         it.next();
1040         QScriptValue value = it.value();
1041         if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) {
1042             qmlInfo(m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script";
1043             return false;
1044         }
1045
1046         QString name = it.name();
1047         QVariant v = it.value().toVariant();
1048
1049         QHash<QString, int>::Iterator iter = m_strings.find(name);
1050         if (iter == m_strings.end()) {
1051             int role = m_roles.count();
1052             m_roles.insert(role, name);
1053             iter = m_strings.insert(name, role);
1054             if (roles)
1055                 roles->append(role);
1056         } else {
1057             int role = iter.value();
1058             if (roles && row->contains(role) && row->value(role) != v)
1059                 roles->append(role);
1060         }
1061         row->insert(*iter, v);
1062     }
1063     return true;
1064 }
1065
1066 void FlatListModel::insertedNode(int index)
1067 {
1068     if (index >= 0 && index <= m_values.count()) {
1069         m_nodeData.insert(index, 0);
1070
1071         for (int i=index + 1; i<m_nodeData.count(); i++) {
1072             if (m_nodeData[i])
1073                 m_nodeData[i]->index = i;
1074         }
1075     }
1076 }
1077
1078 void FlatListModel::removedNode(int index)
1079 {
1080     if (index >= 0 && index < m_nodeData.count()) {
1081         delete m_nodeData.takeAt(index);
1082
1083         for (int i=index; i<m_nodeData.count(); i++) {
1084             if (m_nodeData[i])
1085                 m_nodeData[i]->index = i;
1086         }
1087     }
1088 }
1089
1090 void FlatListModel::moveNodes(int from, int to, int n)
1091 {
1092     if (!m_listModel->canMove(from, to, n))
1093         return;
1094
1095     qdeclarativelistmodel_move<QList<FlatNodeData *> >(from, to, n, &m_nodeData);
1096
1097     for (int i=from; i<from + (to-from); i++)  {
1098         if (m_nodeData[i]) 
1099             m_nodeData[i]->index = i;
1100     }
1101 }
1102
1103
1104
1105 FlatNodeData::~FlatNodeData()
1106 {
1107     for (QSet<FlatNodeObjectData *>::Iterator iter = objects.begin(); iter != objects.end(); ++iter) {
1108         FlatNodeObjectData *data = *iter;
1109         data->nodeData = 0;
1110     }
1111 }
1112
1113 void FlatNodeData::addData(FlatNodeObjectData *data) 
1114 {
1115     objects.insert(data);
1116 }
1117
1118 void FlatNodeData::removeData(FlatNodeObjectData *data)
1119 {
1120     objects.remove(data);
1121 }
1122
1123
1124 FlatListScriptClass::FlatListScriptClass(FlatListModel *model, QScriptEngine *seng)
1125     : QScriptDeclarativeClass(seng),
1126       m_model(model)
1127 {
1128 }
1129
1130 QScriptDeclarativeClass::Value FlatListScriptClass::property(Object *obj, const Identifier &name)
1131 {
1132     FlatNodeObjectData *objData = static_cast<FlatNodeObjectData*>(obj);
1133     if (!objData->nodeData) // item at this index has been deleted
1134         return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue());
1135
1136     int index = objData->nodeData->index;
1137     QString propName = toString(name);
1138     int role = m_model->m_strings.value(propName, -1);
1139
1140     if (role >= 0 && index >=0 ) {
1141         const QHash<int, QVariant> &row = m_model->m_values[index];
1142         QScriptValue sv = engine()->toScriptValue<QVariant>(row[role]);
1143         return QScriptDeclarativeClass::Value(engine(), sv);
1144     }
1145
1146     return QScriptDeclarativeClass::Value(engine(), engine()->undefinedValue());
1147 }
1148
1149 void FlatListScriptClass::setProperty(Object *obj, const Identifier &name, const QScriptValue &value)
1150 {
1151     if (!value.isVariant() && !value.isRegExp() && !value.isDate() && value.isObject()) {
1152         qmlInfo(m_model->m_listModel) << "Cannot add list-type data when modifying or after modification from a worker script";
1153         return;
1154     }
1155
1156     FlatNodeObjectData *objData = static_cast<FlatNodeObjectData*>(obj);
1157     if (!objData->nodeData) // item at this index has been deleted
1158         return;
1159
1160     int index = objData->nodeData->index;
1161     QString propName = toString(name);
1162
1163     int role = m_model->m_strings.value(propName, -1);
1164     if (role >= 0 && index >= 0) {
1165         QHash<int, QVariant> &row = m_model->m_values[index];
1166         row[role] = value.toVariant();
1167
1168         QList<int> roles;
1169         roles << role;
1170         if (m_model->m_parentAgent) {
1171             // This is the list in the worker thread, so tell the agent to
1172             // emit itemsChanged() later
1173             m_model->m_parentAgent->changedData(index, 1, roles);
1174         } else {
1175             // This is the list in the main thread, so emit itemsChanged()
1176             emit m_model->m_listModel->itemsChanged(index, 1, roles);
1177         }
1178     }
1179 }
1180
1181 QScriptClass::QueryFlags FlatListScriptClass::queryProperty(Object *, const Identifier &, QScriptClass::QueryFlags)
1182 {
1183     return (QScriptClass::HandlesReadAccess | QScriptClass::HandlesWriteAccess);
1184 }
1185
1186 bool FlatListScriptClass::compare(Object *obj1, Object *obj2)
1187 {
1188     FlatNodeObjectData *data1 = static_cast<FlatNodeObjectData*>(obj1);
1189     FlatNodeObjectData *data2 = static_cast<FlatNodeObjectData*>(obj2);
1190
1191     if (!data1->nodeData || !data2->nodeData)
1192         return false;
1193
1194     return data1->nodeData->index == data2->nodeData->index;
1195 }
1196
1197
1198
1199 NestedListModel::NestedListModel(QDeclarativeListModel *base)
1200     : _root(0), m_ownsRoot(false), m_listModel(base), _rolesOk(false)
1201 {
1202 }
1203
1204 NestedListModel::~NestedListModel()
1205 {
1206     if (m_ownsRoot)
1207         delete _root;
1208 }
1209
1210 QVariant NestedListModel::valueForNode(ModelNode *node, bool *hasNested) const
1211 {
1212     QObject *rv = 0;
1213     if (hasNested)
1214         *hasNested = false;
1215
1216     if (node->isArray) {
1217         // List
1218         rv = node->model(this);
1219         if (hasNested)
1220             *hasNested = true;
1221     } else {
1222         if (!node->properties.isEmpty()) {
1223             // Object
1224             rv = node->object(this);
1225         } else if (node->values.count() == 0) {
1226             // Invalid
1227             return QVariant();
1228         } else if (node->values.count() == 1) {
1229             // Value
1230             QVariant &var = node->values[0];
1231             ModelNode *valueNode = qvariant_cast<ModelNode *>(var);
1232             if (valueNode) {
1233                 if (!valueNode->properties.isEmpty())
1234                     rv = valueNode->object(this);
1235                 else
1236                     rv = valueNode->model(this);
1237             } else {
1238                 return var;
1239             }
1240         }
1241     }
1242
1243     if (rv) {
1244         return QVariant::fromValue(rv);
1245     } else {
1246         return QVariant();
1247     }
1248 }
1249
1250 QHash<int,QVariant> NestedListModel::data(int index, const QList<int> &roles, bool *hasNested) const
1251 {
1252     Q_ASSERT(_root && index >= 0 && index < _root->values.count());
1253     checkRoles();
1254     QHash<int, QVariant> rv;
1255
1256     ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1257     if (!node)
1258         return rv;
1259
1260     for (int ii = 0; ii < roles.count(); ++ii) {
1261         const QString &roleString = roleStrings.at(roles.at(ii));
1262
1263         QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString);
1264         if (iter != node->properties.end()) {
1265             ModelNode *row = *iter;
1266             rv.insert(roles.at(ii), valueForNode(row, hasNested));
1267         }
1268     }
1269
1270     return rv;
1271 }
1272
1273 QVariant NestedListModel::data(int index, int role) const
1274 {
1275     Q_ASSERT(_root && index >= 0 && index < _root->values.count());
1276     checkRoles();
1277     QVariant rv;
1278     if (roleStrings.count() < role)
1279         return rv;
1280
1281     ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1282     if (!node)
1283         return rv;
1284
1285     const QString &roleString = roleStrings.at(role);
1286
1287     QHash<QString, ModelNode *>::ConstIterator iter = node->properties.find(roleString);
1288     if (iter != node->properties.end()) {
1289         ModelNode *row = *iter;
1290         rv = valueForNode(row);
1291     }
1292
1293     return rv;
1294 }
1295
1296 int NestedListModel::count() const
1297 {
1298     if (!_root) return 0;
1299     return _root->values.count();
1300 }
1301
1302 void NestedListModel::clear()
1303 {
1304     if (_root)
1305         _root->clear();
1306 }
1307
1308 void NestedListModel::remove(int index)
1309 {
1310     if (!_root)
1311         return;
1312     ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1313     _root->values.removeAt(index);
1314     if (node)
1315         delete node;
1316 }
1317
1318 bool NestedListModel::insert(int index, const QScriptValue& valuemap)
1319 {
1320     if (!_root) {
1321         _root = new ModelNode(this);
1322         m_ownsRoot = true;
1323     }
1324
1325     ModelNode *mn = new ModelNode(this);
1326     mn->listIndex = index;
1327     mn->setObjectValue(valuemap);
1328     _root->values.insert(index,QVariant::fromValue(mn));
1329     return true;
1330 }
1331
1332 void NestedListModel::move(int from, int to, int n)
1333 {
1334     if (!_root)
1335         return;
1336     qdeclarativelistmodel_move<QVariantList>(from, to, n, &_root->values);
1337 }
1338
1339 QScriptValue NestedListModel::get(int index) const
1340 {   
1341     QDeclarativeEngine *eng = qmlEngine(m_listModel);
1342     if (!eng) 
1343         return 0;
1344
1345     if (index < 0 || index >= count()) {
1346         QScriptEngine *seng = QDeclarativeEnginePrivate::getScriptEngine(eng);
1347         if (seng)
1348             return seng->undefinedValue();
1349         return 0;
1350     }
1351
1352     ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1353     if (!node)
1354         return 0;
1355     
1356     return QDeclarativeEnginePrivate::qmlScriptObject(node->object(this), eng);
1357 }
1358
1359 void NestedListModel::set(int index, const QScriptValue& valuemap, QList<int> *roles)
1360 {
1361     Q_ASSERT(index >=0 && index < count());
1362
1363     ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1364     bool emitItemsChanged = node->setObjectValue(valuemap);
1365     if (!emitItemsChanged)
1366         return;
1367
1368     QScriptValueIterator it(valuemap);
1369     while (it.hasNext()) {
1370         it.next();
1371         int r = roleStrings.indexOf(it.name());
1372         if (r < 0) {
1373             r = roleStrings.count();
1374             roleStrings << it.name();
1375         }
1376         roles->append(r);
1377     }
1378 }
1379
1380 void NestedListModel::setProperty(int index, const QString& property, const QVariant& value, QList<int> *roles)
1381 {
1382     Q_ASSERT(index >=0 && index < count());
1383
1384     ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(index));
1385     bool emitItemsChanged = node->setProperty(property, value);
1386     if (!emitItemsChanged)
1387         return;
1388
1389     int r = roleStrings.indexOf(property);
1390     if (r < 0) {
1391         r = roleStrings.count();
1392         roleStrings << property;
1393     }
1394     roles->append(r);
1395 }
1396
1397 void NestedListModel::checkRoles() const
1398 {
1399     if (_rolesOk || !_root)
1400         return;
1401
1402     for (int i = 0; i<_root->values.count(); ++i) {
1403         ModelNode *node = qvariant_cast<ModelNode *>(_root->values.at(i));
1404         if (node) {
1405             foreach (const QString &role, node->properties.keys()) {
1406                 if (!roleStrings.contains(role))
1407                     roleStrings.append(role);
1408             }
1409         }
1410     }
1411
1412     _rolesOk = true;
1413 }
1414
1415 QList<int> NestedListModel::roles() const
1416 {
1417     checkRoles();
1418     QList<int> rv;
1419     for (int ii = 0; ii < roleStrings.count(); ++ii)
1420         rv << ii;
1421     return rv;
1422 }
1423
1424 QString NestedListModel::toString(int role) const
1425 {
1426     checkRoles();
1427     if (role < roleStrings.count())
1428         return roleStrings.at(role);
1429     else
1430         return QString();
1431 }
1432
1433
1434 ModelNode::ModelNode(NestedListModel *model)
1435 : modelCache(0), objectCache(0), isArray(false), m_model(model), listIndex(-1)
1436 {
1437 }
1438
1439 ModelNode::~ModelNode()
1440 {
1441     clear();
1442     if (modelCache) { modelCache->m_nested->_root = 0/* ==this */; delete modelCache; modelCache = 0; }
1443     if (objectCache) { delete objectCache; objectCache = 0; }
1444 }
1445
1446 void ModelNode::clear()
1447 {
1448     ModelNode *node;
1449     for (int ii = 0; ii < values.count(); ++ii) {
1450         node = qvariant_cast<ModelNode *>(values.at(ii));
1451         if (node) { delete node; node = 0; }
1452     }
1453     values.clear();
1454
1455     qDeleteAll(properties.values());
1456     properties.clear();
1457 }
1458
1459 bool ModelNode::setObjectValue(const QScriptValue& valuemap, bool writeToCache)
1460 {
1461     bool emitItemsChanged = false;
1462
1463     QScriptValueIterator it(valuemap);
1464     while (it.hasNext()) {
1465         it.next();
1466         ModelNode *prev = properties.value(it.name());
1467         ModelNode *value = new ModelNode(m_model);
1468         QScriptValue v = it.value();
1469
1470         if (v.isArray()) {
1471             value->isArray = true;
1472             value->setListValue(v);
1473             if (writeToCache && objectCache)
1474                 objectCache->setValue(it.name().toUtf8(), QVariant::fromValue(value->model(m_model)));
1475             emitItemsChanged = true;    // for now, too inefficient to check whether list and sublists have changed
1476         } else {
1477             value->values << v.toVariant();
1478             if (writeToCache && objectCache)
1479                 objectCache->setValue(it.name().toUtf8(), value->values.last());
1480             if (!emitItemsChanged && prev && prev->values.count() == 1
1481                     && prev->values[0] != value->values.last()) {
1482                 emitItemsChanged = true;
1483             }
1484         }
1485         if (properties.contains(it.name()))
1486             delete properties[it.name()];
1487         properties.insert(it.name(), value);
1488     }
1489     return emitItemsChanged;
1490 }
1491
1492 void ModelNode::setListValue(const QScriptValue& valuelist) {
1493     values.clear();
1494     int size = valuelist.property(QLatin1String("length")).toInt32();
1495     for (int i=0; i<size; i++) {
1496         ModelNode *value = new ModelNode(m_model);
1497         QScriptValue v = valuelist.property(i);
1498         if (v.isArray()) {
1499             value->isArray = true;
1500             value->setListValue(v);
1501         } else if (v.isObject()) {
1502             value->listIndex = i;
1503             value->setObjectValue(v);
1504         } else {
1505             value->listIndex = i;
1506             value->values << v.toVariant();
1507         }
1508         values.append(QVariant::fromValue(value));
1509     }
1510 }
1511
1512 bool ModelNode::setProperty(const QString& prop, const QVariant& val) {
1513     QHash<QString, ModelNode *>::const_iterator it = properties.find(prop);
1514     bool emitItemsChanged = false;
1515     if (it != properties.end()) {
1516         if (val != (*it)->values[0])
1517             emitItemsChanged = true;
1518         (*it)->values[0] = val;
1519     } else {
1520         ModelNode *n = new ModelNode(m_model);
1521         n->values << val;
1522         properties.insert(prop,n);
1523     }
1524     if (objectCache)
1525         objectCache->setValue(prop.toUtf8(), val);
1526     return emitItemsChanged;
1527 }
1528
1529 void ModelNode::updateListIndexes()
1530 {
1531     for (QHash<QString, ModelNode *>::ConstIterator iter = properties.begin(); iter != properties.end(); ++iter) {
1532         ModelNode *node = iter.value();
1533         if (node->isArray) {
1534             for (int i=0; i<node->values.count(); ++i) {
1535                 ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(i));
1536                 if (subNode)
1537                     subNode->listIndex = i;
1538             }
1539         }
1540         node->updateListIndexes();
1541     }
1542 }
1543
1544 /*
1545     Need to call this to emit itemsChanged() for modifications outside of set()
1546     and setProperty(), i.e. if an item returned from get() is modified
1547 */
1548 void ModelNode::changedProperty(const QString &name) const
1549 {
1550     if (listIndex < 0)
1551         return;
1552
1553     m_model->checkRoles();
1554     QList<int> roles;
1555     int role = m_model->roleStrings.indexOf(name);
1556     if (role < 0)
1557         roles = m_model->roles();
1558     else
1559         roles << role;
1560     emit m_model->m_listModel->itemsChanged(listIndex, 1, roles);
1561 }
1562
1563 void ModelNode::dump(ModelNode *node, int ind)
1564 {
1565     QByteArray indentBa(ind * 4, ' ');
1566     const char *indent = indentBa.constData();
1567
1568     for (int ii = 0; ii < node->values.count(); ++ii) {
1569         ModelNode *subNode = qvariant_cast<ModelNode *>(node->values.at(ii));
1570         if (subNode) {
1571             qWarning().nospace() << indent << "Sub-node " << ii;
1572             dump(subNode, ind + 1);
1573         } else {
1574             qWarning().nospace() << indent << "Sub-node " << ii << ": " << node->values.at(ii).toString();
1575         }
1576     }
1577
1578     for (QHash<QString, ModelNode *>::ConstIterator iter = node->properties.begin(); iter != node->properties.end(); ++iter) {
1579         qWarning().nospace() << indent << "Property " << iter.key() << ':';
1580         dump(iter.value(), ind + 1);
1581     }
1582 }
1583
1584 ModelObject::ModelObject(ModelNode *node, NestedListModel *model, QScriptEngine *seng)
1585     : m_model(model),
1586       m_node(node),
1587       m_meta(new ModelNodeMetaObject(seng, this))
1588 {
1589 }
1590
1591 void ModelObject::setValue(const QByteArray &name, const QVariant &val)
1592 {
1593     m_meta->setValue(name, val);
1594     //setProperty(name.constData(), val);
1595 }
1596
1597 void ModelObject::setNodeUpdatesEnabled(bool enable)
1598 {
1599     m_meta->m_enabled = enable;
1600 }
1601
1602
1603 ModelNodeMetaObject::ModelNodeMetaObject(QScriptEngine *seng, ModelObject *object)
1604     : QDeclarativeOpenMetaObject(object),
1605       m_enabled(false),
1606       m_seng(seng),
1607       m_obj(object)
1608 {
1609 }
1610
1611 void ModelNodeMetaObject::propertyWritten(int index)
1612 {
1613     if (!m_enabled)
1614         return;
1615
1616     QString propName = QString::fromUtf8(name(index));
1617     QVariant value = operator[](index);
1618
1619     QScriptValue sv = m_seng->newObject();
1620     sv.setProperty(propName, m_seng->newVariant(value));
1621     bool changed = m_obj->m_node->setObjectValue(sv, false);
1622     if (changed)
1623         m_obj->m_node->changedProperty(propName);
1624 }
1625
1626
1627 QT_END_NAMESPACE