1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qdeclarativelistmodel_p_p.h"
43 #include "qdeclarativelistmodelworkeragent_p.h"
44 #include "qdeclarativeopenmetaobject_p.h"
45 #include <private/qdeclarativejsast_p.h>
46 #include <private/qdeclarativejsengine_p.h>
48 #include <private/qdeclarativecustomparser_p.h>
49 #include <private/qdeclarativescript_p.h>
50 #include <private/qdeclarativeengine_p.h>
51 #include <qdeclarativecontext.h>
52 #include <qdeclarativeinfo.h>
54 #include <QtCore/qdebug.h>
55 #include <QtCore/qstack.h>
56 #include <QXmlStreamReader>
58 Q_DECLARE_METATYPE(QListModelInterface *)
62 // Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models.
63 enum { MIN_LISTMODEL_UID = 1024 };
65 static QAtomicInt uidCounter(MIN_LISTMODEL_UID);
68 static bool isMemoryUsed(const char *mem)
70 for (size_t i=0 ; i < sizeof(T) ; ++i) {
78 static QString roleTypeName(ListLayout::Role::DataType t)
81 const char *roleTypeNames[] = { "String", "Number", "Bool", "List", "QObject", "VariantMap" };
83 if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType)
84 result = QString::fromLatin1(roleTypeNames[t]);
89 const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type)
91 QStringHash<Role *>::Node *node = roleHash.findNode(key);
93 const Role &r = *node->value;
95 qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type));
99 return createRole(key, type);
102 const ListLayout::Role &ListLayout::getRoleOrCreate(v8::Handle<v8::String> key, Role::DataType type)
104 QHashedV8String hashedKey(key);
105 QStringHash<Role *>::Node *node = roleHash.findNode(hashedKey);
107 const Role &r = *node->value;
109 qmlInfo(0) << QString::fromLatin1("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type));
114 qkey.resize(key->Length());
115 key->Write(reinterpret_cast<uint16_t*>(qkey.data()));
117 return createRole(qkey, type);
120 const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type)
122 const int dataSizes[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QDeclarativeGuard<QObject>), sizeof(QVariantMap) };
123 const int dataAlignments[] = { sizeof(QString), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap) };
129 if (type == Role::List) {
130 r->subLayout = new ListLayout;
135 int dataSize = dataSizes[type];
136 int dataAlignment = dataAlignments[type];
138 int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1);
139 if (dataOffset + dataSize > ListElement::BLOCK_SIZE) {
140 r->blockIndex = ++currentBlock;
142 currentBlockOffset = dataSize;
144 r->blockIndex = currentBlock;
145 r->blockOffset = dataOffset;
146 currentBlockOffset = dataOffset + dataSize;
149 int roleIndex = roles.count();
150 r->index = roleIndex;
153 roleHash.insert(key, r);
158 ListLayout::ListLayout(const ListLayout *other) : currentBlock(0), currentBlockOffset(0)
160 for (int i=0 ; i < other->roles.count() ; ++i) {
161 Role *role = new Role(other->roles[i]);
163 roleHash.insert(role->name, role);
165 currentBlockOffset = other->currentBlockOffset;
166 currentBlock = other->currentBlock;
169 ListLayout::~ListLayout()
171 for (int i=0 ; i < roles.count() ; ++i) {
176 void ListLayout::sync(ListLayout *src, ListLayout *target)
178 int roleOffset = target->roles.count();
179 int newRoleCount = src->roles.count() - roleOffset;
181 for (int i=0 ; i < newRoleCount ; ++i) {
182 Role *role = new Role(src->roles[roleOffset + i]);
183 target->roles.append(role);
184 target->roleHash.insert(role->name, role);
187 target->currentBlockOffset = src->currentBlockOffset;
188 target->currentBlock = src->currentBlock;
191 ListLayout::Role::Role(const Role *other)
195 blockIndex = other->blockIndex;
196 blockOffset = other->blockOffset;
197 index = other->index;
198 if (other->subLayout)
199 subLayout = new ListLayout(other->subLayout);
204 ListLayout::Role::~Role()
209 const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QVariant &data)
213 switch (data.type()) {
214 case QVariant::Double: type = Role::Number; break;
215 case QVariant::Int: type = Role::Number; break;
216 case QVariant::UserType: type = Role::List; break;
217 case QVariant::Bool: type = Role::Bool; break;
218 case QVariant::String: type = Role::String; break;
219 case QVariant::Map: type = Role::VariantMap; break;
220 default: type = Role::Invalid; break;
223 if (type == Role::Invalid) {
224 qmlInfo(0) << "Can't create role for unsupported data type";
228 return &getRoleOrCreate(key, type);
231 const ListLayout::Role *ListLayout::getExistingRole(const QString &key)
234 QStringHash<Role *>::Node *node = roleHash.findNode(key);
240 const ListLayout::Role *ListLayout::getExistingRole(v8::Handle<v8::String> key)
243 QHashedV8String hashedKey(key);
244 QStringHash<Role *>::Node *node = roleHash.findNode(hashedKey);
250 ModelObject *ListModel::getOrCreateModelObject(QDeclarativeListModel *model, int elementIndex)
252 ListElement *e = elements[elementIndex];
253 if (e->m_objectCache == 0) {
254 e->m_objectCache = new ModelObject(model, elementIndex);
256 return e->m_objectCache;
259 void ListModel::sync(ListModel *src, ListModel *target, QHash<int, ListModel *> *targetModelHash)
262 target->m_uid = src->m_uid;
264 targetModelHash->insert(target->m_uid, target);
266 // Build hash of elements <-> uid for each of the lists
267 QHash<int, ElementSync> elementHash;
268 for (int i=0 ; i < target->elements.count() ; ++i) {
269 ListElement *e = target->elements.at(i);
270 int uid = e->getUid();
273 elementHash.insert(uid, sync);
275 for (int i=0 ; i < src->elements.count() ; ++i) {
276 ListElement *e = src->elements.at(i);
277 int uid = e->getUid();
279 QHash<int, ElementSync>::iterator it = elementHash.find(uid);
280 if (it == elementHash.end()) {
283 elementHash.insert(uid, sync);
285 ElementSync &sync = it.value();
290 // Get list of elements that are in the target but no longer in the source. These get deleted first.
291 QHash<int, ElementSync>::iterator it = elementHash.begin();
292 QHash<int, ElementSync>::iterator end = elementHash.end();
294 const ElementSync &s = it.value();
296 s.target->destroy(target->m_layout);
297 target->elements.removeOne(s.target);
304 ListLayout::sync(src->m_layout, target->m_layout);
306 // Clear the target list, and append in correct order from the source
307 target->elements.clear();
308 for (int i=0 ; i < src->elements.count() ; ++i) {
309 ListElement *srcElement = src->elements.at(i);
310 it = elementHash.find(srcElement->getUid());
311 const ElementSync &s = it.value();
312 ListElement *targetElement = s.target;
313 if (targetElement == 0) {
314 targetElement = new ListElement(srcElement->getUid());
316 ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout, targetModelHash);
317 target->elements.append(targetElement);
320 target->updateCacheIndices();
322 // Update values stored in target meta objects
323 for (int i=0 ; i < target->elements.count() ; ++i) {
324 ListElement *e = target->elements[i];
325 if (e->m_objectCache)
326 e->m_objectCache->updateValues();
330 ListModel::ListModel(ListLayout *layout, QDeclarativeListModel *modelCache, int uid) : m_layout(layout), m_modelCache(modelCache)
333 uid = uidCounter.fetchAndAddOrdered(1);
337 void ListModel::destroy()
342 if (m_modelCache && m_modelCache->m_primary == false)
347 int ListModel::appendElement()
349 int elementIndex = elements.count();
350 newElement(elementIndex);
354 void ListModel::insertElement(int index)
357 updateCacheIndices();
360 void ListModel::move(int from, int to, int n)
363 // Only move forwards - flip if backwards moving
371 QPODVector<ListElement *, 4> store;
372 for (int i=0 ; i < (to-from) ; ++i)
373 store.append(elements[from+n+i]);
374 for (int i=0 ; i < n ; ++i)
375 store.append(elements[from+i]);
376 for (int i=0 ; i < store.count() ; ++i)
377 elements[from+i] = store[i];
379 updateCacheIndices();
382 void ListModel::newElement(int index)
384 ListElement *e = new ListElement;
385 elements.insert(index, e);
388 void ListModel::updateCacheIndices()
390 for (int i=0 ; i < elements.count() ; ++i) {
391 ListElement *e = elements.at(i);
392 if (e->m_objectCache) {
393 e->m_objectCache->m_elementIndex = i;
398 QVariant ListModel::getProperty(int elementIndex, int roleIndex, const QDeclarativeListModel *owner, QV8Engine *eng)
400 ListElement *e = elements[elementIndex];
401 const ListLayout::Role &r = m_layout->getExistingRole(roleIndex);
402 return e->getProperty(r, owner, eng);
405 ListModel *ListModel::getListProperty(int elementIndex, const ListLayout::Role &role)
407 ListElement *e = elements[elementIndex];
408 return e->getListProperty(role);
411 void ListModel::set(int elementIndex, v8::Handle<v8::Object> object, QList<int> *roles, QV8Engine *eng)
413 ListElement *e = elements[elementIndex];
415 v8::Local<v8::Array> propertyNames = object->GetPropertyNames();
416 int propertyCount = propertyNames->Length();
418 for (int i=0 ; i < propertyCount ; ++i) {
419 v8::Local<v8::String> propertyName = propertyNames->Get(i)->ToString();
420 v8::Local<v8::Value> propertyValue = object->Get(propertyName);
422 // Check if this key exists yet
426 if (propertyValue->IsString()) {
427 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String);
428 v8::Handle<v8::String> jsString = propertyValue->ToString();
430 qstr.resize(jsString->Length());
431 jsString->Write(reinterpret_cast<uint16_t*>(qstr.data()));
432 roleIndex = e->setStringProperty(r, qstr);
433 } else if (propertyValue->IsNumber()) {
434 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number);
435 roleIndex = e->setDoubleProperty(r, propertyValue->NumberValue());
436 } else if (propertyValue->IsArray()) {
437 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
438 ListModel *subModel = new ListModel(r.subLayout, 0, -1);
440 v8::Handle<v8::Array> subArray = v8::Handle<v8::Array>::Cast(propertyValue);
441 int arrayLength = subArray->Length();
442 for (int j=0 ; j < arrayLength ; ++j) {
443 v8::Handle<v8::Object> subObject = subArray->Get(j)->ToObject();
444 subModel->append(subObject, eng);
447 roleIndex = e->setListProperty(r, subModel);
448 } else if (propertyValue->IsBoolean()) {
449 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool);
450 roleIndex = e->setBoolProperty(r, propertyValue->BooleanValue());
451 } else if (propertyValue->IsObject()) {
452 QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource();
453 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
454 QObject *o = QV8QObjectWrapper::toQObject(r);
455 const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
456 if (role.type == ListLayout::Role::QObject)
457 roleIndex = e->setQObjectProperty(role, o);
459 const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap);
460 if (role.type == ListLayout::Role::VariantMap)
461 roleIndex = e->setVariantMapProperty(role, propertyValue->ToObject(), eng);
463 } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) {
464 const ListLayout::Role *r = m_layout->getExistingRole(propertyName);
466 e->clearProperty(*r);
470 roles->append(roleIndex);
473 if (e->m_objectCache) {
474 e->m_objectCache->updateValues(*roles);
478 void ListModel::set(int elementIndex, v8::Handle<v8::Object> object, QV8Engine *eng)
480 ListElement *e = elements[elementIndex];
482 v8::Local<v8::Array> propertyNames = object->GetPropertyNames();
483 int propertyCount = propertyNames->Length();
485 for (int i=0 ; i < propertyCount ; ++i) {
486 v8::Local<v8::String> propertyName = propertyNames->Get(i)->ToString();
487 v8::Local<v8::Value> propertyValue = object->Get(propertyName);
490 if (propertyValue->IsString()) {
491 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String);
492 if (r.type == ListLayout::Role::String) {
493 v8::Handle<v8::String> jsString = propertyValue->ToString();
495 qstr.resize(jsString->Length());
496 jsString->Write(reinterpret_cast<uint16_t*>(qstr.data()));
497 e->setStringPropertyFast(r, qstr);
499 } else if (propertyValue->IsNumber()) {
500 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number);
501 if (r.type == ListLayout::Role::Number) {
502 e->setDoublePropertyFast(r, propertyValue->NumberValue());
504 } else if (propertyValue->IsArray()) {
505 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
506 if (r.type == ListLayout::Role::List) {
507 ListModel *subModel = new ListModel(r.subLayout, 0, -1);
509 v8::Handle<v8::Array> subArray = v8::Handle<v8::Array>::Cast(propertyValue);
510 int arrayLength = subArray->Length();
511 for (int j=0 ; j < arrayLength ; ++j) {
512 v8::Handle<v8::Object> subObject = subArray->Get(j)->ToObject();
513 subModel->append(subObject, eng);
516 e->setListPropertyFast(r, subModel);
518 } else if (propertyValue->IsBoolean()) {
519 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool);
520 if (r.type == ListLayout::Role::Bool) {
521 e->setBoolPropertyFast(r, propertyValue->BooleanValue());
523 } else if (propertyValue->IsObject()) {
524 QV8ObjectResource *r = (QV8ObjectResource *) propertyValue->ToObject()->GetExternalResource();
525 if (r && r->resourceType() == QV8ObjectResource::QObjectType) {
526 QObject *o = QV8QObjectWrapper::toQObject(r);
527 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
528 if (r.type == ListLayout::Role::QObject)
529 e->setQObjectPropertyFast(r, o);
531 const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap);
532 if (role.type == ListLayout::Role::VariantMap)
533 e->setVariantMapFast(role, propertyValue->ToObject(), eng);
535 } else if (propertyValue.IsEmpty() || propertyValue->IsUndefined() || propertyValue->IsNull()) {
536 const ListLayout::Role *r = m_layout->getExistingRole(propertyName);
538 e->clearProperty(*r);
543 void ListModel::clear()
545 int elementCount = elements.count();
546 for (int i=0 ; i < elementCount ; ++i) {
547 elements[i]->destroy(m_layout);
553 void ListModel::remove(int index, int count)
555 for (int i=0 ; i < count ; ++i) {
556 elements[index+i]->destroy(m_layout);
557 delete elements[index+i];
559 elements.remove(index, count);
560 updateCacheIndices();
563 void ListModel::insert(int elementIndex, v8::Handle<v8::Object> object, QV8Engine *eng)
565 insertElement(elementIndex);
566 set(elementIndex, object, eng);
569 int ListModel::append(v8::Handle<v8::Object> object, QV8Engine *eng)
571 int elementIndex = appendElement();
572 set(elementIndex, object, eng);
576 int ListModel::setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data)
580 if (elementIndex >= 0 && elementIndex < elements.count()) {
581 ListElement *e = elements[elementIndex];
583 const ListLayout::Role *r = m_layout->getRoleOrCreate(key, data);
585 roleIndex = e->setVariantProperty(*r, data);
587 if (roleIndex != -1 && e->m_objectCache) {
590 e->m_objectCache->updateValues(roles);
598 int ListModel::setExistingProperty(int elementIndex, const QString &key, v8::Handle<v8::Value> data, QV8Engine *eng)
602 if (elementIndex >= 0 && elementIndex < elements.count()) {
603 ListElement *e = elements[elementIndex];
604 const ListLayout::Role *r = m_layout->getExistingRole(key);
606 roleIndex = e->setJsProperty(*r, data, eng);
612 inline char *ListElement::getPropertyMemory(const ListLayout::Role &role)
614 ListElement *e = this;
616 while (blockIndex < role.blockIndex) {
618 e->next = new ListElement;
625 char *mem = &e->data[role.blockOffset];
629 QString *ListElement::getStringProperty(const ListLayout::Role &role)
631 char *mem = getPropertyMemory(role);
632 QString *s = reinterpret_cast<QString *>(mem);
633 return s->data_ptr() ? s : 0;
636 QObject *ListElement::getQObjectProperty(const ListLayout::Role &role)
638 char *mem = getPropertyMemory(role);
639 QDeclarativeGuard<QObject> *o = reinterpret_cast<QDeclarativeGuard<QObject> *>(mem);
643 QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role)
645 QVariantMap *map = 0;
647 char *mem = getPropertyMemory(role);
648 if (isMemoryUsed<QVariantMap>(mem))
649 map = reinterpret_cast<QVariantMap *>(mem);
654 QDeclarativeGuard<QObject> *ListElement::getGuardProperty(const ListLayout::Role &role)
656 char *mem = getPropertyMemory(role);
658 bool existingGuard = false;
659 for (size_t i=0 ; i < sizeof(QDeclarativeGuard<QObject>) ; ++i) {
661 existingGuard = true;
666 QDeclarativeGuard<QObject> *o = 0;
669 o = reinterpret_cast<QDeclarativeGuard<QObject> *>(mem);
674 ListModel *ListElement::getListProperty(const ListLayout::Role &role)
676 char *mem = getPropertyMemory(role);
677 ListModel **value = reinterpret_cast<ListModel **>(mem);
681 QVariant ListElement::getProperty(const ListLayout::Role &role, const QDeclarativeListModel *owner, QV8Engine *eng)
683 char *mem = getPropertyMemory(role);
688 case ListLayout::Role::Number:
690 double *value = reinterpret_cast<double *>(mem);
694 case ListLayout::Role::String:
696 QString *value = reinterpret_cast<QString *>(mem);
697 if (value->data_ptr() != 0)
701 case ListLayout::Role::Bool:
703 bool *value = reinterpret_cast<bool *>(mem);
707 case ListLayout::Role::List:
709 ListModel **value = reinterpret_cast<ListModel **>(mem);
710 ListModel *model = *value;
713 if (model->m_modelCache == 0) {
714 model->m_modelCache = new QDeclarativeListModel(owner, model, eng);
715 QDeclarativeEngine::setContextForObject(model->m_modelCache, QDeclarativeEngine::contextForObject(owner));
718 QObject *object = model->m_modelCache;
719 data = QVariant::fromValue(object);
723 case ListLayout::Role::QObject:
725 QDeclarativeGuard<QObject> *guard = reinterpret_cast<QDeclarativeGuard<QObject> *>(mem);
726 QObject *object = guard->data();
728 data = QVariant::fromValue(object);
731 case ListLayout::Role::VariantMap:
733 if (isMemoryUsed<QVariantMap>(mem)) {
734 QVariantMap *map = reinterpret_cast<QVariantMap *>(mem);
746 int ListElement::setStringProperty(const ListLayout::Role &role, const QString &s)
750 if (role.type == ListLayout::Role::String) {
751 char *mem = getPropertyMemory(role);
752 QString *c = reinterpret_cast<QString *>(mem);
754 if (c->data_ptr() == 0) {
755 new (mem) QString(s);
758 changed = c->compare(s) != 0;
762 roleIndex = role.index;
768 int ListElement::setDoubleProperty(const ListLayout::Role &role, double d)
772 if (role.type == ListLayout::Role::Number) {
773 char *mem = getPropertyMemory(role);
774 double *value = new (mem) double;
775 bool changed = *value != d;
778 roleIndex = role.index;
784 int ListElement::setBoolProperty(const ListLayout::Role &role, bool b)
788 if (role.type == ListLayout::Role::Bool) {
789 char *mem = getPropertyMemory(role);
790 bool *value = new (mem) bool;
791 bool changed = *value != b;
794 roleIndex = role.index;
800 int ListElement::setListProperty(const ListLayout::Role &role, ListModel *m)
804 if (role.type == ListLayout::Role::List) {
805 char *mem = getPropertyMemory(role);
806 ListModel **value = new (mem) ListModel *;
812 roleIndex = role.index;
818 int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o)
822 if (role.type == ListLayout::Role::QObject) {
823 char *mem = getPropertyMemory(role);
824 QDeclarativeGuard<QObject> *g = reinterpret_cast<QDeclarativeGuard<QObject> *>(mem);
825 bool existingGuard = false;
826 for (size_t i=0 ; i < sizeof(QDeclarativeGuard<QObject>) ; ++i) {
828 existingGuard = true;
834 changed = g->data() != o;
835 g->~QDeclarativeGuard();
839 new (mem) QDeclarativeGuard<QObject>(o);
841 roleIndex = role.index;
847 int ListElement::setVariantMapProperty(const ListLayout::Role &role, v8::Handle<v8::Object> o, QV8Engine *eng)
851 if (role.type == ListLayout::Role::VariantMap) {
852 char *mem = getPropertyMemory(role);
853 if (isMemoryUsed<QVariantMap>(mem)) {
854 QVariantMap *map = reinterpret_cast<QVariantMap *>(mem);
857 new (mem) QVariantMap(eng->variantMapFromJS(o));
858 roleIndex = role.index;
864 int ListElement::setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m)
868 if (role.type == ListLayout::Role::VariantMap) {
869 char *mem = getPropertyMemory(role);
870 if (isMemoryUsed<QVariantMap>(mem)) {
871 QVariantMap *map = reinterpret_cast<QVariantMap *>(mem);
875 new (mem) QVariantMap(*m);
877 new (mem) QVariantMap;
878 roleIndex = role.index;
884 void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s)
886 char *mem = getPropertyMemory(role);
887 new (mem) QString(s);
890 void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d)
892 char *mem = getPropertyMemory(role);
893 double *value = new (mem) double;
897 void ListElement::setBoolPropertyFast(const ListLayout::Role &role, bool b)
899 char *mem = getPropertyMemory(role);
900 bool *value = new (mem) bool;
904 void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QObject *o)
906 char *mem = getPropertyMemory(role);
907 new (mem) QDeclarativeGuard<QObject>(o);
910 void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m)
912 char *mem = getPropertyMemory(role);
913 ListModel **value = new (mem) ListModel *;
917 void ListElement::setVariantMapFast(const ListLayout::Role &role, v8::Handle<v8::Object> o, QV8Engine *eng)
919 char *mem = getPropertyMemory(role);
920 QVariantMap *map = new (mem) QVariantMap;
921 *map = eng->variantMapFromJS(o);
924 void ListElement::clearProperty(const ListLayout::Role &role)
927 case ListLayout::Role::String:
928 setStringProperty(role, QString());
930 case ListLayout::Role::Number:
931 setDoubleProperty(role, 0.0);
933 case ListLayout::Role::Bool:
934 setBoolProperty(role, false);
936 case ListLayout::Role::List:
937 setListProperty(role, 0);
939 case ListLayout::Role::QObject:
940 setQObjectProperty(role, 0);
942 case ListLayout::Role::VariantMap:
943 setVariantMapProperty(role, 0);
950 ListElement::ListElement()
953 uid = uidCounter.fetchAndAddOrdered(1);
955 qMemSet(data, 0, sizeof(data));
958 ListElement::ListElement(int existingUid)
963 qMemSet(data, 0, sizeof(data));
966 ListElement::~ListElement()
971 void ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout, QHash<int, ListModel *> *targetModelHash)
973 for (int i=0 ; i < srcLayout->roleCount() ; ++i) {
974 const ListLayout::Role &srcRole = srcLayout->getExistingRole(i);
975 const ListLayout::Role &targetRole = targetLayout->getExistingRole(i);
977 switch (srcRole.type) {
978 case ListLayout::Role::List:
980 ListModel *srcSubModel = src->getListProperty(srcRole);
981 ListModel *targetSubModel = target->getListProperty(targetRole);
984 if (targetSubModel == 0) {
985 targetSubModel = new ListModel(targetRole.subLayout, 0, srcSubModel->getUid());
986 target->setListPropertyFast(targetRole, targetSubModel);
988 ListModel::sync(srcSubModel, targetSubModel, targetModelHash);
992 case ListLayout::Role::QObject:
994 QObject *object = src->getQObjectProperty(srcRole);
995 target->setQObjectProperty(targetRole, object);
998 case ListLayout::Role::String:
999 case ListLayout::Role::Number:
1000 case ListLayout::Role::Bool:
1002 QVariant v = src->getProperty(srcRole, 0, 0);
1003 target->setVariantProperty(targetRole, v);
1005 case ListLayout::Role::VariantMap:
1007 QVariantMap *map = src->getVariantMapProperty(srcRole);
1008 target->setVariantMapProperty(targetRole, map);
1018 void ListElement::destroy(ListLayout *layout)
1021 for (int i=0 ; i < layout->roleCount() ; ++i) {
1022 const ListLayout::Role &r = layout->getExistingRole(i);
1025 case ListLayout::Role::String:
1027 QString *string = getStringProperty(r);
1032 case ListLayout::Role::List:
1034 ListModel *model = getListProperty(r);
1041 case ListLayout::Role::QObject:
1043 QDeclarativeGuard<QObject> *guard = getGuardProperty(r);
1045 guard->~QDeclarativeGuard();
1048 case ListLayout::Role::VariantMap:
1050 QVariantMap *map = getVariantMapProperty(r);
1056 // other types don't need explicit cleanup.
1061 delete m_objectCache;
1069 int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant &d)
1073 switch (role.type) {
1074 case ListLayout::Role::Number:
1075 roleIndex = setDoubleProperty(role, d.toDouble());
1077 case ListLayout::Role::String:
1078 roleIndex = setStringProperty(role, d.toString());
1080 case ListLayout::Role::Bool:
1081 roleIndex = setBoolProperty(role, d.toBool());
1083 case ListLayout::Role::List:
1084 roleIndex = setListProperty(role, d.value<ListModel *>());
1086 case ListLayout::Role::VariantMap: {
1087 QVariantMap map = d.toMap();
1088 roleIndex = setVariantMapProperty(role, &map);
1098 int ListElement::setJsProperty(const ListLayout::Role &role, v8::Handle<v8::Value> d, QV8Engine *eng)
1100 // Check if this key exists yet
1103 // Add the value now
1104 if (d->IsString()) {
1105 v8::Handle<v8::String> jsString = d->ToString();
1107 qstr.resize(jsString->Length());
1108 jsString->Write(reinterpret_cast<uint16_t*>(qstr.data()));
1109 roleIndex = setStringProperty(role, qstr);
1110 } else if (d->IsNumber()) {
1111 roleIndex = setDoubleProperty(role, d->NumberValue());
1112 } else if (d->IsArray()) {
1113 ListModel *subModel = new ListModel(role.subLayout, 0, -1);
1114 v8::Handle<v8::Array> subArray = v8::Handle<v8::Array>::Cast(d);
1115 int arrayLength = subArray->Length();
1116 for (int j=0 ; j < arrayLength ; ++j) {
1117 v8::Handle<v8::Object> subObject = subArray->Get(j)->ToObject();
1118 subModel->append(subObject, eng);
1120 roleIndex = setListProperty(role, subModel);
1121 } else if (d->IsBoolean()) {
1122 roleIndex = setBoolProperty(role, d->BooleanValue());
1123 } else if (d->IsObject()) {
1124 QV8ObjectResource *r = (QV8ObjectResource *) d->ToObject()->GetExternalResource();
1125 if (role.type == ListLayout::Role::QObject && r && r->resourceType() == QV8ObjectResource::QObjectType) {
1126 QObject *o = QV8QObjectWrapper::toQObject(r);
1127 roleIndex = setQObjectProperty(role, o);
1128 } else if (role.type == ListLayout::Role::VariantMap) {
1129 roleIndex = setVariantMapProperty(role, d->ToObject(), eng);
1131 } else if (d.IsEmpty() || d->IsUndefined() || d->IsNull()) {
1132 clearProperty(role);
1138 ModelObject::ModelObject(QDeclarativeListModel *model, int elementIndex)
1139 : m_model(model), m_elementIndex(elementIndex), m_meta(new ModelNodeMetaObject(this))
1142 setNodeUpdatesEnabled(true);
1145 void ModelObject::updateValues()
1147 int roleCount = m_model->m_listModel->roleCount();
1148 for (int i=0 ; i < roleCount ; ++i) {
1149 const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i);
1150 QByteArray name = role.name.toUtf8();
1151 const QVariant &data = m_model->data(m_elementIndex, i);
1152 setValue(name, data, role.type == ListLayout::Role::List);
1156 void ModelObject::updateValues(const QList<int> &roles)
1158 int roleCount = roles.count();
1159 for (int i=0 ; i < roleCount ; ++i) {
1160 int roleIndex = roles.at(i);
1161 const ListLayout::Role &role = m_model->m_listModel->getExistingRole(roleIndex);
1162 QByteArray name = role.name.toUtf8();
1163 const QVariant &data = m_model->data(m_elementIndex, roleIndex);
1164 setValue(name, data, role.type == ListLayout::Role::List);
1168 ModelNodeMetaObject::ModelNodeMetaObject(ModelObject *object)
1169 : QDeclarativeOpenMetaObject(object), m_enabled(false), m_obj(object)
1173 ModelNodeMetaObject::~ModelNodeMetaObject()
1177 void ModelNodeMetaObject::propertyWritten(int index)
1182 QV8Engine *eng = m_obj->m_model->engine();
1184 QString propName = QString::fromUtf8(name(index));
1185 QVariant value = operator[](index);
1187 v8::HandleScope handle_scope;
1188 v8::Context::Scope scope(eng->context());
1190 v8::Handle<v8::Value> v = eng->fromVariant(value);
1192 int roleIndex = m_obj->m_model->m_listModel->setExistingProperty(m_obj->m_elementIndex, propName, v, eng);
1193 if (roleIndex != -1) {
1196 m_obj->m_model->emitItemsChanged(m_obj->m_elementIndex, 1, roles);
1200 DynamicRoleModelNode::DynamicRoleModelNode(QDeclarativeListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this))
1202 setNodeUpdatesEnabled(true);
1205 DynamicRoleModelNode *DynamicRoleModelNode::create(const QVariantMap &obj, QDeclarativeListModel *owner)
1207 DynamicRoleModelNode *object = new DynamicRoleModelNode(owner, uidCounter.fetchAndAddOrdered(1));
1209 object->updateValues(obj, roles);
1213 void DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target, QHash<int, QDeclarativeListModel *> *targetModelHash)
1215 for (int i=0 ; i < src->m_meta->count() ; ++i) {
1216 const QByteArray &name = src->m_meta->name(i);
1217 QVariant value = src->m_meta->value(i);
1219 QDeclarativeListModel *srcModel = qobject_cast<QDeclarativeListModel *>(value.value<QObject *>());
1220 QDeclarativeListModel *targetModel = qobject_cast<QDeclarativeListModel *>(target->m_meta->value(i).value<QObject *>());
1223 if (targetModel == 0)
1224 targetModel = QDeclarativeListModel::createWithOwner(target->m_owner);
1226 QDeclarativeListModel::sync(srcModel, targetModel, targetModelHash);
1228 QObject *targetModelObject = targetModel;
1229 value = QVariant::fromValue(targetModelObject);
1230 } else if (targetModel) {
1234 target->setValue(name, value);
1238 void DynamicRoleModelNode::updateValues(const QVariantMap &object, QList<int> &roles)
1240 const QList<QString> &keys = object.keys();
1242 QList<QString>::const_iterator it = keys.begin();
1243 QList<QString>::const_iterator end = keys.end();
1246 const QString &key = *it;
1248 int roleIndex = m_owner->m_roles.indexOf(key);
1249 if (roleIndex == -1) {
1250 roleIndex = m_owner->m_roles.count();
1251 m_owner->m_roles.append(key);
1254 QVariant value = object[key];
1256 if (value.type() == QVariant::List) {
1257 QDeclarativeListModel *subModel = QDeclarativeListModel::createWithOwner(m_owner);
1259 QVariantList subArray = value.toList();
1260 QVariantList::const_iterator subIt = subArray.begin();
1261 QVariantList::const_iterator subEnd = subArray.end();
1262 while (subIt != subEnd) {
1263 const QVariantMap &subObject = subIt->toMap();
1264 subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1268 QObject *subModelObject = subModel;
1269 value = QVariant::fromValue(subModelObject);
1272 const QByteArray &keyUtf8 = key.toUtf8();
1274 QDeclarativeListModel *existingModel = qobject_cast<QDeclarativeListModel *>(m_meta->value(keyUtf8).value<QObject *>());
1276 delete existingModel;
1278 if (m_meta->setValue(keyUtf8, value))
1285 DynamicRoleModelNodeMetaObject::DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object)
1286 : QDeclarativeOpenMetaObject(object), m_enabled(false), m_owner(object)
1290 DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject()
1292 for (int i=0 ; i < count() ; ++i) {
1293 QDeclarativeListModel *subModel = qobject_cast<QDeclarativeListModel *>(value(i).value<QObject *>());
1299 void DynamicRoleModelNodeMetaObject::propertyWrite(int index)
1304 QVariant v = value(index);
1305 QDeclarativeListModel *model = qobject_cast<QDeclarativeListModel *>(v.value<QObject *>());
1310 void DynamicRoleModelNodeMetaObject::propertyWritten(int index)
1315 QDeclarativeListModel *parentModel = m_owner->m_owner;
1317 QVariant v = value(index);
1318 if (v.type() == QVariant::List) {
1319 QDeclarativeListModel *subModel = QDeclarativeListModel::createWithOwner(parentModel);
1321 QVariantList subArray = v.toList();
1322 QVariantList::const_iterator subIt = subArray.begin();
1323 QVariantList::const_iterator subEnd = subArray.end();
1324 while (subIt != subEnd) {
1325 const QVariantMap &subObject = subIt->toMap();
1326 subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1330 QObject *subModelObject = subModel;
1331 v = QVariant::fromValue(subModelObject);
1336 int elementIndex = parentModel->m_modelObjects.indexOf(m_owner);
1337 int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData()));
1339 if (elementIndex != -1 && roleIndex != -1) {
1343 parentModel->emitItemsChanged(elementIndex, 1, roles);
1347 QDeclarativeListModelParser::ListInstruction *QDeclarativeListModelParser::ListModelData::instructions() const
1349 return (QDeclarativeListModelParser::ListInstruction *)((char *)this + sizeof(ListModelData));
1353 \qmlclass ListModel QDeclarativeListModel
1354 \inqmlmodule QtQuick 2
1355 \ingroup qml-working-with-data
1356 \brief The ListModel element defines a free-form list data source.
1358 The ListModel is a simple container of ListElement definitions, each containing data roles.
1359 The contents can be defined dynamically, or explicitly in QML.
1361 The number of elements in the model can be obtained from its \l count property.
1362 A number of familiar methods are also provided to manipulate the contents of the
1363 model, including append(), insert(), move(), remove() and set(). These methods
1364 accept dictionaries as their arguments; these are translated to ListElement objects
1367 Elements can be manipulated via the model using the setProperty() method, which
1368 allows the roles of the specified element to be set and changed.
1370 \section1 Example Usage
1372 The following example shows a ListModel containing three elements, with the roles
1375 \div {class="float-right"}
1376 \inlineimage listmodel.png
1379 \snippet doc/src/snippets/declarative/listmodel.qml 0
1382 Roles (properties) in each element must begin with a lower-case letter and
1383 should be common to all elements in a model. The ListElement documentation
1384 provides more guidelines for how elements should be defined.
1386 Since the example model contains an \c id property, it can be referenced
1387 by views, such as the ListView in this example:
1389 \snippet doc/src/snippets/declarative/listmodel-simple.qml 0
1391 \snippet doc/src/snippets/declarative/listmodel-simple.qml 1
1393 It is possible for roles to contain list data. In the following example we
1394 create a list of fruit attributes:
1396 \snippet doc/src/snippets/declarative/listmodel-nested.qml model
1398 The delegate displays all the fruit attributes:
1400 \div {class="float-right"}
1401 \inlineimage listmodel-nested.png
1404 \snippet doc/src/snippets/declarative/listmodel-nested.qml delegate
1407 \section1 Modifying List Models
1409 The content of a ListModel may be created and modified using the clear(),
1410 append(), set(), insert() and setProperty() methods. For example:
1412 \snippet doc/src/snippets/declarative/listmodel-modify.qml delegate
1414 Note that when creating content dynamically the set of available properties
1415 cannot be changed once set. Whatever properties are first added to the model
1416 are the only permitted properties in the model.
1418 \section1 Using Threaded List Models with WorkerScript
1420 ListModel can be used together with WorkerScript access a list model
1421 from multiple threads. This is useful if list modifications are
1422 synchronous and take some time: the list operations can be moved to a
1423 different thread to avoid blocking of the main GUI thread.
1425 Here is an example that uses WorkerScript to periodically append the
1426 current time to a list model:
1428 \snippet examples/declarative/threading/threadedlistmodel/timedisplay.qml 0
1430 The included file, \tt dataloader.js, looks like this:
1432 \snippet examples/declarative/threading/threadedlistmodel/dataloader.js 0
1434 The timer in the main example sends messages to the worker script by calling
1435 \l WorkerScript::sendMessage(). When this message is received,
1436 \l{WorkerScript::onMessage}{WorkerScript.onMessage()} is invoked in \c dataloader.js,
1437 which appends the current time to the list model.
1439 Note the call to sync() from the \l{WorkerScript::onMessage}{WorkerScript.onMessage()}
1440 handler. You must call sync() or else the changes made to the list from the external
1441 thread will not be reflected in the list model in the main thread.
1443 \sa {qmlmodels}{Data Models}, {declarative/threading/threadedlistmodel}{Threaded ListModel example}, QtDeclarative
1446 QDeclarativeListModel::QDeclarativeListModel(QObject *parent)
1447 : QListModelInterface(parent)
1449 m_mainThread = true;
1452 m_uid = uidCounter.fetchAndAddOrdered(1);
1453 m_dynamicRoles = false;
1455 m_layout = new ListLayout;
1456 m_listModel = new ListModel(m_layout, this, -1);
1461 QDeclarativeListModel::QDeclarativeListModel(const QDeclarativeListModel *owner, ListModel *data, QV8Engine *eng, QObject *parent)
1462 : QListModelInterface(parent)
1464 m_mainThread = owner->m_mainThread;
1466 m_agent = owner->m_agent;
1468 Q_ASSERT(owner->m_dynamicRoles == false);
1469 m_dynamicRoles = false;
1476 QDeclarativeListModel::QDeclarativeListModel(QDeclarativeListModel *orig, QDeclarativeListModelWorkerAgent *agent)
1477 : QListModelInterface(agent)
1479 m_mainThread = false;
1482 m_dynamicRoles = orig->m_dynamicRoles;
1484 m_layout = new ListLayout(orig->m_layout);
1485 m_listModel = new ListModel(m_layout, this, orig->m_listModel->getUid());
1488 sync(orig, this, 0);
1490 ListModel::sync(orig->m_listModel, m_listModel, 0);
1495 QDeclarativeListModel::~QDeclarativeListModel()
1497 for (int i=0 ; i < m_modelObjects.count() ; ++i)
1498 delete m_modelObjects[i];
1501 m_listModel->destroy();
1504 if (m_mainThread && m_agent)
1514 QDeclarativeListModel *QDeclarativeListModel::createWithOwner(QDeclarativeListModel *newOwner)
1516 QDeclarativeListModel *model = new QDeclarativeListModel;
1518 model->m_mainThread = newOwner->m_mainThread;
1519 model->m_engine = newOwner->m_engine;
1520 model->m_agent = newOwner->m_agent;
1521 model->m_dynamicRoles = newOwner->m_dynamicRoles;
1523 QDeclarativeEngine::setContextForObject(model, QDeclarativeEngine::contextForObject(newOwner));
1528 QV8Engine *QDeclarativeListModel::engine() const
1530 if (m_engine == 0) {
1531 m_engine = QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this));
1537 void QDeclarativeListModel::sync(QDeclarativeListModel *src, QDeclarativeListModel *target, QHash<int, QDeclarativeListModel *> *targetModelHash)
1539 Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles);
1541 target->m_uid = src->m_uid;
1542 if (targetModelHash)
1543 targetModelHash->insert(target->m_uid, target);
1544 target->m_roles = src->m_roles;
1546 // Build hash of elements <-> uid for each of the lists
1547 QHash<int, ElementSync> elementHash;
1548 for (int i=0 ; i < target->m_modelObjects.count() ; ++i) {
1549 DynamicRoleModelNode *e = target->m_modelObjects.at(i);
1550 int uid = e->getUid();
1553 elementHash.insert(uid, sync);
1555 for (int i=0 ; i < src->m_modelObjects.count() ; ++i) {
1556 DynamicRoleModelNode *e = src->m_modelObjects.at(i);
1557 int uid = e->getUid();
1559 QHash<int, ElementSync>::iterator it = elementHash.find(uid);
1560 if (it == elementHash.end()) {
1563 elementHash.insert(uid, sync);
1565 ElementSync &sync = it.value();
1570 // Get list of elements that are in the target but no longer in the source. These get deleted first.
1571 QHash<int, ElementSync>::iterator it = elementHash.begin();
1572 QHash<int, ElementSync>::iterator end = elementHash.end();
1574 const ElementSync &s = it.value();
1576 int targetIndex = target->m_modelObjects.indexOf(s.target);
1577 target->m_modelObjects.remove(targetIndex, 1);
1583 // Clear the target list, and append in correct order from the source
1584 target->m_modelObjects.clear();
1585 for (int i=0 ; i < src->m_modelObjects.count() ; ++i) {
1586 DynamicRoleModelNode *srcElement = src->m_modelObjects.at(i);
1587 it = elementHash.find(srcElement->getUid());
1588 const ElementSync &s = it.value();
1589 DynamicRoleModelNode *targetElement = s.target;
1590 if (targetElement == 0) {
1591 targetElement = new DynamicRoleModelNode(target, srcElement->getUid());
1593 DynamicRoleModelNode::sync(srcElement, targetElement, targetModelHash);
1594 target->m_modelObjects.append(targetElement);
1598 void QDeclarativeListModel::emitItemsChanged(int index, int count, const QList<int> &roles)
1601 emit itemsChanged(index, count, roles);
1603 int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
1604 m_agent->data.changedChange(uid, index, count, roles);
1608 void QDeclarativeListModel::emitItemsRemoved(int index, int count)
1611 emit itemsRemoved(index, count);
1612 emit countChanged();
1614 int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
1615 if (index == 0 && count == this->count())
1616 m_agent->data.clearChange(uid);
1617 m_agent->data.removeChange(uid, index, count);
1621 void QDeclarativeListModel::emitItemsInserted(int index, int count)
1624 emit itemsInserted(index, count);
1625 emit countChanged();
1627 int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
1628 m_agent->data.insertChange(uid, index, count);
1632 void QDeclarativeListModel::emitItemsMoved(int from, int to, int n)
1635 emit itemsMoved(from, to, n);
1637 int uid = m_dynamicRoles ? getUid() : m_listModel->getUid();
1638 m_agent->data.moveChange(uid, from, n, to);
1642 QDeclarativeListModelWorkerAgent *QDeclarativeListModel::agent()
1647 m_agent = new QDeclarativeListModelWorkerAgent(this);
1651 QList<int> QDeclarativeListModel::roles() const
1653 QList<int> rolesArray;
1655 if (m_dynamicRoles) {
1656 for (int i=0 ; i < m_roles.count() ; ++i)
1659 for (int i=0 ; i < m_listModel->roleCount() ; ++i)
1666 QString QDeclarativeListModel::toString(int role) const
1670 if (m_dynamicRoles) {
1671 roleName = m_roles[role];
1673 const ListLayout::Role &r = m_listModel->getExistingRole(role);
1680 QVariant QDeclarativeListModel::data(int index, int role) const
1684 if (index >= count() || index < 0)
1688 v = m_modelObjects[index]->getValue(m_roles[role]);
1690 v = m_listModel->getProperty(index, role, this, engine());
1696 \qmlproperty bool QtQuick2::ListModel::dynamicRoles
1698 By default, the type of a role is fixed the first time
1699 the role is used. For example, if you create a role called
1700 "data" and assign a number to it, you can no longer assign
1701 a string to the "data" role. However, when the dynamicRoles
1702 property is enabled, the type of a given role is not fixed
1703 and can be different between elements.
1705 The dynamicRoles property must be set before any data is
1706 added to the ListModel, and must be set from the main
1709 A ListModel that has data statically defined (via the
1710 ListElement QML syntax) cannot have the dynamicRoles
1713 There is a significant performance cost to using a
1714 ListModel with dynamic roles enabled. The cost varies
1715 from platform to platform but is typically somewhere
1716 between 4-6x slower than using static role types.
1718 Due to the performance cost of using dynamic roles,
1719 they are disabled by default.
1721 void QDeclarativeListModel::setDynamicRoles(bool enableDynamicRoles)
1723 if (m_mainThread && m_agent == 0) {
1724 if (enableDynamicRoles) {
1725 if (m_layout->roleCount())
1726 qmlInfo(this) << tr("unable to enable dynamic roles as this model is not empty!");
1728 m_dynamicRoles = true;
1730 if (m_roles.count()) {
1731 qmlInfo(this) << tr("unable to enable static roles as this model is not empty!");
1733 m_dynamicRoles = false;
1737 qmlInfo(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created");
1742 \qmlproperty int QtQuick2::ListModel::count
1743 The number of data entries in the model.
1745 int QDeclarativeListModel::count() const
1750 count = m_modelObjects.count();
1752 count = m_listModel->elementCount();
1759 \qmlmethod QtQuick2::ListModel::clear()
1761 Deletes all content from the model.
1763 \sa append() remove()
1765 void QDeclarativeListModel::clear()
1767 int cleared = count();
1769 if (m_dynamicRoles) {
1770 for (int i=0 ; i < m_modelObjects.count() ; ++i)
1771 delete m_modelObjects[i];
1772 m_modelObjects.clear();
1774 m_listModel->clear();
1777 emitItemsRemoved(0, cleared);
1781 \qmlmethod QtQuick2::ListModel::remove(int index, int count = 1)
1783 Deletes the content at \a index from the model.
1787 void QDeclarativeListModel::remove(QDeclarativeV8Function *args)
1789 int argLength = args->Length();
1791 if (argLength == 1 || argLength == 2) {
1792 int index = (*args)[0]->Int32Value();
1793 int removeCount = (argLength == 2 ? ((*args)[1]->Int32Value()) : 1);
1795 if (index < 0 || index+removeCount > count() || removeCount <= 0) {
1796 qmlInfo(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count());
1800 if (m_dynamicRoles) {
1801 for (int i=0 ; i < removeCount ; ++i)
1802 delete m_modelObjects[index+i];
1803 m_modelObjects.remove(index, removeCount);
1805 m_listModel->remove(index, removeCount);
1808 emitItemsRemoved(index, removeCount);
1810 qmlInfo(this) << tr("remove: incorrect number of arguments");
1815 \qmlmethod QtQuick2::ListModel::insert(int index, jsobject dict)
1817 Adds a new item to the list model at position \a index, with the
1821 fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"})
1824 The \a index must be to an existing item in the list, or one past
1825 the end of the list (equivalent to append).
1830 void QDeclarativeListModel::insert(QDeclarativeV8Function *args)
1832 if (args->Length() == 2) {
1834 v8::Handle<v8::Value> arg0 = (*args)[0];
1835 int index = arg0->Int32Value();
1837 if (index < 0 || index > count()) {
1838 qmlInfo(this) << tr("insert: index %1 out of range").arg(index);
1842 v8::Handle<v8::Value> arg1 = (*args)[1];
1844 if (arg1->IsArray()) {
1845 v8::Handle<v8::Array> objectArray = v8::Handle<v8::Array>::Cast(arg1);
1846 int objectArrayLength = objectArray->Length();
1847 for (int i=0 ; i < objectArrayLength ; ++i) {
1848 v8::Handle<v8::Object> argObject = objectArray->Get(i)->ToObject();
1850 if (m_dynamicRoles) {
1851 m_modelObjects.insert(index+i, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
1853 m_listModel->insert(index+i, argObject, args->engine());
1856 emitItemsInserted(index, objectArrayLength);
1857 } else if (arg1->IsObject()) {
1858 v8::Handle<v8::Object> argObject = arg1->ToObject();
1860 if (m_dynamicRoles) {
1861 m_modelObjects.insert(index, DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
1863 m_listModel->insert(index, argObject, args->engine());
1866 emitItemsInserted(index, 1);
1868 qmlInfo(this) << tr("insert: value is not an object");
1871 qmlInfo(this) << tr("insert: value is not an object");
1876 \qmlmethod QtQuick2::ListModel::move(int from, int to, int n)
1878 Moves \a n items \a from one position \a to another.
1880 The from and to ranges must exist; for example, to move the first 3 items
1881 to the end of the list:
1884 fruitModel.move(0, fruitModel.count - 3, 3)
1889 void QDeclarativeListModel::move(int from, int to, int n)
1891 if (n==0 || from==to)
1893 if (!canMove(from, to, n)) {
1894 qmlInfo(this) << tr("move: out of range");
1898 if (m_dynamicRoles) {
1900 int realFrom = from;
1905 // Only move forwards - flip if backwards moving
1913 QPODVector<DynamicRoleModelNode *, 4> store;
1914 for (int i=0 ; i < (realTo-realFrom) ; ++i)
1915 store.append(m_modelObjects[realFrom+realN+i]);
1916 for (int i=0 ; i < realN ; ++i)
1917 store.append(m_modelObjects[realFrom+i]);
1918 for (int i=0 ; i < store.count() ; ++i)
1919 m_modelObjects[realFrom+i] = store[i];
1922 m_listModel->move(from, to, n);
1925 emitItemsMoved(from, to, n);
1929 \qmlmethod QtQuick2::ListModel::append(jsobject dict)
1931 Adds a new item to the end of the list model, with the
1935 fruitModel.append({"cost": 5.95, "name":"Pizza"})
1940 void QDeclarativeListModel::append(QDeclarativeV8Function *args)
1942 if (args->Length() == 1) {
1943 v8::Handle<v8::Value> arg = (*args)[0];
1945 if (arg->IsArray()) {
1946 v8::Handle<v8::Array> objectArray = v8::Handle<v8::Array>::Cast(arg);
1947 int objectArrayLength = objectArray->Length();
1949 int index = count();
1950 for (int i=0 ; i < objectArrayLength ; ++i) {
1951 v8::Handle<v8::Object> argObject = objectArray->Get(i)->ToObject();
1953 if (m_dynamicRoles) {
1954 m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
1956 m_listModel->append(argObject, args->engine());
1960 emitItemsInserted(index, objectArrayLength);
1961 } else if (arg->IsObject()) {
1962 v8::Handle<v8::Object> argObject = arg->ToObject();
1966 if (m_dynamicRoles) {
1967 index = m_modelObjects.count();
1968 m_modelObjects.append(DynamicRoleModelNode::create(args->engine()->variantMapFromJS(argObject), this));
1970 index = m_listModel->append(argObject, args->engine());
1973 emitItemsInserted(index, 1);
1975 qmlInfo(this) << tr("append: value is not an object");
1978 qmlInfo(this) << tr("append: value is not an object");
1983 \qmlmethod object QtQuick2::ListModel::get(int index)
1985 Returns the item at \a index in the list model. This allows the item
1986 data to be accessed or modified from JavaScript:
1989 Component.onCompleted: {
1990 fruitModel.append({"cost": 5.95, "name":"Jackfruit"});
1991 console.log(fruitModel.get(0).cost);
1992 fruitModel.get(0).cost = 10.95;
1996 The \a index must be an element in the list.
1998 Note that properties of the returned object that are themselves objects
1999 will also be models, and this get() method is used to access elements:
2002 fruitModel.append(..., "attributes":
2003 [{"name":"spikes","value":"7mm"},
2004 {"name":"color","value":"green"}]);
2005 fruitModel.get(0).attributes.get(1).value; // == "green"
2008 \warning The returned object is not guaranteed to remain valid. It
2009 should not be used in \l{Property Binding}{property bindings}.
2013 QDeclarativeV8Handle QDeclarativeListModel::get(int index) const
2015 v8::Handle<v8::Value> result = v8::Undefined();
2017 if (index >= 0 && index < count()) {
2018 QV8Engine *v8engine = engine();
2020 if (m_dynamicRoles) {
2021 DynamicRoleModelNode *object = m_modelObjects[index];
2022 result = v8engine->newQObject(object);
2024 ModelObject *object = m_listModel->getOrCreateModelObject(const_cast<QDeclarativeListModel *>(this), index);
2025 result = v8engine->newQObject(object);
2029 return QDeclarativeV8Handle::fromHandle(result);
2033 \qmlmethod QtQuick2::ListModel::set(int index, jsobject dict)
2035 Changes the item at \a index in the list model with the
2036 values in \a dict. Properties not appearing in \a dict
2040 fruitModel.set(3, {"cost": 5.95, "name":"Pizza"})
2043 If \a index is equal to count() then a new item is appended to the
2044 list. Otherwise, \a index must be an element in the list.
2048 void QDeclarativeListModel::set(int index, const QDeclarativeV8Handle &handle)
2050 v8::Handle<v8::Value> valuemap = handle.toHandle();
2052 if (!valuemap->IsObject() || valuemap->IsArray()) {
2053 qmlInfo(this) << tr("set: value is not an object");
2056 if (index > count() || index < 0) {
2057 qmlInfo(this) << tr("set: index %1 out of range").arg(index);
2061 v8::Handle<v8::Object> object = valuemap->ToObject();
2063 if (index == count()) {
2065 if (m_dynamicRoles) {
2066 m_modelObjects.append(DynamicRoleModelNode::create(engine()->variantMapFromJS(object), this));
2068 m_listModel->insert(index, object, engine());
2071 emitItemsInserted(index, 1);
2076 if (m_dynamicRoles) {
2077 m_modelObjects[index]->updateValues(engine()->variantMapFromJS(object), roles);
2079 m_listModel->set(index, object, &roles, engine());
2083 emitItemsChanged(index, 1, roles);
2088 \qmlmethod QtQuick2::ListModel::setProperty(int index, string property, variant value)
2090 Changes the \a property of the item at \a index in the list model to \a value.
2093 fruitModel.setProperty(3, "cost", 5.95)
2096 The \a index must be an element in the list.
2100 void QDeclarativeListModel::setProperty(int index, const QString& property, const QVariant& value)
2102 if (count() == 0 || index >= count() || index < 0) {
2103 qmlInfo(this) << tr("set: index %1 out of range").arg(index);
2107 if (m_dynamicRoles) {
2108 int roleIndex = m_roles.indexOf(property);
2109 if (roleIndex == -1) {
2110 roleIndex = m_roles.count();
2111 m_roles.append(property);
2113 if (m_modelObjects[index]->setValue(property.toUtf8(), value)) {
2116 emitItemsChanged(index, 1, roles);
2119 int roleIndex = m_listModel->setOrCreateProperty(index, property, value);
2120 if (roleIndex != -1) {
2125 emitItemsChanged(index, 1, roles);
2131 \qmlmethod QtQuick2::ListModel::sync()
2133 Writes any unsaved changes to the list model after it has been modified
2134 from a worker script.
2136 void QDeclarativeListModel::sync()
2138 // This is just a dummy method to make it look like sync() exists in
2139 // ListModel (and not just QDeclarativeListModelWorkerAgent) and to let
2140 // us document sync().
2141 qmlInfo(this) << "List sync() can only be called from a WorkerScript";
2144 bool QDeclarativeListModelParser::compileProperty(const QDeclarativeCustomParserProperty &prop, QList<ListInstruction> &instr, QByteArray &data)
2146 QList<QVariant> values = prop.assignedValues();
2147 for(int ii = 0; ii < values.count(); ++ii) {
2148 const QVariant &value = values.at(ii);
2150 if(value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) {
2151 QDeclarativeCustomParserNode node =
2152 qvariant_cast<QDeclarativeCustomParserNode>(value);
2154 if (node.name() != listElementTypeName) {
2155 const QMetaObject *mo = resolveType(node.name());
2156 if (mo != &QDeclarativeListElement::staticMetaObject) {
2157 error(node, QDeclarativeListModel::tr("ListElement: cannot contain nested elements"));
2160 listElementTypeName = node.name(); // cache right name for next time
2165 li.type = ListInstruction::Push;
2170 QList<QDeclarativeCustomParserProperty> props = node.properties();
2171 for(int jj = 0; jj < props.count(); ++jj) {
2172 const QDeclarativeCustomParserProperty &nodeProp = props.at(jj);
2173 if (nodeProp.name().isEmpty()) {
2174 error(nodeProp, QDeclarativeListModel::tr("ListElement: cannot contain nested elements"));
2177 if (nodeProp.name() == QStringLiteral("id")) {
2178 error(nodeProp, QDeclarativeListModel::tr("ListElement: cannot use reserved \"id\" property"));
2183 int ref = data.count();
2184 data.append(nodeProp.name().toUtf8());
2186 li.type = ListInstruction::Set;
2190 if(!compileProperty(nodeProp, instr, data))
2193 li.type = ListInstruction::Pop;
2200 li.type = ListInstruction::Pop;
2207 QDeclarativeScript::Variant variant =
2208 qvariant_cast<QDeclarativeScript::Variant>(value);
2210 int ref = data.count();
2213 d += char(variant.type()); // type tag
2214 if (variant.isString()) {
2215 d += variant.asString().toUtf8();
2216 } else if (variant.isNumber()) {
2217 d += QByteArray::number(variant.asNumber(),'g',20);
2218 } else if (variant.isBoolean()) {
2219 d += char(variant.asBoolean());
2220 } else if (variant.isScript()) {
2221 if (definesEmptyList(variant.asScript())) {
2222 d[0] = char(QDeclarativeScript::Variant::Invalid); // marks empty list
2224 QByteArray script = variant.asScript().toUtf8();
2225 int v = evaluateEnum(script);
2227 using namespace QDeclarativeJS;
2228 AST::Node *node = variant.asAST();
2229 AST::StringLiteral *literal = 0;
2230 if (AST::CallExpression *callExpr = AST::cast<AST::CallExpression *>(node)) {
2231 if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(callExpr->base)) {
2232 if (idExpr->name == QLatin1String("QT_TR_NOOP") || idExpr->name == QLatin1String("QT_TRID_NOOP")) {
2233 if (callExpr->arguments && !callExpr->arguments->next)
2234 literal = AST::cast<AST::StringLiteral *>(callExpr->arguments->expression);
2236 error(prop, QDeclarativeListModel::tr("ListElement: improperly specified %1").arg(idExpr->name.toString()));
2239 } else if (idExpr->name == QLatin1String("QT_TRANSLATE_NOOP")) {
2240 if (callExpr->arguments && callExpr->arguments->next && !callExpr->arguments->next->next)
2241 literal = AST::cast<AST::StringLiteral *>(callExpr->arguments->next->expression);
2243 error(prop, QDeclarativeListModel::tr("ListElement: improperly specified QT_TRANSLATE_NOOP"));
2251 d[0] = char(QDeclarativeScript::Variant::String);
2252 d += literal->value.toUtf8();
2254 error(prop, QDeclarativeListModel::tr("ListElement: cannot use script for property value"));
2258 d[0] = char(QDeclarativeScript::Variant::Number);
2259 d += QByteArray::number(v);
2267 li.type = ListInstruction::Value;
2276 QByteArray QDeclarativeListModelParser::compile(const QList<QDeclarativeCustomParserProperty> &customProps)
2278 QList<ListInstruction> instr;
2280 listElementTypeName = QString(); // unknown
2282 for(int ii = 0; ii < customProps.count(); ++ii) {
2283 const QDeclarativeCustomParserProperty &prop = customProps.at(ii);
2284 if(!prop.name().isEmpty()) { // isn't default property
2285 error(prop, QDeclarativeListModel::tr("ListModel: undefined property '%1'").arg(prop.name()));
2286 return QByteArray();
2289 if(!compileProperty(prop, instr, data)) {
2290 return QByteArray();
2294 int size = sizeof(ListModelData) +
2295 instr.count() * sizeof(ListInstruction) +
2301 ListModelData *lmd = (ListModelData *)rv.data();
2302 lmd->dataOffset = sizeof(ListModelData) +
2303 instr.count() * sizeof(ListInstruction);
2304 lmd->instrCount = instr.count();
2305 for (int ii = 0; ii < instr.count(); ++ii)
2306 lmd->instructions()[ii] = instr.at(ii);
2307 ::memcpy(rv.data() + lmd->dataOffset, data.constData(), data.count());
2312 void QDeclarativeListModelParser::setCustomData(QObject *obj, const QByteArray &d)
2314 QDeclarativeListModel *rv = static_cast<QDeclarativeListModel *>(obj);
2316 QV8Engine *engine = QDeclarativeEnginePrivate::getV8Engine(qmlEngine(rv));
2317 rv->m_engine = engine;
2319 const ListModelData *lmd = (const ListModelData *)d.constData();
2320 const char *data = ((const char *)lmd) + lmd->dataOffset;
2322 QStack<DataStackElement> stack;
2324 for (int ii = 0; ii < lmd->instrCount; ++ii) {
2325 const ListInstruction &instr = lmd->instructions()[ii];
2327 switch(instr.type) {
2328 case ListInstruction::Push:
2330 Q_ASSERT(!rv->m_dynamicRoles);
2332 ListModel *subModel = 0;
2334 if (stack.count() == 0) {
2335 subModel = rv->m_listModel;
2337 const DataStackElement &e0 = stack.at(stack.size() - 1);
2338 DataStackElement &e1 = stack[stack.size() - 2];
2340 const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name);
2341 if (role.type == ListLayout::Role::List) {
2342 subModel = e1.model->getListProperty(e1.elementIndex, role);
2344 if (subModel == 0) {
2345 subModel = new ListModel(role.subLayout, 0, -1);
2346 QVariant vModel = QVariant::fromValue(subModel);
2347 e1.model->setOrCreateProperty(e1.elementIndex, e0.name, vModel);
2354 e.elementIndex = subModel ? subModel->appendElement() : -1;
2359 case ListInstruction::Pop:
2363 case ListInstruction::Value:
2365 const DataStackElement &e0 = stack.at(stack.size() - 1);
2366 DataStackElement &e1 = stack[stack.size() - 2];
2368 QString name = e0.name;
2371 switch (QDeclarativeScript::Variant::Type(data[instr.dataIdx])) {
2372 case QDeclarativeScript::Variant::Invalid:
2374 const ListLayout::Role &role = e1.model->getOrCreateListRole(e0.name);
2375 ListModel *emptyModel = new ListModel(role.subLayout, 0, -1);
2376 value = QVariant::fromValue(emptyModel);
2379 case QDeclarativeScript::Variant::Boolean:
2380 value = bool(data[1 + instr.dataIdx]);
2382 case QDeclarativeScript::Variant::Number:
2383 value = QByteArray(data + 1 + instr.dataIdx).toDouble();
2385 case QDeclarativeScript::Variant::String:
2386 value = QString::fromUtf8(data + 1 + instr.dataIdx);
2389 Q_ASSERT("Format error in ListInstruction");
2392 e1.model->setOrCreateProperty(e1.elementIndex, name, value);
2396 case ListInstruction::Set:
2399 e.name = QString::fromUtf8(data + instr.dataIdx);
2407 bool QDeclarativeListModelParser::definesEmptyList(const QString &s)
2409 if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) {
2410 for (int i=1; i<s.length()-1; i++) {
2411 if (!s[i].isSpace())
2421 \qmlclass ListElement QDeclarativeListElement
2422 \inqmlmodule QtQuick 2
2423 \ingroup qml-working-with-data
2424 \brief The ListElement element defines a data item in a ListModel.
2426 List elements are defined inside ListModel definitions, and represent items in a
2427 list that will be displayed using ListView or \l Repeater items.
2429 List elements are defined like other QML elements except that they contain
2430 a collection of \e role definitions instead of properties. Using the same
2431 syntax as property definitions, roles both define how the data is accessed
2432 and include the data itself.
2434 The names used for roles must begin with a lower-case letter and should be
2435 common to all elements in a given model. Values must be simple constants; either
2436 strings (quoted and optionally within a call to QT_TR_NOOP), boolean values
2437 (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter).
2439 \section1 Referencing Roles
2441 The role names are used by delegates to obtain data from list elements.
2442 Each role name is accessible in the delegate's scope, and refers to the
2443 corresponding role in the current element. Where a role name would be
2444 ambiguous to use, it can be accessed via the \l{ListView::}{model}
2445 property (e.g., \c{model.cost} instead of \c{cost}).
2447 \section1 Example Usage
2449 The following model defines a series of list elements, each of which
2450 contain "name" and "cost" roles and their associated values.
2452 \snippet doc/src/snippets/declarative/qml-data-models/listelements.qml model
2454 The delegate obtains the name and cost for each element by simply referring
2455 to \c name and \c cost:
2457 \snippet doc/src/snippets/declarative/qml-data-models/listelements.qml view