Update to 5.0.0-beta1
[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     \qmltype ListModel
1427     \instantiates QQuickListModel
1428     \inqmlmodule QtQuick 2
1429     \brief Defines a free-form list data source
1430     \ingroup qtquick-models
1431
1432     The ListModel is a simple container of ListElement definitions, each containing data roles.
1433     The contents can be defined dynamically, or explicitly in QML.
1434
1435     The number of elements in the model can be obtained from its \l count property.
1436     A number of familiar methods are also provided to manipulate the contents of the
1437     model, including append(), insert(), move(), remove() and set(). These methods
1438     accept dictionaries as their arguments; these are translated to ListElement objects
1439     by the model.
1440
1441     Elements can be manipulated via the model using the setProperty() method, which
1442     allows the roles of the specified element to be set and changed.
1443
1444     \section1 Example Usage
1445
1446     The following example shows a ListModel containing three elements, with the roles
1447     "name" and "cost".
1448
1449     \div {class="float-right"}
1450     \inlineimage listmodel.png
1451     \enddiv
1452
1453     \snippet qml/listmodel/listmodel.qml 0
1454
1455     Roles (properties) in each element must begin with a lower-case letter and
1456     should be common to all elements in a model. The ListElement documentation
1457     provides more guidelines for how elements should be defined.
1458
1459     Since the example model contains an \c id property, it can be referenced
1460     by views, such as the ListView in this example:
1461
1462     \snippet qml/listmodel/listmodel-simple.qml 0
1463     \dots 8
1464     \snippet qml/listmodel/listmodel-simple.qml 1
1465
1466     It is possible for roles to contain list data.  In the following example we
1467     create a list of fruit attributes:
1468
1469     \snippet qml/listmodel/listmodel-nested.qml model
1470
1471     The delegate displays all the fruit attributes:
1472
1473     \div {class="float-right"}
1474     \inlineimage listmodel-nested.png
1475     \enddiv
1476
1477     \snippet qml/listmodel/listmodel-nested.qml delegate
1478
1479     \section1 Modifying List Models
1480
1481     The content of a ListModel may be created and modified using the clear(),
1482     append(), set(), insert() and setProperty() methods.  For example:
1483
1484     \snippet qml/listmodel/listmodel-modify.qml delegate
1485
1486     Note that when creating content dynamically the set of available properties
1487     cannot be changed once set. Whatever properties are first added to the model
1488     are the only permitted properties in the model.
1489
1490     \section1 Using Threaded List Models with WorkerScript
1491
1492     ListModel can be used together with WorkerScript access a list model
1493     from multiple threads. This is useful if list modifications are
1494     synchronous and take some time: the list operations can be moved to a
1495     different thread to avoid blocking of the main GUI thread.
1496
1497     Here is an example that uses WorkerScript to periodically append the
1498     current time to a list model:
1499
1500     \snippet examples/quick/threading/threadedlistmodel/timedisplay.qml 0
1501
1502     The included file, \tt dataloader.js, looks like this:
1503
1504     \snippet examples/quick/threading/threadedlistmodel/dataloader.js 0
1505
1506     The timer in the main example sends messages to the worker script by calling
1507     \l WorkerScript::sendMessage(). When this message is received,
1508     \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js,
1509     which appends the current time to the list model.
1510
1511     Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()}
1512     handler. You must call sync() or else the changes made to the list from the external
1513     thread will not be reflected in the list model in the main thread.
1514
1515     \sa {qml-data-models}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtQml
1516 */
1517
1518 QQuickListModel::QQuickListModel(QObject *parent)
1519 : QAbstractListModel(parent)
1520 {
1521     m_mainThread = true;
1522     m_primary = true;
1523     m_agent = 0;
1524     m_uid = uidCounter.fetchAndAddOrdered(1);
1525     m_dynamicRoles = false;
1526
1527     m_layout = new ListLayout;
1528     m_listModel = new ListModel(m_layout, this, -1);
1529
1530     m_engine = 0;
1531 }
1532
1533 QQuickListModel::QQuickListModel(const QQuickListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent)
1534 : QAbstractListModel(parent)
1535 {
1536     m_mainThread = owner->m_mainThread;
1537     m_primary = false;
1538     m_agent = owner->m_agent;
1539
1540     Q_ASSERT(owner->m_dynamicRoles == false);
1541     m_dynamicRoles = false;
1542     m_layout = 0;
1543     m_listModel = data;
1544
1545     m_engine = eng;
1546 }
1547
1548 QQuickListModel::QQuickListModel(QQuickListModel *orig, QQuickListModelWorkerAgent *agent)
1549 : QAbstractListModel(agent)
1550 {
1551     m_mainThread = false;
1552     m_primary = true;
1553     m_agent = agent;
1554     m_dynamicRoles = orig->m_dynamicRoles;
1555
1556     m_layout = new ListLayout(orig->m_layout);
1557     m_listModel = new ListModel(m_layout, this, orig->m_listModel->getUid());
1558
1559     if (m_dynamicRoles)
1560         sync(orig, this, 0);
1561     else
1562         ListModel::sync(orig->m_listModel, m_listModel, 0);
1563
1564     m_engine = 0;
1565 }
1566
1567 QQuickListModel::~QQuickListModel()
1568 {
1569     for (int i=0 ; i < m_modelObjects.count() ; ++i)
1570         delete m_modelObjects[i];
1571
1572     if (m_primary) {
1573         m_listModel->destroy();
1574         delete m_listModel;
1575
1576         if (m_mainThread && m_agent) {
1577             m_agent->modelDestroyed();
1578             m_agent->release();
1579         }
1580     }
1581
1582     m_listModel = 0;
1583
1584     delete m_layout;
1585     m_layout = 0;
1586 }
1587
1588 QQuickListModel *QQuickListModel::createWithOwner(QQuickListModel *newOwner)
1589 {
1590     QQuickListModel *model = new QQuickListModel;
1591
1592     model->m_mainThread = newOwner->m_mainThread;
1593     model->m_engine = newOwner->m_engine;
1594     model->m_agent = newOwner->m_agent;
1595     model->m_dynamicRoles = newOwner->m_dynamicRoles;
1596
1597     if (model->m_mainThread && model->m_agent)
1598         model->m_agent->addref();
1599
1600     QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner));
1601
1602     return model;
1603 }
1604
1605 QV8Engine *QQuickListModel::engine() const
1606 {
1607     if (m_engine == 0) {
1608         m_engine  = QQmlEnginePrivate::getV8Engine(qmlEngine(this));
1609     }
1610
1611     return m_engine;
1612 }
1613
1614 void QQuickListModel::sync(QQuickListModel *src, QQuickListModel *target, QHash<int, QQuickListModel *> *targetModelHash)
1615 {
1616     Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles);
1617
1618     target->m_uid = src->m_uid;
1619     if (targetModelHash)
1620         targetModelHash->insert(target->m_uid, target);
1621     target->m_roles = src->m_roles;
1622
1623     // Build hash of elements <-> uid for each of the lists
1624     QHash<int, ElementSync> elementHash;
1625     for (int i=0 ; i < target->m_modelObjects.count() ; ++i) {
1626         DynamicRoleModelNode *e = target->m_modelObjects.at(i);
1627         int uid = e->getUid();
1628         ElementSync sync;
1629         sync.target = e;
1630         elementHash.insert(uid, sync);
1631     }
1632     for (int i=0 ; i < src->m_modelObjects.count() ; ++i) {
1633         DynamicRoleModelNode *e = src->m_modelObjects.at(i);
1634         int uid = e->getUid();
1635
1636         QHash<int, ElementSync>::iterator it = elementHash.find(uid);
1637         if (it == elementHash.end()) {
1638             ElementSync sync;
1639             sync.src = e;
1640             elementHash.insert(uid, sync);
1641         } else {
1642             ElementSync &sync = it.value();
1643             sync.src = e;
1644         }
1645     }
1646
1647     // Get list of elements that are in the target but no longer in the source. These get deleted first.
1648     QHash<int, ElementSync>::iterator it = elementHash.begin();
1649     QHash<int, ElementSync>::iterator end = elementHash.end();
1650     while (it != end) {
1651         const ElementSync &s = it.value();
1652         if (s.src == 0) {
1653             int targetIndex = target->m_modelObjects.indexOf(s.target);
1654             target->m_modelObjects.remove(targetIndex, 1);
1655             delete s.target;
1656         }
1657         ++it;
1658     }
1659
1660     // Clear the target list, and append in correct order from the source
1661     target->m_modelObjects.clear();
1662     for (int i=0 ; i < src->m_modelObjects.count() ; ++i) {
1663         DynamicRoleModelNode *srcElement = src->m_modelObjects.at(i);
1664         it = elementHash.find(srcElement->getUid());
1665         const ElementSync &s = it.value();
1666         DynamicRoleModelNode *targetElement = s.target;
1667         if (targetElement == 0) {
1668             targetElement = new DynamicRoleModelNode(target, srcElement->getUid());
1669         }
1670         DynamicRoleModelNode::sync(srcElement, targetElement, targetModelHash);
1671         target->m_modelObjects.append(targetElement);
1672     }
1673 }
1674
1675 void QQuickListModel::emitItemsChanged(int index, int count, const QVector<int> &roles)
1676 {
1677     if (count <= 0)
1678         return;
1679
1680     if (m_mainThread) {
1681         emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);;
1682     } else {
1683         int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
1684         m_agent->data.changedChange(uid, index, count, roles);
1685     }
1686 }
1687
1688 void QQuickListModel::emitItemsRemoved(int index, int count)
1689 {
1690     if (count <= 0)
1691         return;
1692
1693     if (m_mainThread) {
1694             beginRemoveRows(QModelIndex(), index, index + count - 1);
1695             endRemoveRows();
1696             emit countChanged();
1697     } else {
1698         int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
1699         if (index == 0 && count == this->count())
1700             m_agent->data.clearChange(uid);
1701         m_agent->data.removeChange(uid, index, count);
1702     }
1703 }
1704
1705 void QQuickListModel::emitItemsInserted(int index, int count)
1706 {
1707     if (count <= 0)
1708         return;
1709
1710     if (m_mainThread) {
1711         beginInsertRows(QModelIndex(), index, index + count - 1);
1712         endInsertRows();
1713         emit countChanged();
1714     } else {
1715         int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
1716         m_agent->data.insertChange(uid, index, count);
1717     }
1718 }
1719
1720 void QQuickListModel::emitItemsMoved(int from, int to, int n)
1721 {
1722     if (n <= 0)
1723         return;
1724
1725     if (m_mainThread) {
1726         beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to);
1727         endMoveRows();
1728     } else {
1729         int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
1730         m_agent->data.moveChange(uid, from, n, to);
1731     }
1732 }
1733
1734 QQuickListModelWorkerAgent *QQuickListModel::agent()
1735 {
1736     if (m_agent)
1737         return m_agent;
1738
1739     m_agent = new QQuickListModelWorkerAgent(this);
1740     return m_agent;
1741 }
1742
1743 QModelIndex QQuickListModel::index(int row, int column, const QModelIndex &parent) const
1744 {
1745     return row >= 0 && row < count() && column == 0 && !parent.isValid()
1746             ? createIndex(row, column)
1747             : QModelIndex();
1748 }
1749
1750 int QQuickListModel::rowCount(const QModelIndex &parent) const
1751 {
1752     return !parent.isValid() ? count() : 0;
1753 }
1754
1755 QVariant QQuickListModel::data(const QModelIndex &index, int role) const
1756 {
1757     return data(index.row(), role);
1758 }
1759
1760 QVariant QQuickListModel::data(int index, int role) const
1761 {
1762     QVariant v;
1763
1764     if (index >= count() || index < 0)
1765         return v;
1766
1767     if (m_dynamicRoles)
1768         v = m_modelObjects[index]->getValue(m_roles[role]);
1769     else
1770         v = m_listModel->getProperty(index, role, this, engine());
1771
1772     return v;
1773 }
1774
1775 QHash<int, QByteArray> QQuickListModel::roleNames() const
1776 {
1777     QHash<int, QByteArray> roleNames;
1778
1779     if (m_dynamicRoles) {
1780         for (int i = 0 ; i < m_roles.count() ; ++i)
1781             roleNames.insert(i, m_roles.at(i).toUtf8());
1782     } else {
1783         for (int i = 0 ; i < m_listModel->roleCount() ; ++i) {
1784             const ListLayout::Role &r = m_listModel->getExistingRole(i);
1785             roleNames.insert(i, r.name.toUtf8());
1786         }
1787     }
1788
1789     return roleNames;
1790 }
1791
1792 /*!
1793     \qmlproperty bool QtQuick2::ListModel::dynamicRoles
1794
1795     By default, the type of a role is fixed the first time
1796     the role is used. For example, if you create a role called
1797     "data" and assign a number to it, you can no longer assign
1798     a string to the "data" role. However, when the dynamicRoles
1799     property is enabled, the type of a given role is not fixed
1800     and can be different between elements.
1801
1802     The dynamicRoles property must be set before any data is
1803     added to the ListModel, and must be set from the main
1804     thread.
1805
1806     A ListModel that has data statically defined (via the
1807     ListElement QML syntax) cannot have the dynamicRoles
1808     property enabled.
1809
1810     There is a significant performance cost to using a
1811     ListModel with dynamic roles enabled. The cost varies
1812     from platform to platform but is typically somewhere
1813     between 4-6x slower than using static role types.
1814
1815     Due to the performance cost of using dynamic roles,
1816     they are disabled by default.
1817 */
1818 void QQuickListModel::setDynamicRoles(bool enableDynamicRoles)
1819 {
1820     if (m_mainThread && m_agent == 0) {
1821         if (enableDynamicRoles) {
1822             if (m_layout->roleCount())
1823                 qmlInfo(this) << tr("unable to enable dynamic roles as this model is not empty!");
1824             else
1825                 m_dynamicRoles = true;
1826         } else {
1827             if (m_roles.count()) {
1828                 qmlInfo(this) << tr("unable to enable static roles as this model is not empty!");
1829             } else {
1830                 m_dynamicRoles = false;
1831             }
1832         }
1833     } else {
1834         qmlInfo(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created");
1835     }
1836 }
1837
1838 /*!
1839     \qmlproperty int QtQuick2::ListModel::count
1840     The number of data entries in the model.
1841 */
1842 int QQuickListModel::count() const
1843 {
1844     int count;
1845
1846     if (m_dynamicRoles)
1847         count = m_modelObjects.count();
1848     else {
1849         count = m_listModel->elementCount();
1850     }
1851
1852     return count;
1853 }
1854
1855 /*!
1856     \qmlmethod QtQuick2::ListModel::clear()
1857
1858     Deletes all content from the model.
1859
1860     \sa append(), remove()
1861 */
1862 void QQuickListModel::clear()
1863 {
1864     int cleared = count();
1865
1866     if (m_dynamicRoles) {
1867         for (int i=0 ; i < m_modelObjects.count() ; ++i)
1868             delete m_modelObjects[i];
1869         m_modelObjects.clear();
1870     } else {
1871         m_listModel->clear();
1872     }
1873
1874     emitItemsRemoved(0, cleared);
1875 }
1876
1877 /*!
1878     \qmlmethod QtQuick2::ListModel::remove(int index, int count = 1)
1879
1880     Deletes the content at \a index from the model.
1881
1882     \sa clear()
1883 */
1884 void QQuickListModel::remove(QQmlV8Function *args)
1885 {
1886     int argLength = args->Length();
1887
1888     if (argLength == 1 || argLength == 2) {
1889         int index = (*args)[0]->Int32Value();
1890         int removeCount = (argLength == 2 ? ((*args)[1]->Int32Value()) : 1);
1891
1892         if (index < 0 || index+removeCount > count() || removeCount <= 0) {
1893             qmlInfo(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count());
1894             return;
1895         }
1896
1897         if (m_dynamicRoles) {
1898             for (int i=0 ; i < removeCount ; ++i)
1899                 delete m_modelObjects[index+i];
1900             m_modelObjects.remove(index, removeCount);
1901         } else {
1902             m_listModel->remove(index, removeCount);
1903         }
1904
1905         emitItemsRemoved(index, removeCount);
1906     } else {
1907         qmlInfo(this) << tr("remove: incorrect number of arguments");
1908     }
1909 }
1910
1911 /*!
1912     \qmlmethod QtQuick2::ListModel::insert(int index, jsobject dict)
1913
1914     Adds a new item to the list model at position \a index, with the
1915     values in \a dict.
1916
1917     \code
1918         fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"})
1919     \endcode
1920
1921     The \a index must be to an existing item in the list, or one past
1922     the end of the list (equivalent to append).
1923
1924     \sa set(), append()
1925 */
1926
1927 void QQuickListModel::insert(QQmlV8Function *args)
1928 {
1929     if (args->Length() == 2) {
1930
1931         v8::Handle<v8::Value> arg0 = (*args)[0];
1932         int index = arg0->Int32Value();
1933
1934         if (index < 0 || index > count()) {
1935             qmlInfo(this) << tr("insert: index %1 out of range").arg(index);
1936             return;
1937         }
1938
1939         v8::Handle<v8::Value> arg1 = (*args)[1];
1940
1941         if (arg1->IsArray()) {
1942             v8::Handle<v8::Array> objectArray = v8::Handle<v8::Array>::Cast(arg1);
1943             int objectArrayLength = objectArray->Length();
1944             for (int i=0 ; i < objectArrayLength ; ++i) {
1945                 v8::Handle<v8::Object> argObject = objectArray->Get(i)->ToObject();
1946
1947                 if (m_dynamicRoles) {
1948                     m_modelObjects.insert(index+i, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
1949                 } else {
1950                     m_listModel->insert(index+i, argObject, args->engine());
1951                 }
1952             }
1953             emitItemsInserted(index, objectArrayLength);
1954         } else if (arg1->IsObject()) {
1955             v8::Handle<v8::Object> argObject = arg1->ToObject();
1956
1957             if (m_dynamicRoles) {
1958                 m_modelObjects.insert(index, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
1959             } else {
1960                 m_listModel->insert(index, argObject, args->engine());
1961             }
1962
1963             emitItemsInserted(index, 1);
1964         } else {
1965             qmlInfo(this) << tr("insert: value is not an object");
1966         }
1967     } else {
1968         qmlInfo(this) << tr("insert: value is not an object");
1969     }
1970 }
1971
1972 /*!
1973     \qmlmethod QtQuick2::ListModel::move(int from, int to, int n)
1974
1975     Moves \a n items \a from one position \a to another.
1976
1977     The from and to ranges must exist; for example, to move the first 3 items
1978     to the end of the list:
1979
1980     \code
1981         fruitModel.move(0, fruitModel.count - 3, 3)
1982     \endcode
1983
1984     \sa append()
1985 */
1986 void QQuickListModel::move(int from, int to, int n)
1987 {
1988     if (n==0 || from==to)
1989         return;
1990     if (!canMove(from, to, n)) {
1991         qmlInfo(this) << tr("move: out of range");
1992         return;
1993     }
1994
1995     if (m_dynamicRoles) {
1996
1997         int realFrom = from;
1998         int realTo = to;
1999         int realN = n;
2000
2001         if (from > to) {
2002             // Only move forwards - flip if backwards moving
2003             int tfrom = from;
2004             int tto = to;
2005             realFrom = tto;
2006             realTo = tto+n;
2007             realN = tfrom-tto;
2008         }
2009
2010         QPODVector<DynamicRoleModelNode *, 4> store;
2011         for (int i=0 ; i < (realTo-realFrom) ; ++i)
2012             store.append(m_modelObjects[realFrom+realN+i]);
2013         for (int i=0 ; i < realN ; ++i)
2014             store.append(m_modelObjects[realFrom+i]);
2015         for (int i=0 ; i < store.count() ; ++i)
2016             m_modelObjects[realFrom+i] = store[i];
2017
2018     } else {
2019         m_listModel->move(from, to, n);
2020     }
2021
2022     emitItemsMoved(from, to, n);
2023 }
2024
2025 /*!
2026     \qmlmethod QtQuick2::ListModel::append(jsobject dict)
2027
2028     Adds a new item to the end of the list model, with the
2029     values in \a dict.
2030
2031     \code
2032         fruitModel.append({"cost": 5.95, "name":"Pizza"})
2033     \endcode
2034
2035     \sa set(), remove()
2036 */
2037 void QQuickListModel::append(QQmlV8Function *args)
2038 {
2039     if (args->Length() == 1) {
2040         v8::Handle<v8::Value> arg = (*args)[0];
2041
2042         if (arg->IsArray()) {
2043             v8::Handle<v8::Array> objectArray = v8::Handle<v8::Array>::Cast(arg);
2044             int objectArrayLength = objectArray->Length();
2045
2046             int index = count();
2047             for (int i=0 ; i < objectArrayLength ; ++i) {
2048                 v8::Handle<v8::Object> argObject = objectArray->Get(i)->ToObject();
2049
2050                 if (m_dynamicRoles) {
2051                     m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
2052                 } else {
2053                     m_listModel->append(argObject, args->engine());
2054                 }
2055             }
2056
2057             emitItemsInserted(index, objectArrayLength);
2058         } else if (arg->IsObject()) {
2059             v8::Handle<v8::Object> argObject = arg->ToObject();
2060
2061             int index;
2062
2063             if (m_dynamicRoles) {
2064                 index = m_modelObjects.count();
2065                 m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
2066             } else {
2067                 index = m_listModel->append(argObject, args->engine());
2068             }
2069
2070             emitItemsInserted(index, 1);
2071         } else {
2072             qmlInfo(this) << tr("append: value is not an object");
2073         }
2074     } else {
2075         qmlInfo(this) << tr("append: value is not an object");
2076     }
2077 }
2078
2079 /*!
2080     \qmlmethod object QtQuick2::ListModel::get(int index)
2081
2082     Returns the item at \a index in the list model. This allows the item
2083     data to be accessed or modified from JavaScript:
2084
2085     \code
2086     Component.onCompleted: {
2087         fruitModel.append({"cost": 5.95, "name":"Jackfruit"});
2088         console.log(fruitModel.get(0).cost);
2089         fruitModel.get(0).cost = 10.95;
2090     }
2091     \endcode
2092
2093     The \a index must be an element in the list.
2094
2095     Note that properties of the returned object that are themselves objects
2096     will also be models, and this get() method is used to access elements:
2097
2098     \code
2099         fruitModel.append(..., "attributes":
2100             [{"name":"spikes","value":"7mm"},
2101              {"name":"color","value":"green"}]);
2102         fruitModel.get(0).attributes.get(1).value; // == "green"
2103     \endcode
2104
2105     \warning The returned object is not guaranteed to remain valid. It
2106     should not be used in \l{Property Binding}{property bindings}.
2107
2108     \sa append()
2109 */
2110 QQmlV8Handle QQuickListModel::get(int index) const
2111 {
2112     v8::Handle<v8::Value> result = v8::Undefined();
2113
2114     if (index >= 0 && index < count()) {
2115         QV8Engine *v8engine = engine();
2116
2117         if (m_dynamicRoles) {
2118             DynamicRoleModelNode *object = m_modelObjects[index];
2119             result = v8engine->newQObject(object);
2120         } else {
2121             ModelObject *object = m_listModel->getOrCreateModelObject(const_cast<QQuickListModel *>(this), index);
2122             result = v8engine->newQObject(object);
2123         }
2124     }
2125
2126     return QQmlV8Handle::fromHandle(result);
2127 }
2128
2129 /*!
2130     \qmlmethod QtQuick2::ListModel::set(int index, jsobject dict)
2131
2132     Changes the item at \a index in the list model with the
2133     values in \a dict. Properties not appearing in \a dict
2134     are left unchanged.
2135
2136     \code
2137         fruitModel.set(3, {"cost": 5.95, "name":"Pizza"})
2138     \endcode
2139
2140     If \a index is equal to count() then a new item is appended to the
2141     list. Otherwise, \a index must be an element in the list.
2142
2143     \sa append()
2144 */
2145 void QQuickListModel::set(int index, const QQmlV8Handle &handle)
2146 {
2147     v8::Handle<v8::Value> valuemap = handle.toHandle();
2148
2149     if (!valuemap->IsObject() || valuemap->IsArray()) {
2150         qmlInfo(this) << tr("set: value is not an object");
2151         return;
2152     }
2153     if (index > count() || index < 0) {
2154         qmlInfo(this) << tr("set: index %1 out of range").arg(index);
2155         return;
2156     }
2157
2158     v8::Handle<v8::Object> object = valuemap->ToObject();
2159
2160     if (index == count()) {
2161
2162         if (m_dynamicRoles) {
2163             m_modelObjects.append(DynamicRoleModelNode::create(engine()->variantMapFromJS(object), this));
2164         } else {
2165             m_listModel->insert(index, object, engine());
2166         }
2167
2168         emitItemsInserted(index, 1);
2169     } else {
2170
2171         QVector<int> roles;
2172
2173         if (m_dynamicRoles) {
2174             m_modelObjects[index]->updateValues(engine()->variantMapFromJS(object), roles);
2175         } else {
2176             m_listModel->set(index, object, &roles, engine());
2177         }
2178
2179         if (roles.count())
2180             emitItemsChanged(index, 1, roles);
2181     }
2182 }
2183
2184 /*!
2185     \qmlmethod QtQuick2::ListModel::setProperty(int index, string property, variant value)
2186
2187     Changes the \a property of the item at \a index in the list model to \a value.
2188
2189     \code
2190         fruitModel.setProperty(3, "cost", 5.95)
2191     \endcode
2192
2193     The \a index must be an element in the list.
2194
2195     \sa append()
2196 */
2197 void QQuickListModel::setProperty(int index, const QString& property, const QVariant& value)
2198 {
2199     if (count() == 0 || index >= count() || index < 0) {
2200         qmlInfo(this) << tr("set: index %1 out of range").arg(index);
2201         return;
2202     }
2203
2204     if (m_dynamicRoles) {
2205         int roleIndex = m_roles.indexOf(property);
2206         if (roleIndex == -1) {
2207             roleIndex = m_roles.count();
2208             m_roles.append(property);
2209         }
2210         if (m_modelObjects[index]->setValue(property.toUtf8(), value)) {
2211             QVector<int> roles;
2212             roles << roleIndex;
2213             emitItemsChanged(index, 1, roles);
2214         }
2215     } else {
2216         int roleIndex = m_listModel->setOrCreateProperty(index, property, value);
2217         if (roleIndex != -1) {
2218
2219             QVector<int> roles;
2220             roles << roleIndex;
2221
2222             emitItemsChanged(index, 1, roles);
2223         }
2224     }
2225 }
2226
2227 /*!
2228     \qmlmethod QtQuick2::ListModel::sync()
2229
2230     Writes any unsaved changes to the list model after it has been modified
2231     from a worker script.
2232 */
2233 void QQuickListModel::sync()
2234 {
2235     // This is just a dummy method to make it look like sync() exists in
2236     // ListModel (and not just QQuickListModelWorkerAgent) and to let
2237     // us document sync().
2238     qmlInfo(this) << "List sync() can only be called from a WorkerScript";
2239 }
2240
2241 bool QQuickListModelParser::compileProperty(const QQmlCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data)
2242 {
2243     QList<QVariant> values = prop.assignedValues();
2244     for(int ii = 0; ii < values.count(); ++ii) {
2245         const QVariant &value = values.at(ii);
2246
2247         if(value.userType() == qMetaTypeId<QQmlCustomParserNode>()) {
2248             QQmlCustomParserNode node =
2249                 qvariant_cast<QQmlCustomParserNode>(value);
2250
2251             if (node.name() != listElementTypeName) {
2252                 const QMetaObject *mo = resolveType(node.name());
2253                 if (mo != &QQuickListElement::staticMetaObject) {
2254                     error(node, QQuickListModel::tr("ListElement: cannot contain nested elements"));
2255                     return false;
2256                 }
2257                 listElementTypeName = node.name(); // cache right name for next time
2258             }
2259
2260             {
2261             ListInstruction li;
2262             li.type = ListInstruction::Push;
2263             li.dataIdx = -1;
2264             instr << li;
2265             }
2266
2267             QList<QQmlCustomParserProperty> props = node.properties();
2268             for(int jj = 0; jj < props.count(); ++jj) {
2269                 const QQmlCustomParserProperty &nodeProp = props.at(jj);
2270                 if (nodeProp.name().isEmpty()) {
2271                     error(nodeProp, QQuickListModel::tr("ListElement: cannot contain nested elements"));
2272                     return false;
2273                 }
2274                 if (nodeProp.name() == QStringLiteral("id")) {
2275                     error(nodeProp, QQuickListModel::tr("ListElement: cannot use reserved \"id\" property"));
2276                     return false;
2277                 }
2278
2279                 ListInstruction li;
2280                 int ref = data.count();
2281                 data.append(nodeProp.name().toUtf8());
2282                 data.append('\0');
2283                 li.type = ListInstruction::Set;
2284                 li.dataIdx = ref;
2285                 instr << li;
2286
2287                 if(!compileProperty(nodeProp, instr, data))
2288                     return false;
2289
2290                 li.type = ListInstruction::Pop;
2291                 li.dataIdx = -1;
2292                 instr << li;
2293             }
2294
2295             {
2296             ListInstruction li;
2297             li.type = ListInstruction::Pop;
2298             li.dataIdx = -1;
2299             instr << li;
2300             }
2301
2302         } else {
2303
2304             QQmlScript::Variant variant =
2305                 qvariant_cast<QQmlScript::Variant>(value);
2306
2307             int ref = data.count();
2308
2309             QByteArray d;
2310             d += char(variant.type()); // type tag
2311             if (variant.isString()) {
2312                 d += variant.asString().toUtf8();
2313             } else if (variant.isNumber()) {
2314                 d += QByteArray::number(variant.asNumber(),'g',20);
2315             } else if (variant.isBoolean()) {
2316                 d += char(variant.asBoolean());
2317             } else if (variant.isScript()) {
2318                 if (definesEmptyList(variant.asScript())) {
2319                     d[0] = char(QQmlScript::Variant::Invalid); // marks empty list
2320                 } else {
2321                     QByteArray script = variant.asScript().toUtf8();
2322                     bool ok;
2323                     int v = evaluateEnum(script, &ok);
2324                     if (!ok) {
2325                         using namespace QQmlJS;
2326                         AST::Node *node = variant.asAST();
2327                         AST::StringLiteral *literal = 0;
2328                         if (AST::CallExpression *callExpr = AST::cast<AST::CallExpression *>(node)) {
2329                             if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(callExpr->base)) {
2330                                 if (idExpr->name == QLatin1String("QT_TR_NOOP") || idExpr->name == QLatin1String("QT_TRID_NOOP")) {
2331                                     if (callExpr->arguments && !callExpr->arguments->next)
2332                                         literal = AST::cast<AST::StringLiteral *>(callExpr->arguments->expression);
2333                                     if (!literal) {
2334                                         error(prop, QQuickListModel::tr("ListElement: improperly specified %1").arg(idExpr->name.toString()));
2335                                         return false;
2336                                     }
2337                                 } else if (idExpr->name == QLatin1String("QT_TRANSLATE_NOOP")) {
2338                                     if (callExpr->arguments && callExpr->arguments->next && !callExpr->arguments->next->next)
2339                                         literal = AST::cast<AST::StringLiteral *>(callExpr->arguments->next->expression);
2340                                     if (!literal) {
2341                                         error(prop, QQuickListModel::tr("ListElement: improperly specified QT_TRANSLATE_NOOP"));
2342                                         return false;
2343                                     }
2344                                 }
2345                             }
2346                         }
2347
2348                         if (literal) {
2349                             d[0] = char(QQmlScript::Variant::String);
2350                             d += literal->value.toUtf8();
2351                         } else {
2352                             error(prop, QQuickListModel::tr("ListElement: cannot use script for property value"));
2353                             return false;
2354                         }
2355                     } else {
2356                         d[0] = char(QQmlScript::Variant::Number);
2357                         d += QByteArray::number(v);
2358                     }
2359                 }
2360             }
2361             d.append('\0');
2362             data.append(d);
2363
2364             ListInstruction li;
2365             li.type = ListInstruction::Value;
2366             li.dataIdx = ref;
2367             instr << li;
2368         }
2369     }
2370
2371     return true;
2372 }
2373
2374 QByteArray QQuickListModelParser::compile(const QList<QQmlCustomParserProperty> &customProps)
2375 {
2376     QList<ListInstruction> instr;
2377     QByteArray data;
2378     listElementTypeName = QString(); // unknown
2379
2380     for(int ii = 0; ii < customProps.count(); ++ii) {
2381         const QQmlCustomParserProperty &prop = customProps.at(ii);
2382         if(!prop.name().isEmpty()) { // isn't default property
2383             error(prop, QQuickListModel::tr("ListModel: undefined property '%1'").arg(prop.name()));
2384             return QByteArray();
2385         }
2386
2387         if(!compileProperty(prop, instr, data)) {
2388             return QByteArray();
2389         }
2390     }
2391
2392     int size = sizeof(ListModelData) +
2393                instr.count() * sizeof(ListInstruction) +
2394                data.count();
2395
2396     QByteArray rv;
2397     rv.resize(size);
2398
2399     ListModelData *lmd = (ListModelData *)rv.data();
2400     lmd->dataOffset = sizeof(ListModelData) +
2401                      instr.count() * sizeof(ListInstruction);
2402     lmd->instrCount = instr.count();
2403     for (int ii = 0; ii < instr.count(); ++ii)
2404         lmd->instructions()[ii] = instr.at(ii);
2405     ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count());
2406
2407     return rv;
2408 }
2409
2410 void QQuickListModelParser::setCustomData(QObject *obj, const QByteArray &d)
2411 {
2412     QQuickListModel *rv = static_cast<QQuickListModel *>(obj);
2413
2414     QV8Engine *engine = QQmlEnginePrivate::getV8Engine(qmlEngine(rv));
2415     rv->m_engine = engine;
2416
2417     const ListModelData *lmd = (const ListModelData *)d.constData();
2418     const char *data = ((const char *)lmd) + lmd->dataOffset;
2419
2420     bool setRoles = false;
2421
2422     QStack<DataStackElement> stack;
2423
2424     for (int ii = 0; ii < lmd->instrCount; ++ii) {
2425         const ListInstruction &instr = lmd->instructions()[ii];
2426
2427         switch(instr.type) {
2428         case ListInstruction::Push:
2429             {
2430                 Q_ASSERT(!rv->m_dynamicRoles);
2431
2432                 ListModel *subModel = 0;
2433
2434                 if (stack.count() == 0) {
2435                     subModel = rv->m_listModel;
2436                 } else {
2437                     const DataStackElement &e0 = stack.at(stack.size() - 1);
2438                     DataStackElement &e1 = stack[stack.size() - 2];
2439
2440                     const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name);
2441                     if (role.type == ListLayout::Role::List) {
2442                         subModel = e1.model->getListProperty(e1.elementIndex, role);
2443
2444                         if (subModel == 0) {
2445                             subModel = new ListModel(role.subLayout, 0, -1);
2446                             QVariant vModel = QVariant::fromValue(subModel);
2447                             e1.model->setOrCreateProperty(e1.elementIndex, e0.name, vModel);
2448                         }
2449                     }
2450                 }
2451
2452                 DataStackElement e;
2453                 e.model = subModel;
2454                 e.elementIndex = subModel ? subModel->appendElement() : -1;
2455                 stack.push(e);
2456             }
2457             break;
2458
2459         case ListInstruction::Pop:
2460             stack.pop();
2461             break;
2462
2463         case ListInstruction::Value:
2464             {
2465                 const DataStackElement &e0 = stack.at(stack.size() - 1);
2466                 DataStackElement &e1 = stack[stack.size() - 2];
2467
2468                 QString name = e0.name;
2469                 QVariant value;
2470
2471                 switch (QQmlScript::Variant::Type(data[instr.dataIdx])) {
2472                     case QQmlScript::Variant::Invalid:
2473                         {
2474                             const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name);
2475                             ListModel *emptyModel = new ListModel(role.subLayout, 0, -1);
2476                             value = QVariant::fromValue(emptyModel);
2477                         }
2478                         break;
2479                     case QQmlScript::Variant::Boolean:
2480                         value = bool(data[1 + instr.dataIdx]);
2481                         break;
2482                     case QQmlScript::Variant::Number:
2483                         value = QByteArray(data + 1 + instr.dataIdx).toDouble();
2484                         break;
2485                     case QQmlScript::Variant::String:
2486                         value = QString::fromUtf8(data + 1 + instr.dataIdx);
2487                         break;
2488                     default:
2489                         Q_ASSERT("Format error in ListInstruction");
2490                 }
2491
2492                 e1.model->setOrCreateProperty(e1.elementIndex, name, value);
2493                 setRoles = true;
2494             }
2495             break;
2496
2497         case ListInstruction::Set:
2498             {
2499                 DataStackElement e;
2500                 e.name = QString::fromUtf8(data + instr.dataIdx);
2501                 stack.push(e);
2502             }
2503             break;
2504         }
2505     }
2506
2507     if (setRoles == false)
2508         qmlInfo(obj) << "All ListElement declarations are empty, no roles can be created unless dynamicRoles is set.";
2509 }
2510
2511 bool QQuickListModelParser::definesEmptyList(const QString &s)
2512 {
2513     if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) {
2514         for (int i=1; i<s.length()-1; i++) {
2515             if (!s[i].isSpace())
2516                 return false;
2517         }
2518         return true;
2519     }
2520     return false;
2521 }
2522
2523
2524 /*!
2525     \qmltype ListElement
2526     \instantiates QQuickListElement
2527     \inqmlmodule QtQuick 2
2528     \brief Defines a data item in a ListModel
2529     \ingroup qtquick-models
2530
2531     List elements are defined inside ListModel definitions, and represent items in a
2532     list that will be displayed using ListView or \l Repeater items.
2533
2534     List elements are defined like other QML elements except that they contain
2535     a collection of \e role definitions instead of properties. Using the same
2536     syntax as property definitions, roles both define how the data is accessed
2537     and include the data itself.
2538
2539     The names used for roles must begin with a lower-case letter and should be
2540     common to all elements in a given model. Values must be simple constants; either
2541     strings (quoted and optionally within a call to QT_TR_NOOP), boolean values
2542     (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter).
2543
2544     \section1 Referencing Roles
2545
2546     The role names are used by delegates to obtain data from list elements.
2547     Each role name is accessible in the delegate's scope, and refers to the
2548     corresponding role in the current element. Where a role name would be
2549     ambiguous to use, it can be accessed via the \l{ListView::}{model}
2550     property (e.g., \c{model.cost} instead of \c{cost}).
2551
2552     \section1 Example Usage
2553
2554     The following model defines a series of list elements, each of which
2555     contain "name" and "cost" roles and their associated values.
2556
2557     \snippet qml/listmodel/listelements.qml model
2558
2559     The delegate obtains the name and cost for each element by simply referring
2560     to \c name and \c cost:
2561
2562     \snippet qml/listmodel/listelements.qml view
2563
2564     \sa ListModel
2565 */
2566
2567 QT_END_NAMESPACE