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