Remove "All rights reserved" line from license headers.
[profile/ivi/qtdeclarative.git] / src / declarative / qml / qdeclarativelistmodel.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qdeclarativelistmodel_p_p.h"
43 #include "qdeclarativelistmodelworkeragent_p.h"
44 #include "qdeclarativeopenmetaobject_p.h"
45 #include <private/qdeclarativejsast_p.h>
46 #include <private/qdeclarativejsengine_p.h>
47
48 #include <private/qdeclarativecustomparser_p.h>
49 #include <private/qdeclarativescript_p.h>
50 #include <private/qdeclarativeengine_p.h>
51 #include <qdeclarativecontext.h>
52 #include <qdeclarativeinfo.h>
53
54 #include <QtCore/qdebug.h>
55 #include <QtCore/qstack.h>
56 #include <QXmlStreamReader>
57
58 Q_DECLARE_METATYPE(QListModelInterface *)
59
60 QT_BEGIN_NAMESPACE
61
62 // Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models.
63 enum { MIN_LISTMODEL_UID = 1024 };
64
65 static QAtomicInt uidCounter(MIN_LISTMODEL_UID);
66
67 template <typename T>
68 static bool isMemoryUsed(const char *mem)
69 {
70     for (size_t i=0 ; i < sizeof(T) ; ++i) {
71         if (mem[i] != 0)
72             return true;
73     }
74
75     return false;
76 }
77
78 static QString roleTypeName(ListLayout::Role::DataType t)
79 {
80     QString result;
81     const char *roleTypeNames[] = { "String", "Number", "Bool", "List", "QObject", "VariantMap" };
82
83     if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType)
84         result = QString::fromLatin1(roleTypeNames[t]);
85
86     return result;
87 }
88
89 const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type)
90 {
91     QStringHash<Role *>::Node *node = roleHash.findNode(key);
92     if (node) {
93         const Role &r = *node->value;
94         if (type != r.type)
95             qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type));
96         return r;
97     }
98
99     return createRole(key, type);
100 }
101
102 const ListLayout::Role &ListLayout::getRoleOrCreate(v8::Handle<v8::String> key, Role::DataType type)
103 {
104     QHashedV8String hashedKey(key);
105     QStringHash<Role *>::Node *node = roleHash.findNode(hashedKey);
106     if (node) {
107         const Role &r = *node->value;
108         if (type != r.type)
109             qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type));
110         return r;
111     }
112
113     QString qkey;
114     qkey.resize(key->Length());
115     key->Write(reinterpret_cast<uint16_t*>(qkey.data()));
116
117     return createRole(qkey, type);
118 }
119
120 const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type)
121 {
122     const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QDeclarativeGuard<QObject>), sizeof(QVariantMap) };
123     const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap) };
124
125     Role *r = new Role;
126     r->name = key;
127     r->type = type;
128
129     if (type == Role::List) {
130         r->subLayout = new ListLayout;
131     } else {
132         r->subLayout = 0;
133     }
134
135     int dataSize = dataSizes[type];
136     int dataAlignment = dataAlignments[type];
137
138     int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1);
139     if (dataOffset + dataSize > ListElement::BLOCK_SIZE) {
140         r->blockIndex = ++currentBlock;
141         r->blockOffset = 0;
142         currentBlockOffset = dataSize;
143     } else {
144         r->blockIndex = currentBlock;
145         r->blockOffset = dataOffset;
146         currentBlockOffset = dataOffset + dataSize;
147     }
148
149     int roleIndex = roles.count();
150     r->index = roleIndex;
151
152     roles.append(r);
153     roleHash.insert(key, r);
154
155     return *r;
156 }
157
158 ListLayout::ListLayout(const ListLayout *other) : currentBlock(0), currentBlockOffset(0)
159 {
160     for (int i=0 ; i < other->roles.count() ; ++i) {
161         Role *role = new Role(other->roles[i]);
162         roles.append(role);
163         roleHash.insert(role->name, role);
164     }
165     currentBlockOffset = other->currentBlockOffset;
166     currentBlock = other->currentBlock;
167 }
168
169 ListLayout::~ListLayout()
170 {
171     for (int i=0 ; i < roles.count() ; ++i) {
172         delete roles[i];
173     }
174 }
175
176 void ListLayout::sync(ListLayout *src, ListLayout *target)
177 {
178     int roleOffset = target->roles.count();
179     int newRoleCount = src->roles.count() - roleOffset;
180
181     for (int i=0 ; i < newRoleCount ; ++i) {
182         Role *role = new Role(src->roles[roleOffset + i]);
183         target->roles.append(role);
184         target->roleHash.insert(role->name, role);
185     }
186
187     target->currentBlockOffset = src->currentBlockOffset;
188     target->currentBlock = src->currentBlock;
189 }
190
191 ListLayout::Role::Role(const Role *other)
192 {
193     name = other->name;
194     type = other->type;
195     blockIndex = other->blockIndex;
196     blockOffset = other->blockOffset;
197     index = other->index;
198     if (other->subLayout)
199         subLayout = new ListLayout(other->subLayout);
200     else
201         subLayout = 0;
202 }
203
204 ListLayout::Role::~Role()
205 {
206     delete subLayout;
207 }
208
209 const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QVariant &data)
210 {
211     Role::DataType type;
212
213     switch (data.type()) {
214         case QVariant::Double:      type = Role::Number;      break;
215         case QVariant::Int:         type = Role::Number;      break;
216         case QVariant::UserType:    type = Role::List;        break;
217         case QVariant::Bool:        type = Role::Bool;        break;
218         case QVariant::String:      type = Role::String;      break;
219         case QVariant::Map:         type = Role::VariantMap;  break;
220         default:                    type = Role::Invalid;     break;
221     }
222
223     if (type == Role::Invalid) {
224         qmlInfo(0) << "Can't create role for unsupported data type";
225         return 0;
226     }
227
228     return &getRoleOrCreate(key, type);
229 }
230
231 const ListLayout::Role *ListLayout::getExistingRole(const QString &key)
232 {
233     Role *r = 0;
234     QStringHash<Role *>::Node *node = roleHash.findNode(key);
235     if (node)
236         r = node->value;
237     return r;
238 }
239
240 const ListLayout::Role *ListLayout::getExistingRole(v8::Handle<v8::String> key)
241 {
242     Role *r = 0;
243     QHashedV8String hashedKey(key);
244     QStringHash<Role *>::Node *node = roleHash.findNode(hashedKey);
245     if (node)
246         r = node->value;
247     return r;
248 }
249
250 ModelObject *ListModel::getOrCreateModelObject(QDeclarativeListModel *model, int elementIndex)
251 {
252     ListElement *e = elements[elementIndex];
253     if (e->m_objectCache == 0) {
254         e->m_objectCache = new ModelObject(model, elementIndex);
255     }
256     return e->m_objectCache;
257 }
258
259 void ListModel::sync(ListModel *src, ListModel *target, QHash<int, ListModel *> *targetModelHash)
260 {
261     // Sanity check
262     target->m_uid = src->m_uid;
263     if (targetModelHash)
264         targetModelHash->insert(target->m_uid, target);
265
266     // Build hash of elements <-> uid for each of the lists
267     QHash<int, ElementSync> elementHash;
268     for (int i=0 ; i < target->elements.count() ; ++i) {
269         ListElement *e = target->elements.at(i);
270         int uid = e->getUid();
271         ElementSync sync;
272         sync.target = e;
273         elementHash.insert(uid, sync);
274     }
275     for (int i=0 ; i < src->elements.count() ; ++i) {
276         ListElement *e = src->elements.at(i);
277         int uid = e->getUid();
278
279         QHash<int, ElementSync>::iterator it = elementHash.find(uid);
280         if (it == elementHash.end()) {
281             ElementSync sync;
282             sync.src = e;
283             elementHash.insert(uid, sync);
284         } else {
285             ElementSync &sync = it.value();
286             sync.src = e;
287         }
288     }
289
290     // Get list of elements that are in the target but no longer in the source. These get deleted first.
291     QHash<int, ElementSync>::iterator it = elementHash.begin();
292     QHash<int, ElementSync>::iterator end = elementHash.end();
293     while (it != end) {
294         const ElementSync &s = it.value();
295         if (s.src == 0) {
296             s.target->destroy(target->m_layout);
297             target->elements.removeOne(s.target);
298             delete s.target;
299         }
300         ++it;
301     }
302
303     // Sync the layouts
304     ListLayout::sync(src->m_layout, target->m_layout);
305
306     // Clear the target list, and append in correct order from the source
307     target->elements.clear();
308     for (int i=0 ; i < src->elements.count() ; ++i) {
309         ListElement *srcElement = src->elements.at(i);
310         it = elementHash.find(srcElement->getUid());
311         const ElementSync &s = it.value();
312         ListElement *targetElement = s.target;
313         if (targetElement == 0) {
314             targetElement = new ListElement(srcElement->getUid());
315         }
316         ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout, targetModelHash);
317         target->elements.append(targetElement);
318     }
319
320     target->updateCacheIndices();
321
322     // Update values stored in target meta objects
323     for (int i=0 ; i < target->elements.count() ; ++i) {
324         ListElement *e = target->elements[i];
325         if (e->m_objectCache)
326             e->m_objectCache->updateValues();
327     }
328 }
329
330 ListModel::ListModel(ListLayout *layout, QDeclarativeListModel *modelCache, int uid) : m_layout(layout), m_modelCache(modelCache)
331 {
332     if (uid == -1)
333         uid = uidCounter.fetchAndAddOrdered(1);
334     m_uid = uid;
335 }
336
337 void ListModel::destroy()
338 {
339     clear();
340     m_uid = -1;
341     m_layout = 0;
342     if (m_modelCache && m_modelCache->m_primary == false)
343         delete m_modelCache;
344     m_modelCache = 0;
345 }
346
347 int ListModel::appendElement()
348 {
349     int elementIndex = elements.count();
350     newElement(elementIndex);
351     return elementIndex;
352 }
353
354 void ListModel::insertElement(int index)
355 {
356     newElement(index);
357     updateCacheIndices();
358 }
359
360 void ListModel::move(int from, int to, int n)
361 {
362     if (from > to) {
363         // Only move forwards - flip if backwards moving
364         int tfrom = from;
365         int tto = to;
366         from = tto;
367         to = tto+n;
368         n = tfrom-tto;
369     }
370
371     QPODVector<ListElement *, 4> store;
372     for (int i=0 ; i < (to-from) ; ++i)
373         store.append(elements[from+n+i]);
374     for (int i=0 ; i < n ; ++i)
375         store.append(elements[from+i]);
376     for (int i=0 ; i < store.count() ; ++i)
377         elements[from+i] = store[i];
378
379     updateCacheIndices();
380 }
381
382 void ListModel::newElement(int index)
383 {
384     ListElement *e = new ListElement;
385     elements.insert(index, e);
386 }
387
388 void ListModel::updateCacheIndices()
389 {
390     for (int i=0 ; i < elements.count() ; ++i) {
391         ListElement *e = elements.at(i);
392         if (e->m_objectCache) {
393             e->m_objectCache->m_elementIndex = i;
394         }
395     }
396 }
397
398 QVariant ListModel::getProperty(int elementIndex, int roleIndex, const QDeclarativeListModel *owner, QV8Engine *eng)
399 {
400     ListElement *e = elements[elementIndex];
401     const ListLayout::Role &r = m_layout->getExistingRole(roleIndex);
402     return e->getProperty(r, owner, eng);
403 }
404
405 ListModel *ListModel::getListProperty(int elementIndex, const ListLayout::Role &role)
406 {
407     ListElement *e = elements[elementIndex];
408     return e->getListProperty(role);
409 }
410
411 void ListModel::set(int elementIndex, v8::Handle<v8::Object> object, QList<int> *roles, QV8Engine *eng)
412 {
413     ListElement *e = elements[elementIndex];
414
415     v8::Local<v8::Array> propertyNames = object->GetPropertyNames();
416     int propertyCount = propertyNames->Length();
417
418     for (int i=0 ; i < propertyCount ; ++i) {
419         v8::Local<v8::String> propertyName = propertyNames->Get(i)->ToString();
420         v8::Local<v8::Value> propertyValue = object->Get(propertyName);
421
422         // Check if this key exists yet
423         int roleIndex = -1;
424
425         // Add the value now
426         if (propertyValue->IsString()) {
427             const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String);
428             v8::Handle<v8::String> jsString = propertyValue->ToString();
429             QString qstr;
430             qstr.resize(jsString->Length());
431             jsString->Write(reinterpret_cast<uint16_t*>(qstr.data()));
432             roleIndex = e->setStringProperty(r, qstr);
433         } else if (propertyValue->IsNumber()) {
434             const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number);
435             roleIndex = e->setDoubleProperty(r, propertyValue->NumberValue());
436         } else if (propertyValue->IsArray()) {
437             const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
438             ListModel *subModel = new ListModel(r.subLayout, 0, -1);
439
440             v8::Handle<v8::Array> subArray = v8::Handle<v8::Array>::Cast(propertyValue);
441             int arrayLength = subArray->Length();
442             for (int j=0 ; j < arrayLength ; ++j) {
443                 v8::Handle<v8::Object> subObject = subArray->Get(j)->ToObject();
444                 subModel->append(subObject, eng);
445             }
446
447             roleIndex = e->setListProperty(r, subModel);
448         } else if (propertyValue->IsBoolean()) {
449             const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool);
450             roleIndex = e->setBoolProperty(r, propertyValue->BooleanValue());
451         } else if (propertyValue->IsObject()) {
452             QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource();
453             if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
454                 QObject *o = QV8QObjectWrapper::toQObject(r);
455                 const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
456                 if (role.type == ListLayout::Role::QObject)
457                     roleIndex = e->setQObjectProperty(role, o);
458             } else {
459                 const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap);
460                 if (role.type == ListLayout::Role::VariantMap)
461                     roleIndex = e->setVariantMapProperty(role, propertyValue->ToObject(), eng);
462             }
463         } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) {
464             const ListLayout::Role *r = m_layout->getExistingRole(propertyName);
465             if (r)
466                 e->clearProperty(*r);
467         }
468
469         if (roleIndex != -1)
470             roles->append(roleIndex);
471     }
472
473     if (e->m_objectCache) {
474         e->m_objectCache->updateValues(*roles);
475     }
476 }
477
478 void ListModel::set(int elementIndex, v8::Handle<v8::Object> object, QV8Engine *eng)
479 {
480     ListElement *e = elements[elementIndex];
481
482     v8::Local<v8::Array> propertyNames = object->GetPropertyNames();
483     int propertyCount = propertyNames->Length();
484
485     for (int i=0 ; i < propertyCount ; ++i) {
486         v8::Local<v8::String> propertyName = propertyNames->Get(i)->ToString();
487         v8::Local<v8::Value> propertyValue = object->Get(propertyName);
488
489         // Add the value now
490         if (propertyValue->IsString()) {
491             const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String);
492             if (r.type == ListLayout::Role::String) {
493                 v8::Handle<v8::String> jsString = propertyValue->ToString();
494                 QString qstr;
495                 qstr.resize(jsString->Length());
496                 jsString->Write(reinterpret_cast<uint16_t*>(qstr.data()));
497                 e->setStringPropertyFast(r, qstr);
498             }
499         } else if (propertyValue->IsNumber()) {
500             const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number);
501             if (r.type == ListLayout::Role::Number) {
502                 e->setDoublePropertyFast(r, propertyValue->NumberValue());
503             }
504         } else if (propertyValue->IsArray()) {
505             const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
506             if (r.type == ListLayout::Role::List) {
507                 ListModel *subModel = new ListModel(r.subLayout, 0, -1);
508
509                 v8::Handle<v8::Array> subArray = v8::Handle<v8::Array>::Cast(propertyValue);
510                 int arrayLength = subArray->Length();
511                 for (int j=0 ; j < arrayLength ; ++j) {
512                     v8::Handle<v8::Object> subObject = subArray->Get(j)->ToObject();
513                     subModel->append(subObject, eng);
514                 }
515
516                 e->setListPropertyFast(r, subModel);
517             }
518         } else if (propertyValue->IsBoolean()) {
519             const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool);
520             if (r.type == ListLayout::Role::Bool) {
521                 e->setBoolPropertyFast(r, propertyValue->BooleanValue());
522             }
523         } else if (propertyValue->IsObject()) {
524             QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource();
525             if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
526                 QObject *o = QV8QObjectWrapper::toQObject(r);
527                 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
528                 if (r.type == ListLayout::Role::QObject)
529                     e->setQObjectPropertyFast(r, o);
530             } else {
531                 const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap);
532                 if (role.type == ListLayout::Role::VariantMap)
533                     e->setVariantMapFast(role, propertyValue->ToObject(), eng);
534             }
535         } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) {
536             const ListLayout::Role *r = m_layout->getExistingRole(propertyName);
537             if (r)
538                 e->clearProperty(*r);
539         }
540     }
541 }
542
543 void ListModel::clear()
544 {
545     int elementCount = elements.count();
546     for (int i=0 ; i < elementCount ; ++i) {
547         elements[i]->destroy(m_layout);
548         delete elements[i];
549     }
550     elements.clear();
551 }
552
553 void ListModel::remove(int index, int count)
554 {
555     for (int i=0 ; i < count ; ++i) {
556         elements[index+i]->destroy(m_layout);
557         delete elements[index+i];
558     }
559     elements.remove(index, count);
560     updateCacheIndices();
561 }
562
563 void ListModel::insert(int elementIndex, v8::Handle<v8::Object> object, QV8Engine *eng)
564 {
565     insertElement(elementIndex);
566     set(elementIndex, object, eng);
567 }
568
569 int ListModel::append(v8::Handle<v8::Object> object, QV8Engine *eng)
570 {
571     int elementIndex = appendElement();
572     set(elementIndex, object, eng);
573     return elementIndex;
574 }
575
576 int ListModel::setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data)
577 {
578     int roleIndex = -1;
579
580     if (elementIndex >= 0 && elementIndex < elements.count()) {
581         ListElement *e = elements[elementIndex];
582
583         const ListLayout::Role *r = m_layout->getRoleOrCreate(key, data);
584         if (r) {
585             roleIndex = e->setVariantProperty(*r, data);
586
587             if (roleIndex != -1 && e->m_objectCache) {
588                 QList<int> roles;
589                 roles << roleIndex;
590                 e->m_objectCache->updateValues(roles);
591             }
592         }
593     }
594
595     return roleIndex;
596 }
597
598 int ListModel::setExistingProperty(int elementIndex, const QString &key, v8::Handle<v8::Value> data, QV8Engine *eng)
599 {
600     int roleIndex = -1;
601
602     if (elementIndex >= 0 && elementIndex < elements.count()) {
603         ListElement *e = elements[elementIndex];
604         const ListLayout::Role *r = m_layout->getExistingRole(key);
605         if (r)
606             roleIndex = e->setJsProperty(*r, data, eng);
607     }
608
609     return roleIndex;
610 }
611
612 inline char *ListElement::getPropertyMemory(const ListLayout::Role &role)
613 {
614     ListElement *e = this;
615     int blockIndex = 0;
616     while (blockIndex < role.blockIndex) {
617         if (e->next == 0) {
618             e->next = new ListElement;
619             e->next->uid = uid;
620         }
621         e = e->next;
622         ++blockIndex;
623     }
624
625     char *mem = &e->data[role.blockOffset];
626     return mem;
627 }
628
629 QString *ListElement::getStringProperty(const ListLayout::Role &role)
630 {
631     char *mem = getPropertyMemory(role);
632     QString *s = reinterpret_cast<QString *>(mem);
633     return s->data_ptr() ? s : 0;
634 }
635
636 QObject *ListElement::getQObjectProperty(const ListLayout::Role &role)
637 {
638     char *mem = getPropertyMemory(role);
639     QDeclarativeGuard<QObject> *o = reinterpret_cast<QDeclarativeGuard<QObject> *>(mem);
640     return o->data();
641 }
642
643 QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role)
644 {
645     QVariantMap *map = 0;
646
647     char *mem = getPropertyMemory(role);
648     if (isMemoryUsed<QVariantMap>(mem))
649         map = reinterpret_cast<QVariantMap *>(mem);
650
651     return map;
652 }
653
654 QDeclarativeGuard<QObject> *ListElement::getGuardProperty(const ListLayout::Role &role)
655 {
656     char *mem = getPropertyMemory(role);
657
658     bool existingGuard = false;
659     for (size_t i=0 ; i < sizeof(QDeclarativeGuard<QObject>) ; ++i) {
660         if (mem[i] != 0) {
661             existingGuard = true;
662             break;
663         }
664     }
665
666     QDeclarativeGuard<QObject> *o = 0;
667
668     if (existingGuard)
669         o = reinterpret_cast<QDeclarativeGuard<QObject> *>(mem);
670
671     return o;
672 }
673
674 ListModel *ListElement::getListProperty(const ListLayout::Role &role)
675 {
676     char *mem = getPropertyMemory(role);
677     ListModel **value = reinterpret_cast<ListModel **>(mem);
678     return *value;
679 }
680
681 QVariant ListElement::getProperty(const ListLayout::Role &role, const QDeclarativeListModel *owner, QV8Engine *eng)
682 {
683     char *mem = getPropertyMemory(role);
684
685     QVariant data;
686
687     switch (role.type) {
688         case ListLayout::Role::Number:
689             {
690                 double *value = reinterpret_cast<double *>(mem);
691                 data = *value;
692             }
693             break;
694         case ListLayout::Role::String:
695             {
696                 QString *value = reinterpret_cast<QString *>(mem);
697                 if (value->data_ptr() != 0)
698                     data = *value;
699             }
700             break;
701         case ListLayout::Role::Bool:
702             {
703                 bool *value = reinterpret_cast<bool *>(mem);
704                 data = *value;
705             }
706             break;
707         case ListLayout::Role::List:
708             {
709                 ListModel **value = reinterpret_cast<ListModel **>(mem);
710                 ListModel *model = *value;
711
712                 if (model) {
713                     if (model->m_modelCache == 0) {
714                         model->m_modelCache = new QDeclarativeListModel(owner, model, eng);
715                         QDeclarativeEngine::setContextForObject(model->m_modelCache, QDeclarativeEngine::contextForObject(owner));
716                     }
717
718                     QObject *object = model->m_modelCache;
719                     data = QVariant::fromValue(object);
720                 }
721             }
722             break;
723         case ListLayout::Role::QObject:
724             {
725                 QDeclarativeGuard<QObject> *guard = reinterpret_cast<QDeclarativeGuard<QObject> *>(mem);
726                 QObject *object = guard->data();
727                 if (object)
728                     data = QVariant::fromValue(object);
729             }
730             break;
731         case ListLayout::Role::VariantMap:
732             {
733                 if (isMemoryUsed<QVariantMap>(mem)) {
734                     QVariantMap *map = reinterpret_cast<QVariantMap *>(mem);
735                     data = *map;
736                 }
737             }
738             break;
739         default:
740             break;
741     }
742
743     return data;
744 }
745
746 int ListElement::setStringProperty(const ListLayout::Role &role, const QString &s)
747 {
748     int roleIndex = -1;
749
750     if (role.type == ListLayout::Role::String) {
751         char *mem = getPropertyMemory(role);
752         QString *c = reinterpret_cast<QString *>(mem);
753         bool changed;
754         if (c->data_ptr() == 0) {
755             new (mem) QString(s);
756             changed = true;
757         } else {
758             changed = c->compare(s) != 0;
759             *c = s;
760         }
761         if (changed)
762             roleIndex = role.index;
763     }
764
765     return roleIndex;
766 }
767
768 int ListElement::setDoubleProperty(const ListLayout::Role &role, double d)
769 {
770     int roleIndex = -1;
771
772     if (role.type == ListLayout::Role::Number) {
773         char *mem = getPropertyMemory(role);
774         double *value = new (mem) double;
775         bool changed = *value != d;
776         *value = d;
777         if (changed)
778             roleIndex = role.index;
779     }
780
781     return roleIndex;
782 }
783
784 int ListElement::setBoolProperty(const ListLayout::Role &role, bool b)
785 {
786     int roleIndex = -1;
787
788     if (role.type == ListLayout::Role::Bool) {
789         char *mem = getPropertyMemory(role);
790         bool *value = new (mem) bool;
791         bool changed = *value != b;
792         *value = b;
793         if (changed)
794             roleIndex = role.index;
795     }
796
797     return roleIndex;
798 }
799
800 int ListElement::setListProperty(const ListLayout::Role &role, ListModel *m)
801 {
802     int roleIndex = -1;
803
804     if (role.type == ListLayout::Role::List) {
805         char *mem = getPropertyMemory(role);
806         ListModel **value = new (mem) ListModel *;
807         if (*value) {
808             (*value)->destroy();
809             delete *value;
810         }
811         *value = m;
812         roleIndex = role.index;
813     }
814
815     return roleIndex;
816 }
817
818 int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o)
819 {
820     int roleIndex = -1;
821
822     if (role.type == ListLayout::Role::QObject) {
823         char *mem = getPropertyMemory(role);
824         QDeclarativeGuard<QObject> *g = reinterpret_cast<QDeclarativeGuard<QObject> *>(mem);
825         bool existingGuard = false;
826         for (size_t i=0 ; i < sizeof(QDeclarativeGuard<QObject>) ; ++i) {
827             if (mem[i] != 0) {
828                 existingGuard = true;
829                 break;
830             }
831         }
832         bool changed;
833         if (existingGuard) {
834             changed = g->data() != o;
835             g->~QDeclarativeGuard();
836         } else {
837             changed = true;
838         }
839         new (mem) QDeclarativeGuard<QObject>(o);
840         if (changed)
841             roleIndex = role.index;
842     }
843
844     return roleIndex;
845 }
846
847 int ListElement::setVariantMapProperty(const ListLayout::Role &role, v8::Handle<v8::Object> o, QV8Engine *eng)
848 {
849     int roleIndex = -1;
850
851     if (role.type == ListLayout::Role::VariantMap) {
852         char *mem = getPropertyMemory(role);
853         if (isMemoryUsed<QVariantMap>(mem)) {
854             QVariantMap *map = reinterpret_cast<QVariantMap *>(mem);
855             map->~QMap();
856         }
857         new (mem) QVariantMap(eng->variantMapFromJS(o));
858         roleIndex = role.index;
859     }
860
861     return roleIndex;
862 }
863
864 int ListElement::setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m)
865 {
866     int roleIndex = -1;
867
868     if (role.type == ListLayout::Role::VariantMap) {
869         char *mem = getPropertyMemory(role);
870         if (isMemoryUsed<QVariantMap>(mem)) {
871             QVariantMap *map = reinterpret_cast<QVariantMap *>(mem);
872             map->~QMap();
873         }
874         if (m)
875             new (mem) QVariantMap(*m);
876         else
877             new (mem) QVariantMap;
878         roleIndex = role.index;
879     }
880
881     return roleIndex;
882 }
883
884 void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s)
885 {
886     char *mem = getPropertyMemory(role);
887     new (mem) QString(s);
888 }
889
890 void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d)
891 {
892     char *mem = getPropertyMemory(role);
893     double *value = new (mem) double;
894     *value = d;
895 }
896
897 void ListElement::setBoolPropertyFast(const ListLayout::Role &role, bool b)
898 {
899     char *mem = getPropertyMemory(role);
900     bool *value = new (mem) bool;
901     *value = b;
902 }
903
904 void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QObject *o)
905 {
906     char *mem = getPropertyMemory(role);
907     new (mem) QDeclarativeGuard<QObject>(o);
908 }
909
910 void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m)
911 {
912     char *mem = getPropertyMemory(role);
913     ListModel **value = new (mem) ListModel *;
914     *value = m;
915 }
916
917 void ListElement::setVariantMapFast(const ListLayout::Role &role, v8::Handle<v8::Object> o, QV8Engine *eng)
918 {
919     char *mem = getPropertyMemory(role);
920     QVariantMap *map = new (mem) QVariantMap;
921     *map = eng->variantMapFromJS(o);
922 }
923
924 void ListElement::clearProperty(const ListLayout::Role &role)
925 {
926     switch (role.type) {
927     case ListLayout::Role::String:
928         setStringProperty(role, QString());
929         break;
930     case ListLayout::Role::Number:
931         setDoubleProperty(role, 0.0);
932         break;
933     case ListLayout::Role::Bool:
934         setBoolProperty(role, false);
935         break;
936     case ListLayout::Role::List:
937         setListProperty(role, 0);
938         break;
939     case ListLayout::Role::QObject:
940         setQObjectProperty(role, 0);
941         break;
942     case ListLayout::Role::VariantMap:
943         setVariantMapProperty(role, 0);
944         break;
945     default:
946         break;
947     }
948 }
949
950 ListElement::ListElement()
951 {
952     m_objectCache = 0;
953     uid = uidCounter.fetchAndAddOrdered(1);
954     next = 0;
955     qMemSet(data, 0, sizeof(data));
956 }
957
958 ListElement::ListElement(int existingUid)
959 {
960     m_objectCache = 0;
961     uid = existingUid;
962     next = 0;
963     qMemSet(data, 0, sizeof(data));
964 }
965
966 ListElement::~ListElement()
967 {
968     delete next;
969 }
970
971 void ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash<int, ListModel *> *targetModelHash)
972 {
973     for (int i=0 ; i < srcLayout->roleCount() ; ++i) {
974         const ListLayout::Role &srcRole = srcLayout->getExistingRole(i);
975         const ListLayout::Role &targetRole = targetLayout->getExistingRole(i);
976
977         switch (srcRole.type) {
978             case ListLayout::Role::List:
979                 {
980                     ListModel *srcSubModel = src->getListProperty(srcRole);
981                     ListModel *targetSubModel = target->getListProperty(targetRole);
982
983                     if (srcSubModel) {
984                         if (targetSubModel == 0) {
985                             targetSubModel = new ListModel(targetRole.subLayout, 0, srcSubModel->getUid());
986                             target->setListPropertyFast(targetRole, targetSubModel);
987                         }
988                         ListModel::sync(srcSubModel, targetSubModel, targetModelHash);
989                     }
990                 }
991                 break;
992             case ListLayout::Role::QObject:
993                 {
994                     QObject *object = src->getQObjectProperty(srcRole);
995                     target->setQObjectProperty(targetRole, object);
996                 }
997                 break;
998             case ListLayout::Role::String:
999             case ListLayout::Role::Number:
1000             case ListLayout::Role::Bool:
1001                 {
1002                     QVariant v = src->getProperty(srcRole, 0, 0);
1003                     target->setVariantProperty(targetRole, v);
1004                 }
1005             case ListLayout::Role::VariantMap:
1006                 {
1007                     QVariantMap *map = src->getVariantMapProperty(srcRole);
1008                     target->setVariantMapProperty(targetRole, map);
1009                 }
1010                 break;
1011             default:
1012                 break;
1013         }
1014     }
1015
1016 }
1017
1018 void ListElement::destroy(ListLayout *layout)
1019 {
1020     if (layout) {
1021         for (int i=0 ; i < layout->roleCount() ; ++i) {
1022             const ListLayout::Role &r = layout->getExistingRole(i);
1023
1024             switch (r.type) {
1025                 case ListLayout::Role::String:
1026                     {
1027                         QString *string = getStringProperty(r);
1028                         if (string)
1029                             string->~QString();
1030                     }
1031                     break;
1032                 case ListLayout::Role::List:
1033                     {
1034                         ListModel *model = getListProperty(r);
1035                         if (model) {
1036                             model->destroy();
1037                             delete model;
1038                         }
1039                     }
1040                     break;
1041                 case ListLayout::Role::QObject:
1042                     {
1043                         QDeclarativeGuard<QObject> *guard = getGuardProperty(r);
1044                         if (guard)
1045                             guard->~QDeclarativeGuard();
1046                     }
1047                     break;
1048                 case ListLayout::Role::VariantMap:
1049                     {
1050                         QVariantMap *map = getVariantMapProperty(r);
1051                         if (map)
1052                             map->~QMap();
1053                     }
1054                     break;
1055                 default:
1056                     // other types don't need explicit cleanup.
1057                     break;
1058             }
1059         }
1060
1061         delete m_objectCache;
1062     }
1063
1064     if (next)
1065         next->destroy(0);
1066     uid = -1;
1067 }
1068
1069 int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant &d)
1070 {
1071     int roleIndex = -1;
1072
1073     switch (role.type) {
1074         case ListLayout::Role::Number:
1075             roleIndex = setDoubleProperty(role, d.toDouble());
1076             break;
1077         case ListLayout::Role::String:
1078             roleIndex = setStringProperty(role, d.toString());
1079             break;
1080         case ListLayout::Role::Bool:
1081             roleIndex = setBoolProperty(role, d.toBool());
1082             break;
1083         case ListLayout::Role::List:
1084             roleIndex = setListProperty(role, d.value<ListModel *>());
1085             break;
1086         case ListLayout::Role::VariantMap: {
1087                 QVariantMap map = d.toMap();
1088                 roleIndex = setVariantMapProperty(role, &map);
1089             }
1090             break;
1091         default:
1092             break;
1093     }
1094
1095     return roleIndex;
1096 }
1097
1098 int ListElement::setJsProperty(const ListLayout::Role &role, v8::Handle<v8::Value> d, QV8Engine *eng)
1099 {
1100     // Check if this key exists yet
1101     int roleIndex = -1;
1102
1103     // Add the value now
1104     if (d->IsString()) {
1105         v8::Handle<v8::String> jsString = d->ToString();
1106         QString qstr;
1107         qstr.resize(jsString->Length());
1108         jsString->Write(reinterpret_cast<uint16_t*>(qstr.data()));
1109         roleIndex = setStringProperty(role, qstr);
1110     } else if (d->IsNumber()) {
1111         roleIndex = setDoubleProperty(role, d->NumberValue());
1112     } else if (d->IsArray()) {
1113         ListModel *subModel = new ListModel(role.subLayout, 0, -1);
1114         v8::Handle<v8::Array> subArray = v8::Handle<v8::Array>::Cast(d);
1115         int arrayLength = subArray->Length();
1116         for (int j=0 ; j < arrayLength ; ++j) {
1117             v8::Handle<v8::Object> subObject = subArray->Get(j)->ToObject();
1118             subModel->append(subObject, eng);
1119         }
1120         roleIndex = setListProperty(role, subModel);
1121     } else if (d->IsBoolean()) {
1122         roleIndex = setBoolProperty(role, d->BooleanValue());
1123     } else if (d->IsObject()) {
1124         QV8ObjectResource *r = (QV8ObjectResource *) d->ToObject()->GetExternalResource();
1125         if (role.type == ListLayout::Role::QObject && r && r->resourceType() == QV8ObjectResource::QObjectType) {
1126             QObject *o = QV8QObjectWrapper::toQObject(r);
1127             roleIndex = setQObjectProperty(role, o);
1128         } else if (role.type == ListLayout::Role::VariantMap) {
1129             roleIndex = setVariantMapProperty(role, d->ToObject(), eng);
1130         }
1131     } else if (d.IsEmpty() || d->IsUndefined() || d->IsNull()) {
1132         clearProperty(role);
1133     }
1134
1135     return roleIndex;
1136 }
1137
1138 ModelObject::ModelObject(QDeclarativeListModel *model, int elementIndex)
1139 : m_model(model), m_elementIndex(elementIndex), m_meta(new ModelNodeMetaObject(this))
1140 {
1141     updateValues();
1142     setNodeUpdatesEnabled(true);
1143 }
1144
1145 void ModelObject::updateValues()
1146 {
1147     int roleCount = m_model->m_listModel->roleCount();
1148     for (int i=0 ; i < roleCount ; ++i) {
1149         const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i);
1150         QByteArray name = role.name.toUtf8();
1151         const QVariant &data = m_model->data(m_elementIndex, i);
1152         setValue(name, data, role.type == ListLayout::Role::List);
1153     }
1154 }
1155
1156 void ModelObject::updateValues(const QList<int> &roles)
1157 {
1158     int roleCount = roles.count();
1159     for (int i=0 ; i < roleCount ; ++i) {
1160         int roleIndex = roles.at(i);
1161         const ListLayout::Role &role = m_model->m_listModel->getExistingRole(roleIndex);
1162         QByteArray name = role.name.toUtf8();
1163         const QVariant &data = m_model->data(m_elementIndex, roleIndex);
1164         setValue(name, data, role.type == ListLayout::Role::List);
1165     }
1166 }
1167
1168 ModelNodeMetaObject::ModelNodeMetaObject(ModelObject *object)
1169 : QDeclarativeOpenMetaObject(object), m_enabled(false), m_obj(object)
1170 {
1171 }
1172
1173 ModelNodeMetaObject::~ModelNodeMetaObject()
1174 {
1175 }
1176
1177 void ModelNodeMetaObject::propertyWritten(int index)
1178 {
1179     if (!m_enabled)
1180         return;
1181
1182     QV8Engine *eng = m_obj->m_model->engine();
1183
1184     QString propName = QString::fromUtf8(name(index));
1185     QVariant value = operator[](index);
1186
1187     v8::HandleScope handle_scope;
1188     v8::Context::Scope scope(eng->context());
1189
1190     v8::Handle<v8::Value> v = eng->fromVariant(value);
1191
1192     int roleIndex = m_obj->m_model->m_listModel->setExistingProperty(m_obj->m_elementIndex, propName, v, eng);
1193     if (roleIndex != -1) {
1194         QList<int> roles;
1195         roles << roleIndex;
1196         m_obj->m_model->emitItemsChanged(m_obj->m_elementIndex, 1, roles);
1197     }
1198 }
1199
1200 DynamicRoleModelNode::DynamicRoleModelNode(QDeclarativeListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this))
1201 {
1202     setNodeUpdatesEnabled(true);
1203 }
1204
1205 DynamicRoleModelNode *DynamicRoleModelNode::create(const QVariantMap &obj, QDeclarativeListModel *owner)
1206 {
1207     DynamicRoleModelNode *object = new DynamicRoleModelNode(owner, uidCounter.fetchAndAddOrdered(1));
1208     QList<int> roles;
1209     object->updateValues(obj, roles);
1210     return object;
1211 }
1212
1213 void DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash<int, QDeclarativeListModel *> *targetModelHash)
1214 {
1215     for (int i=0 ; i < src->m_meta->count() ; ++i) {
1216         const QByteArray &name = src->m_meta->name(i);
1217         QVariant value = src->m_meta->value(i);
1218
1219         QDeclarativeListModel *srcModel = qobject_cast<QDeclarativeListModel *>(value.value<QObject *>());
1220         QDeclarativeListModel *targetModel = qobject_cast<QDeclarativeListModel *>(target->m_meta->value(i).value<QObject *>());
1221
1222         if (srcModel) {
1223             if (targetModel == 0)
1224                 targetModel = QDeclarativeListModel::createWithOwner(target->m_owner);
1225
1226             QDeclarativeListModel::sync(srcModel, targetModel, targetModelHash);
1227
1228             QObject *targetModelObject = targetModel;
1229             value = QVariant::fromValue(targetModelObject);
1230         } else if (targetModel) {
1231             delete targetModel;
1232         }
1233
1234         target->setValue(name, value);
1235     }
1236 }
1237
1238 void DynamicRoleModelNode::updateValues(const QVariantMap &object, QList<int> &roles)
1239 {
1240     const QList<QString> &keys = object.keys();
1241
1242     QList<QString>::const_iterator it = keys.begin();
1243     QList<QString>::const_iterator end = keys.end();
1244
1245     while (it != end) {
1246         const QString &key = *it;
1247
1248         int roleIndex = m_owner->m_roles.indexOf(key);
1249         if (roleIndex == -1) {
1250             roleIndex = m_owner->m_roles.count();
1251             m_owner->m_roles.append(key);
1252         }
1253
1254         QVariant value = object[key];
1255
1256         if (value.type() == QVariant::List) {
1257             QDeclarativeListModel *subModel = QDeclarativeListModel::createWithOwner(m_owner);
1258
1259             QVariantList subArray = value.toList();
1260             QVariantList::const_iterator subIt = subArray.begin();
1261             QVariantList::const_iterator subEnd = subArray.end();
1262             while (subIt != subEnd) {
1263                 const QVariantMap &subObject = subIt->toMap();
1264                 subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1265                 ++subIt;
1266             }
1267
1268             QObject *subModelObject = subModel;
1269             value = QVariant::fromValue(subModelObject);
1270         }
1271
1272         const QByteArray &keyUtf8 = key.toUtf8();
1273
1274         QDeclarativeListModel *existingModel = qobject_cast<QDeclarativeListModel *>(m_meta->value(keyUtf8).value<QObject *>());
1275         if (existingModel)
1276             delete existingModel;
1277
1278         if (m_meta->setValue(keyUtf8, value))
1279             roles << roleIndex;
1280
1281         ++it;
1282     }
1283 }
1284
1285 DynamicRoleModelNodeMetaObject::DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object)
1286     : QDeclarativeOpenMetaObject(object), m_enabled(false), m_owner(object)
1287 {
1288 }
1289
1290 DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject()
1291 {
1292     for (int i=0 ; i < count() ; ++i) {
1293         QDeclarativeListModel *subModel = qobject_cast<QDeclarativeListModel *>(value(i).value<QObject *>());
1294         if (subModel)
1295             delete subModel;
1296     }
1297 }
1298
1299 void DynamicRoleModelNodeMetaObject::propertyWrite(int index)
1300 {
1301     if (!m_enabled)
1302         return;
1303
1304     QVariant v = value(index);
1305     QDeclarativeListModel *model = qobject_cast<QDeclarativeListModel *>(v.value<QObject *>());
1306     if (model)
1307         delete model;
1308 }
1309
1310 void DynamicRoleModelNodeMetaObject::propertyWritten(int index)
1311 {
1312     if (!m_enabled)
1313         return;
1314
1315     QDeclarativeListModel *parentModel = m_owner->m_owner;
1316
1317     QVariant v = value(index);
1318     if (v.type() == QVariant::List) {
1319         QDeclarativeListModel *subModel = QDeclarativeListModel::createWithOwner(parentModel);
1320
1321         QVariantList subArray = v.toList();
1322         QVariantList::const_iterator subIt = subArray.begin();
1323         QVariantList::const_iterator subEnd = subArray.end();
1324         while (subIt != subEnd) {
1325             const QVariantMap &subObject = subIt->toMap();
1326             subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1327             ++subIt;
1328         }
1329
1330         QObject *subModelObject = subModel;
1331         v = QVariant::fromValue(subModelObject);
1332
1333         setValue(index, v);
1334     }
1335
1336     int elementIndex = parentModel->m_modelObjects.indexOf(m_owner);
1337     int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData()));
1338
1339     if (elementIndex != -1 && roleIndex != -1) {
1340         QList<int> roles;
1341         roles << roleIndex;
1342
1343         parentModel->emitItemsChanged(elementIndex, 1, roles);
1344     }
1345 }
1346
1347 QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListModelData::instructions() const
1348 {
1349     return (QDeclarativeListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData));
1350 }
1351
1352 /*!
1353     \qmlclass ListModel QDeclarativeListModel
1354     \inqmlmodule QtQuick 2
1355     \ingroup qml-working-with-data
1356     \brief The ListModel element defines a free-form list data source.
1357
1358     The ListModel is a simple container of ListElement definitions, each containing data roles.
1359     The contents can be defined dynamically, or explicitly in QML.
1360
1361     The number of elements in the model can be obtained from its \l count property.
1362     A number of familiar methods are also provided to manipulate the contents of the
1363     model, including append(), insert(), move(), remove() and set(). These methods
1364     accept dictionaries as their arguments; these are translated to ListElement objects
1365     by the model.
1366
1367     Elements can be manipulated via the model using the setProperty() method, which
1368     allows the roles of the specified element to be set and changed.
1369
1370     \section1 Example Usage
1371
1372     The following example shows a ListModel containing three elements, with the roles
1373     "name" and "cost".
1374
1375     \div {class="float-right"}
1376     \inlineimage listmodel.png
1377     \enddiv
1378
1379     \snippet doc/src/snippets/declarative/listmodel.qml 0
1380
1381     \clearfloat
1382     Roles (properties) in each element must begin with a lower-case letter and
1383     should be common to all elements in a model. The ListElement documentation
1384     provides more guidelines for how elements should be defined.
1385
1386     Since the example model contains an \c id property, it can be referenced
1387     by views, such as the ListView in this example:
1388
1389     \snippet doc/src/snippets/declarative/listmodel-simple.qml 0
1390     \dots 8
1391     \snippet doc/src/snippets/declarative/listmodel-simple.qml 1
1392
1393     It is possible for roles to contain list data.  In the following example we
1394     create a list of fruit attributes:
1395
1396     \snippet doc/src/snippets/declarative/listmodel-nested.qml model
1397
1398     The delegate displays all the fruit attributes:
1399
1400     \div {class="float-right"}
1401     \inlineimage listmodel-nested.png
1402     \enddiv
1403
1404     \snippet doc/src/snippets/declarative/listmodel-nested.qml delegate
1405
1406     \clearfloat
1407     \section1 Modifying List Models
1408
1409     The content of a ListModel may be created and modified using the clear(),
1410     append(), set(), insert() and setProperty() methods.  For example:
1411
1412     \snippet doc/src/snippets/declarative/listmodel-modify.qml delegate
1413
1414     Note that when creating content dynamically the set of available properties
1415     cannot be changed once set. Whatever properties are first added to the model
1416     are the only permitted properties in the model.
1417
1418     \section1 Using Threaded List Models with WorkerScript
1419
1420     ListModel can be used together with WorkerScript access a list model
1421     from multiple threads. This is useful if list modifications are
1422     synchronous and take some time: the list operations can be moved to a
1423     different thread to avoid blocking of the main GUI thread.
1424
1425     Here is an example that uses WorkerScript to periodically append the
1426     current time to a list model:
1427
1428     \snippet examples/declarative/threading/threadedlistmodel/timedisplay.qml 0
1429
1430     The included file, \tt dataloader.js, looks like this:
1431
1432     \snippet examples/declarative/threading/threadedlistmodel/dataloader.js 0
1433
1434     The timer in the main example sends messages to the worker script by calling
1435     \l WorkerScript::sendMessage(). When this message is received,
1436     \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js,
1437     which appends the current time to the list model.
1438
1439     Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()}
1440     handler. You must call sync() or else the changes made to the list from the external
1441     thread will not be reflected in the list model in the main thread.
1442
1443     \sa {qmlmodels}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtDeclarative
1444 */
1445
1446 QDeclarativeListModel::QDeclarativeListModel(QObject *parent)
1447 : QListModelInterface(parent)
1448 {
1449     m_mainThread = true;
1450     m_primary = true;
1451     m_agent = 0;
1452     m_uid = uidCounter.fetchAndAddOrdered(1);
1453     m_dynamicRoles = false;
1454
1455     m_layout = new ListLayout;
1456     m_listModel = new ListModel(m_layout, this, -1);
1457
1458     m_engine = 0;
1459 }
1460
1461 QDeclarativeListModel::QDeclarativeListModel(const QDeclarativeListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent)
1462 : QListModelInterface(parent)
1463 {
1464     m_mainThread = owner->m_mainThread;
1465     m_primary = false;
1466     m_agent = owner->m_agent;
1467
1468     Q_ASSERT(owner->m_dynamicRoles == false);
1469     m_dynamicRoles = false;
1470     m_layout = 0;
1471     m_listModel = data;
1472
1473     m_engine = eng;
1474 }
1475
1476 QDeclarativeListModel::QDeclarativeListModel(QDeclarativeListModel *orig, QDeclarativeListModelWorkerAgent *agent)
1477 : QListModelInterface(agent)
1478 {
1479     m_mainThread = false;
1480     m_primary = true;
1481     m_agent = agent;
1482     m_dynamicRoles = orig->m_dynamicRoles;
1483
1484     m_layout = new ListLayout(orig->m_layout);
1485     m_listModel = new ListModel(m_layout, this, orig->m_listModel->getUid());
1486
1487     if (m_dynamicRoles)
1488         sync(orig, this, 0);
1489     else
1490         ListModel::sync(orig->m_listModel, m_listModel, 0);
1491
1492     m_engine = 0;
1493 }
1494
1495 QDeclarativeListModel::~QDeclarativeListModel()
1496 {
1497     for (int i=0 ; i < m_modelObjects.count() ; ++i)
1498         delete m_modelObjects[i];
1499
1500     if (m_primary) {
1501         m_listModel->destroy();
1502         delete m_listModel;
1503
1504         if (m_mainThread && m_agent)
1505             m_agent->release();
1506     }
1507
1508     m_listModel = 0;
1509
1510     delete m_layout;
1511     m_layout = 0;
1512 }
1513
1514 QDeclarativeListModel *QDeclarativeListModel::createWithOwner(QDeclarativeListModel *newOwner)
1515 {
1516     QDeclarativeListModel *model = new QDeclarativeListModel;
1517
1518     model->m_mainThread = newOwner->m_mainThread;
1519     model->m_engine = newOwner->m_engine;
1520     model->m_agent = newOwner->m_agent;
1521     model->m_dynamicRoles = newOwner->m_dynamicRoles;
1522
1523     QDeclarativeEngine::setContextForObject(model, QDeclarativeEngine::contextForObject(newOwner));
1524
1525     return model;
1526 }
1527
1528 QV8Engine *QDeclarativeListModel::engine() const
1529 {
1530     if (m_engine == 0) {
1531         m_engine  = QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this));
1532     }
1533
1534     return m_engine;
1535 }
1536
1537 void QDeclarativeListModel::sync(QDeclarativeListModel *src, QDeclarativeListModel *target, QHash<int, QDeclarativeListModel *> *targetModelHash)
1538 {
1539     Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles);
1540
1541     target->m_uid = src->m_uid;
1542     if (targetModelHash)
1543         targetModelHash->insert(target->m_uid, target);
1544     target->m_roles = src->m_roles;
1545
1546     // Build hash of elements <-> uid for each of the lists
1547     QHash<int, ElementSync> elementHash;
1548     for (int i=0 ; i < target->m_modelObjects.count() ; ++i) {
1549         DynamicRoleModelNode *e = target->m_modelObjects.at(i);
1550         int uid = e->getUid();
1551         ElementSync sync;
1552         sync.target = e;
1553         elementHash.insert(uid, sync);
1554     }
1555     for (int i=0 ; i < src->m_modelObjects.count() ; ++i) {
1556         DynamicRoleModelNode *e = src->m_modelObjects.at(i);
1557         int uid = e->getUid();
1558
1559         QHash<int, ElementSync>::iterator it = elementHash.find(uid);
1560         if (it == elementHash.end()) {
1561             ElementSync sync;
1562             sync.src = e;
1563             elementHash.insert(uid, sync);
1564         } else {
1565             ElementSync &sync = it.value();
1566             sync.src = e;
1567         }
1568     }
1569
1570     // Get list of elements that are in the target but no longer in the source. These get deleted first.
1571     QHash<int, ElementSync>::iterator it = elementHash.begin();
1572     QHash<int, ElementSync>::iterator end = elementHash.end();
1573     while (it != end) {
1574         const ElementSync &s = it.value();
1575         if (s.src == 0) {
1576             int targetIndex = target->m_modelObjects.indexOf(s.target);
1577             target->m_modelObjects.remove(targetIndex, 1);
1578             delete s.target;
1579         }
1580         ++it;
1581     }
1582
1583     // Clear the target list, and append in correct order from the source
1584     target->m_modelObjects.clear();
1585     for (int i=0 ; i < src->m_modelObjects.count() ; ++i) {
1586         DynamicRoleModelNode *srcElement = src->m_modelObjects.at(i);
1587         it = elementHash.find(srcElement->getUid());
1588         const ElementSync &s = it.value();
1589         DynamicRoleModelNode *targetElement = s.target;
1590         if (targetElement == 0) {
1591             targetElement = new DynamicRoleModelNode(target, srcElement->getUid());
1592         }
1593         DynamicRoleModelNode::sync(srcElement, targetElement, targetModelHash);
1594         target->m_modelObjects.append(targetElement);
1595     }
1596 }
1597
1598 void QDeclarativeListModel::emitItemsChanged(int index, int count, const QList<int> &roles)
1599 {
1600     if (m_mainThread) {
1601         emit itemsChanged(index, count, roles);
1602     } else {
1603         int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
1604         m_agent->data.changedChange(uid, index, count, roles);
1605     }
1606 }
1607
1608 void QDeclarativeListModel::emitItemsRemoved(int index, int count)
1609 {
1610     if (m_mainThread) {
1611         emit itemsRemoved(index, count);
1612         emit countChanged();
1613     } else {
1614         int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
1615         if (index == 0 && count == this->count())
1616             m_agent->data.clearChange(uid);
1617         m_agent->data.removeChange(uid, index, count);
1618     }
1619 }
1620
1621 void QDeclarativeListModel::emitItemsInserted(int index, int count)
1622 {
1623     if (m_mainThread) {
1624         emit itemsInserted(index, count);
1625         emit countChanged();
1626     } else {
1627         int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
1628         m_agent->data.insertChange(uid, index, count);
1629     }
1630 }
1631
1632 void QDeclarativeListModel::emitItemsMoved(int from, int to, int n)
1633 {
1634     if (m_mainThread) {
1635         emit itemsMoved(from, to, n);
1636     } else {
1637         int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
1638         m_agent->data.moveChange(uid, from, n, to);
1639     }
1640 }
1641
1642 QDeclarativeListModelWorkerAgent *QDeclarativeListModel::agent()
1643 {
1644     if (m_agent)
1645         return m_agent;
1646
1647     m_agent = new QDeclarativeListModelWorkerAgent(this);
1648     return m_agent;
1649 }
1650
1651 QList<int> QDeclarativeListModel::roles() const
1652 {
1653     QList<int> rolesArray;
1654
1655     if (m_dynamicRoles) {
1656         for (int i=0 ; i < m_roles.count() ; ++i)
1657             rolesArray << i;
1658     } else {
1659         for (int i=0 ; i < m_listModel->roleCount() ; ++i)
1660             rolesArray << i;
1661     }
1662
1663     return rolesArray;
1664 }
1665
1666 QString QDeclarativeListModel::toString(int role) const
1667 {
1668     QString roleName;
1669
1670     if (m_dynamicRoles) {
1671         roleName = m_roles[role];
1672     } else {
1673         const ListLayout::Role &r = m_listModel->getExistingRole(role);
1674         roleName = r.name;
1675     }
1676
1677     return roleName;
1678 }
1679
1680 QVariant QDeclarativeListModel::data(int index, int role) const
1681 {
1682     QVariant v;
1683
1684     if (index >= count() || index < 0)
1685         return v;
1686
1687     if (m_dynamicRoles)
1688         v = m_modelObjects[index]->getValue(m_roles[role]);
1689     else
1690         v = m_listModel->getProperty(index, role, this, engine());
1691
1692     return v;
1693 }
1694
1695 /*!
1696     \qmlproperty bool QtQuick2::ListModel::dynamicRoles
1697
1698     By default, the type of a role is fixed the first time
1699     the role is used. For example, if you create a role called
1700     "data" and assign a number to it, you can no longer assign
1701     a string to the "data" role. However, when the dynamicRoles
1702     property is enabled, the type of a given role is not fixed
1703     and can be different between elements.
1704
1705     The dynamicRoles property must be set before any data is
1706     added to the ListModel, and must be set from the main
1707     thread.
1708
1709     A ListModel that has data statically defined (via the
1710     ListElement QML syntax) cannot have the dynamicRoles
1711     property enabled.
1712
1713     There is a significant performance cost to using a
1714     ListModel with dynamic roles enabled. The cost varies
1715     from platform to platform but is typically somewhere
1716     between 4-6x slower than using static role types.
1717
1718     Due to the performance cost of using dynamic roles,
1719     they are disabled by default.
1720 */
1721 void QDeclarativeListModel::setDynamicRoles(bool enableDynamicRoles)
1722 {
1723     if (m_mainThread && m_agent == 0) {
1724         if (enableDynamicRoles) {
1725             if (m_layout->roleCount())
1726                 qmlInfo(this) << tr("unable to enable dynamic roles as this model is not empty!");
1727             else
1728                 m_dynamicRoles = true;
1729         } else {
1730             if (m_roles.count()) {
1731                 qmlInfo(this) << tr("unable to enable static roles as this model is not empty!");
1732             } else {
1733                 m_dynamicRoles = false;
1734             }
1735         }
1736     } else {
1737         qmlInfo(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created");
1738     }
1739 }
1740
1741 /*!
1742     \qmlproperty int QtQuick2::ListModel::count
1743     The number of data entries in the model.
1744 */
1745 int QDeclarativeListModel::count() const
1746 {
1747     int count;
1748
1749     if (m_dynamicRoles)
1750         count = m_modelObjects.count();
1751     else {
1752         count = m_listModel->elementCount();
1753     }
1754
1755     return count;
1756 }
1757
1758 /*!
1759     \qmlmethod QtQuick2::ListModel::clear()
1760
1761     Deletes all content from the model.
1762
1763     \sa append() remove()
1764 */
1765 void QDeclarativeListModel::clear()
1766 {
1767     int cleared = count();
1768
1769     if (m_dynamicRoles) {
1770         for (int i=0 ; i < m_modelObjects.count() ; ++i)
1771             delete m_modelObjects[i];
1772         m_modelObjects.clear();
1773     } else {
1774         m_listModel->clear();
1775     }
1776
1777     emitItemsRemoved(0, cleared);
1778 }
1779
1780 /*!
1781     \qmlmethod QtQuick2::ListModel::remove(int index, int count = 1)
1782
1783     Deletes the content at \a index from the model.
1784
1785     \sa clear()
1786 */
1787 void QDeclarativeListModel::remove(QDeclarativeV8Function *args)
1788 {
1789     int argLength = args->Length();
1790
1791     if (argLength == 1 || argLength == 2) {
1792         int index = (*args)[0]->Int32Value();
1793         int removeCount = (argLength == 2 ? ((*args)[1]->Int32Value()) : 1);
1794
1795         if (index < 0 || index+removeCount > count() || removeCount <= 0) {
1796             qmlInfo(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count());
1797             return;
1798         }
1799
1800         if (m_dynamicRoles) {
1801             for (int i=0 ; i < removeCount ; ++i)
1802                 delete m_modelObjects[index+i];
1803             m_modelObjects.remove(index, removeCount);
1804         } else {
1805             m_listModel->remove(index, removeCount);
1806         }
1807
1808         emitItemsRemoved(index, removeCount);
1809     } else {
1810         qmlInfo(this) << tr("remove: incorrect number of arguments");
1811     }
1812 }
1813
1814 /*!
1815     \qmlmethod QtQuick2::ListModel::insert(int index, jsobject dict)
1816
1817     Adds a new item to the list model at position \a index, with the
1818     values in \a dict.
1819
1820     \code
1821         fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"})
1822     \endcode
1823
1824     The \a index must be to an existing item in the list, or one past
1825     the end of the list (equivalent to append).
1826
1827     \sa set() append()
1828 */
1829
1830 void QDeclarativeListModel::insert(QDeclarativeV8Function *args)
1831 {
1832     if (args->Length() == 2) {
1833
1834         v8::Handle<v8::Value> arg0 = (*args)[0];
1835         int index = arg0->Int32Value();
1836
1837         if (index < 0 || index > count()) {
1838             qmlInfo(this) << tr("insert: index %1 out of range").arg(index);
1839             return;
1840         }
1841
1842         v8::Handle<v8::Value> arg1 = (*args)[1];
1843
1844         if (arg1->IsArray()) {
1845             v8::Handle<v8::Array> objectArray = v8::Handle<v8::Array>::Cast(arg1);
1846             int objectArrayLength = objectArray->Length();
1847             for (int i=0 ; i < objectArrayLength ; ++i) {
1848                 v8::Handle<v8::Object> argObject = objectArray->Get(i)->ToObject();
1849
1850                 if (m_dynamicRoles) {
1851                     m_modelObjects.insert(index+i, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
1852                 } else {
1853                     m_listModel->insert(index+i, argObject, args->engine());
1854                 }
1855             }
1856             emitItemsInserted(index, objectArrayLength);
1857         } else if (arg1->IsObject()) {
1858             v8::Handle<v8::Object> argObject = arg1->ToObject();
1859
1860             if (m_dynamicRoles) {
1861                 m_modelObjects.insert(index, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
1862             } else {
1863                 m_listModel->insert(index, argObject, args->engine());
1864             }
1865
1866             emitItemsInserted(index, 1);
1867         } else {
1868             qmlInfo(this) << tr("insert: value is not an object");
1869         }
1870     } else {
1871         qmlInfo(this) << tr("insert: value is not an object");
1872     }
1873 }
1874
1875 /*!
1876     \qmlmethod QtQuick2::ListModel::move(int from, int to, int n)
1877
1878     Moves \a n items \a from one position \a to another.
1879
1880     The from and to ranges must exist; for example, to move the first 3 items
1881     to the end of the list:
1882
1883     \code
1884         fruitModel.move(0, fruitModel.count - 3, 3)
1885     \endcode
1886
1887     \sa append()
1888 */
1889 void QDeclarativeListModel::move(int from, int to, int n)
1890 {
1891     if (n==0 || from==to)
1892         return;
1893     if (!canMove(from, to, n)) {
1894         qmlInfo(this) << tr("move: out of range");
1895         return;
1896     }
1897
1898     if (m_dynamicRoles) {
1899
1900         int realFrom = from;
1901         int realTo = to;
1902         int realN = n;
1903
1904         if (from > to) {
1905             // Only move forwards - flip if backwards moving
1906             int tfrom = from;
1907             int tto = to;
1908             realFrom = tto;
1909             realTo = tto+n;
1910             realN = tfrom-tto;
1911         }
1912
1913         QPODVector<DynamicRoleModelNode *, 4> store;
1914         for (int i=0 ; i < (realTo-realFrom) ; ++i)
1915             store.append(m_modelObjects[realFrom+realN+i]);
1916         for (int i=0 ; i < realN ; ++i)
1917             store.append(m_modelObjects[realFrom+i]);
1918         for (int i=0 ; i < store.count() ; ++i)
1919             m_modelObjects[realFrom+i] = store[i];
1920
1921     } else {
1922         m_listModel->move(from, to, n);
1923     }
1924
1925     emitItemsMoved(from, to, n);
1926 }
1927
1928 /*!
1929     \qmlmethod QtQuick2::ListModel::append(jsobject dict)
1930
1931     Adds a new item to the end of the list model, with the
1932     values in \a dict.
1933
1934     \code
1935         fruitModel.append({"cost": 5.95, "name":"Pizza"})
1936     \endcode
1937
1938     \sa set() remove()
1939 */
1940 void QDeclarativeListModel::append(QDeclarativeV8Function *args)
1941 {
1942     if (args->Length() == 1) {
1943         v8::Handle<v8::Value> arg = (*args)[0];
1944
1945         if (arg->IsArray()) {
1946             v8::Handle<v8::Array> objectArray = v8::Handle<v8::Array>::Cast(arg);
1947             int objectArrayLength = objectArray->Length();
1948
1949             int index = count();
1950             for (int i=0 ; i < objectArrayLength ; ++i) {
1951                 v8::Handle<v8::Object> argObject = objectArray->Get(i)->ToObject();
1952
1953                 if (m_dynamicRoles) {
1954                     m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
1955                 } else {
1956                     m_listModel->append(argObject, args->engine());
1957                 }
1958             }
1959
1960             emitItemsInserted(index, objectArrayLength);
1961         } else if (arg->IsObject()) {
1962             v8::Handle<v8::Object> argObject = arg->ToObject();
1963
1964             int index;
1965
1966             if (m_dynamicRoles) {
1967                 index = m_modelObjects.count();
1968                 m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
1969             } else {
1970                 index = m_listModel->append(argObject, args->engine());
1971             }
1972
1973             emitItemsInserted(index, 1);
1974         } else {
1975             qmlInfo(this) << tr("append: value is not an object");
1976         }
1977     } else {
1978         qmlInfo(this) << tr("append: value is not an object");
1979     }
1980 }
1981
1982 /*!
1983     \qmlmethod object QtQuick2::ListModel::get(int index)
1984
1985     Returns the item at \a index in the list model. This allows the item
1986     data to be accessed or modified from JavaScript:
1987
1988     \code
1989     Component.onCompleted: {
1990         fruitModel.append({"cost": 5.95, "name":"Jackfruit"});
1991         console.log(fruitModel.get(0).cost);
1992         fruitModel.get(0).cost = 10.95;
1993     }
1994     \endcode
1995
1996     The \a index must be an element in the list.
1997
1998     Note that properties of the returned object that are themselves objects
1999     will also be models, and this get() method is used to access elements:
2000
2001     \code
2002         fruitModel.append(..., "attributes":
2003             [{"name":"spikes","value":"7mm"},
2004              {"name":"color","value":"green"}]);
2005         fruitModel.get(0).attributes.get(1).value; // == "green"
2006     \endcode
2007
2008     \warning The returned object is not guaranteed to remain valid. It
2009     should not be used in \l{Property Binding}{property bindings}.
2010
2011     \sa append()
2012 */
2013 QDeclarativeV8Handle QDeclarativeListModel::get(int index) const
2014 {
2015     v8::Handle<v8::Value> result = v8::Undefined();
2016
2017     if (index >= 0 && index < count()) {
2018         QV8Engine *v8engine = engine();
2019
2020         if (m_dynamicRoles) {
2021             DynamicRoleModelNode *object = m_modelObjects[index];
2022             result = v8engine->newQObject(object);
2023         } else {
2024             ModelObject *object = m_listModel->getOrCreateModelObject(const_cast<QDeclarativeListModel *>(this), index);
2025             result = v8engine->newQObject(object);
2026         }
2027     }
2028
2029     return QDeclarativeV8Handle::fromHandle(result);
2030 }
2031
2032 /*!
2033     \qmlmethod QtQuick2::ListModel::set(int index, jsobject dict)
2034
2035     Changes the item at \a index in the list model with the
2036     values in \a dict. Properties not appearing in \a dict
2037     are left unchanged.
2038
2039     \code
2040         fruitModel.set(3, {"cost": 5.95, "name":"Pizza"})
2041     \endcode
2042
2043     If \a index is equal to count() then a new item is appended to the
2044     list. Otherwise, \a index must be an element in the list.
2045
2046     \sa append()
2047 */
2048 void QDeclarativeListModel::set(int index, const QDeclarativeV8Handle &handle)
2049 {
2050     v8::Handle<v8::Value> valuemap = handle.toHandle();
2051
2052     if (!valuemap->IsObject() || valuemap->IsArray()) {
2053         qmlInfo(this) << tr("set: value is not an object");
2054         return;
2055     }
2056     if (index > count() || index < 0) {
2057         qmlInfo(this) << tr("set: index %1 out of range").arg(index);
2058         return;
2059     }
2060
2061     v8::Handle<v8::Object> object = valuemap->ToObject();
2062
2063     if (index == count()) {
2064
2065         if (m_dynamicRoles) {
2066             m_modelObjects.append(DynamicRoleModelNode::create(engine()->variantMapFromJS(object), this));
2067         } else {
2068             m_listModel->insert(index, object, engine());
2069         }
2070
2071         emitItemsInserted(index, 1);
2072     } else {
2073
2074         QList<int> roles;
2075
2076         if (m_dynamicRoles) {
2077             m_modelObjects[index]->updateValues(engine()->variantMapFromJS(object), roles);
2078         } else {
2079             m_listModel->set(index, object, &roles, engine());
2080         }
2081
2082         if (roles.count())
2083             emitItemsChanged(index, 1, roles);
2084     }
2085 }
2086
2087 /*!
2088     \qmlmethod QtQuick2::ListModel::setProperty(int index, string property, variant value)
2089
2090     Changes the \a property of the item at \a index in the list model to \a value.
2091
2092     \code
2093         fruitModel.setProperty(3, "cost", 5.95)
2094     \endcode
2095
2096     The \a index must be an element in the list.
2097
2098     \sa append()
2099 */
2100 void QDeclarativeListModel::setProperty(int index, const QString& property, const QVariant& value)
2101 {
2102     if (count() == 0 || index >= count() || index < 0) {
2103         qmlInfo(this) << tr("set: index %1 out of range").arg(index);
2104         return;
2105     }
2106
2107     if (m_dynamicRoles) {
2108         int roleIndex = m_roles.indexOf(property);
2109         if (roleIndex == -1) {
2110             roleIndex = m_roles.count();
2111             m_roles.append(property);
2112         }
2113         if (m_modelObjects[index]->setValue(property.toUtf8(), value)) {
2114             QList<int> roles;
2115             roles << roleIndex;
2116             emitItemsChanged(index, 1, roles);
2117         }
2118     } else {
2119         int roleIndex = m_listModel->setOrCreateProperty(index, property, value);
2120         if (roleIndex != -1) {
2121
2122             QList<int> roles;
2123             roles << roleIndex;
2124
2125             emitItemsChanged(index, 1, roles);
2126         }
2127     }
2128 }
2129
2130 /*!
2131     \qmlmethod QtQuick2::ListModel::sync()
2132
2133     Writes any unsaved changes to the list model after it has been modified
2134     from a worker script.
2135 */
2136 void QDeclarativeListModel::sync()
2137 {
2138     // This is just a dummy method to make it look like sync() exists in
2139     // ListModel (and not just QDeclarativeListModelWorkerAgent) and to let
2140     // us document sync().
2141     qmlInfo(this) << "List sync() can only be called from a WorkerScript";
2142 }
2143
2144 bool QDeclarativeListModelParser::compileProperty(const QDeclarativeCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data)
2145 {
2146     QList<QVariant> values = prop.assignedValues();
2147     for(int ii = 0; ii < values.count(); ++ii) {
2148         const QVariant &value = values.at(ii);
2149
2150         if(value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) {
2151             QDeclarativeCustomParserNode node =
2152                 qvariant_cast<QDeclarativeCustomParserNode>(value);
2153
2154             if (node.name() != listElementTypeName) {
2155                 const QMetaObject *mo = resolveType(node.name());
2156                 if (mo != &QDeclarativeListElement::staticMetaObject) {
2157                     error(node, QDeclarativeListModel::tr("ListElement: cannot contain nested elements"));
2158                     return false;
2159                 }
2160                 listElementTypeName = node.name(); // cache right name for next time
2161             }
2162
2163             {
2164             ListInstruction li;
2165             li.type = ListInstruction::Push;
2166             li.dataIdx = -1;
2167             instr << li;
2168             }
2169
2170             QList<QDeclarativeCustomParserProperty> props = node.properties();
2171             for(int jj = 0; jj < props.count(); ++jj) {
2172                 const QDeclarativeCustomParserProperty &nodeProp = props.at(jj);
2173                 if (nodeProp.name().isEmpty()) {
2174                     error(nodeProp, QDeclarativeListModel::tr("ListElement: cannot contain nested elements"));
2175                     return false;
2176                 }
2177                 if (nodeProp.name() == QStringLiteral("id")) {
2178                     error(nodeProp, QDeclarativeListModel::tr("ListElement: cannot use reserved \"id\" property"));
2179                     return false;
2180                 }
2181
2182                 ListInstruction li;
2183                 int ref = data.count();
2184                 data.append(nodeProp.name().toUtf8());
2185                 data.append('\0');
2186                 li.type = ListInstruction::Set;
2187                 li.dataIdx = ref;
2188                 instr << li;
2189
2190                 if(!compileProperty(nodeProp, instr, data))
2191                     return false;
2192
2193                 li.type = ListInstruction::Pop;
2194                 li.dataIdx = -1;
2195                 instr << li;
2196             }
2197
2198             {
2199             ListInstruction li;
2200             li.type = ListInstruction::Pop;
2201             li.dataIdx = -1;
2202             instr << li;
2203             }
2204
2205         } else {
2206
2207             QDeclarativeScript::Variant variant =
2208                 qvariant_cast<QDeclarativeScript::Variant>(value);
2209
2210             int ref = data.count();
2211
2212             QByteArray d;
2213             d += char(variant.type()); // type tag
2214             if (variant.isString()) {
2215                 d += variant.asString().toUtf8();
2216             } else if (variant.isNumber()) {
2217                 d += QByteArray::number(variant.asNumber(),'g',20);
2218             } else if (variant.isBoolean()) {
2219                 d += char(variant.asBoolean());
2220             } else if (variant.isScript()) {
2221                 if (definesEmptyList(variant.asScript())) {
2222                     d[0] = char(QDeclarativeScript::Variant::Invalid); // marks empty list
2223                 } else {
2224                     QByteArray script = variant.asScript().toUtf8();
2225                     int v = evaluateEnum(script);
2226                     if (v<0) {
2227                         using namespace QDeclarativeJS;
2228                         AST::Node *node = variant.asAST();
2229                         AST::StringLiteral *literal = 0;
2230                         if (AST::CallExpression *callExpr = AST::cast<AST::CallExpression *>(node)) {
2231                             if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(callExpr->base)) {
2232                                 if (idExpr->name == QLatin1String("QT_TR_NOOP") || idExpr->name == QLatin1String("QT_TRID_NOOP")) {
2233                                     if (callExpr->arguments && !callExpr->arguments->next)
2234                                         literal = AST::cast<AST::StringLiteral *>(callExpr->arguments->expression);
2235                                     if (!literal) {
2236                                         error(prop, QDeclarativeListModel::tr("ListElement: improperly specified %1").arg(idExpr->name.toString()));
2237                                         return false;
2238                                     }
2239                                 } else if (idExpr->name == QLatin1String("QT_TRANSLATE_NOOP")) {
2240                                     if (callExpr->arguments && callExpr->arguments->next && !callExpr->arguments->next->next)
2241                                         literal = AST::cast<AST::StringLiteral *>(callExpr->arguments->next->expression);
2242                                     if (!literal) {
2243                                         error(prop, QDeclarativeListModel::tr("ListElement: improperly specified QT_TRANSLATE_NOOP"));
2244                                         return false;
2245                                     }
2246                                 }
2247                             }
2248                         }
2249
2250                         if (literal) {
2251                             d[0] = char(QDeclarativeScript::Variant::String);
2252                             d += literal->value.toUtf8();
2253                         } else {
2254                             error(prop, QDeclarativeListModel::tr("ListElement: cannot use script for property value"));
2255                             return false;
2256                         }
2257                     } else {
2258                         d[0] = char(QDeclarativeScript::Variant::Number);
2259                         d += QByteArray::number(v);
2260                     }
2261                 }
2262             }
2263             d.append('\0');
2264             data.append(d);
2265
2266             ListInstruction li;
2267             li.type = ListInstruction::Value;
2268             li.dataIdx = ref;
2269             instr << li;
2270         }
2271     }
2272
2273     return true;
2274 }
2275
2276 QByteArray QDeclarativeListModelParser::compile(const QList<QDeclarativeCustomParserProperty> &customProps)
2277 {
2278     QList<ListInstruction> instr;
2279     QByteArray data;
2280     listElementTypeName = QString(); // unknown
2281
2282     for(int ii = 0; ii < customProps.count(); ++ii) {
2283         const QDeclarativeCustomParserProperty &prop = customProps.at(ii);
2284         if(!prop.name().isEmpty()) { // isn't default property
2285             error(prop, QDeclarativeListModel::tr("ListModel: undefined property '%1'").arg(prop.name()));
2286             return QByteArray();
2287         }
2288
2289         if(!compileProperty(prop, instr, data)) {
2290             return QByteArray();
2291         }
2292     }
2293
2294     int size = sizeof(ListModelData) +
2295                instr.count() * sizeof(ListInstruction) +
2296                data.count();
2297
2298     QByteArray rv;
2299     rv.resize(size);
2300
2301     ListModelData *lmd = (ListModelData *)rv.data();
2302     lmd->dataOffset = sizeof(ListModelData) +
2303                      instr.count() * sizeof(ListInstruction);
2304     lmd->instrCount = instr.count();
2305     for (int ii = 0; ii < instr.count(); ++ii)
2306         lmd->instructions()[ii] = instr.at(ii);
2307     ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count());
2308
2309     return rv;
2310 }
2311
2312 void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray &d)
2313 {
2314     QDeclarativeListModel *rv = static_cast<QDeclarativeListModel *>(obj);
2315
2316     QV8Engine *engine = QDeclarativeEnginePrivate::getV8Engine(qmlEngine(rv));
2317     rv->m_engine = engine;
2318
2319     const ListModelData *lmd = (const ListModelData *)d.constData();
2320     const char *data = ((const char *)lmd) + lmd->dataOffset;
2321
2322     QStack<DataStackElement> stack;
2323
2324     for (int ii = 0; ii < lmd->instrCount; ++ii) {
2325         const ListInstruction &instr = lmd->instructions()[ii];
2326
2327         switch(instr.type) {
2328         case ListInstruction::Push:
2329             {
2330                 Q_ASSERT(!rv->m_dynamicRoles);
2331
2332                 ListModel *subModel = 0;
2333
2334                 if (stack.count() == 0) {
2335                     subModel = rv->m_listModel;
2336                 } else {
2337                     const DataStackElement &e0 = stack.at(stack.size() - 1);
2338                     DataStackElement &e1 = stack[stack.size() - 2];
2339
2340                     const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name);
2341                     if (role.type == ListLayout::Role::List) {
2342                         subModel = e1.model->getListProperty(e1.elementIndex, role);
2343
2344                         if (subModel == 0) {
2345                             subModel = new ListModel(role.subLayout, 0, -1);
2346                             QVariant vModel = QVariant::fromValue(subModel);
2347                             e1.model->setOrCreateProperty(e1.elementIndex, e0.name, vModel);
2348                         }
2349                     }
2350                 }
2351
2352                 DataStackElement e;
2353                 e.model = subModel;
2354                 e.elementIndex = subModel ? subModel->appendElement() : -1;
2355                 stack.push(e);
2356             }
2357             break;
2358
2359         case ListInstruction::Pop:
2360             stack.pop();
2361             break;
2362
2363         case ListInstruction::Value:
2364             {
2365                 const DataStackElement &e0 = stack.at(stack.size() - 1);
2366                 DataStackElement &e1 = stack[stack.size() - 2];
2367
2368                 QString name = e0.name;
2369                 QVariant value;
2370
2371                 switch (QDeclarativeScript::Variant::Type(data[instr.dataIdx])) {
2372                     case QDeclarativeScript::Variant::Invalid:
2373                         {
2374                             const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name);
2375                             ListModel *emptyModel = new ListModel(role.subLayout, 0, -1);
2376                             value = QVariant::fromValue(emptyModel);
2377                         }
2378                         break;
2379                     case QDeclarativeScript::Variant::Boolean:
2380                         value = bool(data[1 + instr.dataIdx]);
2381                         break;
2382                     case QDeclarativeScript::Variant::Number:
2383                         value = QByteArray(data + 1 + instr.dataIdx).toDouble();
2384                         break;
2385                     case QDeclarativeScript::Variant::String:
2386                         value = QString::fromUtf8(data + 1 + instr.dataIdx);
2387                         break;
2388                     default:
2389                         Q_ASSERT("Format error in ListInstruction");
2390                 }
2391
2392                 e1.model->setOrCreateProperty(e1.elementIndex, name, value);
2393             }
2394             break;
2395
2396         case ListInstruction::Set:
2397             {
2398                 DataStackElement e;
2399                 e.name = QString::fromUtf8(data + instr.dataIdx);
2400                 stack.push(e);
2401             }
2402             break;
2403         }
2404     }
2405 }
2406
2407 bool QDeclarativeListModelParser::definesEmptyList(const QString &s)
2408 {
2409     if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) {
2410         for (int i=1; i<s.length()-1; i++) {
2411             if (!s[i].isSpace())
2412                 return false;
2413         }
2414         return true;
2415     }
2416     return false;
2417 }
2418
2419
2420 /*!
2421     \qmlclass ListElement QDeclarativeListElement
2422     \inqmlmodule QtQuick 2
2423     \ingroup qml-working-with-data
2424     \brief The ListElement element defines a data item in a ListModel.
2425
2426     List elements are defined inside ListModel definitions, and represent items in a
2427     list that will be displayed using ListView or \l Repeater items.
2428
2429     List elements are defined like other QML elements except that they contain
2430     a collection of \e role definitions instead of properties. Using the same
2431     syntax as property definitions, roles both define how the data is accessed
2432     and include the data itself.
2433
2434     The names used for roles must begin with a lower-case letter and should be
2435     common to all elements in a given model. Values must be simple constants; either
2436     strings (quoted and optionally within a call to QT_TR_NOOP), boolean values
2437     (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter).
2438
2439     \section1 Referencing Roles
2440
2441     The role names are used by delegates to obtain data from list elements.
2442     Each role name is accessible in the delegate's scope, and refers to the
2443     corresponding role in the current element. Where a role name would be
2444     ambiguous to use, it can be accessed via the \l{ListView::}{model}
2445     property (e.g., \c{model.cost} instead of \c{cost}).
2446
2447     \section1 Example Usage
2448
2449     The following model defines a series of list elements, each of which
2450     contain "name" and "cost" roles and their associated values.
2451
2452     \snippet doc/src/snippets/declarative/qml-data-models/listelements.qml model
2453
2454     The delegate obtains the name and cost for each element by simply referring
2455     to \c name and \c cost:
2456
2457     \snippet doc/src/snippets/declarative/qml-data-models/listelements.qml view
2458
2459     \sa ListModel
2460 */
2461
2462 QT_END_NAMESPACE