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