8#include <private/qjsvalue_p.h>
10#include <private/qqmlcustomparser_p.h>
11#include <private/qqmlengine_p.h>
12#include <private/qqmljsast_p.h>
13#include <private/qqmljsengine_p.h>
14#include <private/qqmllistwrapper_p.h>
15#include <private/qqmlnotifier_p.h>
16#include <private/qqmlopenmetaobject_p.h>
18#include <private/qv4alloca_p.h>
19#include <private/qv4dateobject_p.h>
20#include <private/qv4lookup_p.h>
21#include <private/qv4object_p.h>
22#include <private/qv4objectiterator_p.h>
23#include <private/qv4qmlcontext_p.h>
24#include <private/qv4sequenceobject_p.h>
25#include <private/qv4urlobject_p.h>
27#include <qqmlcontext.h>
30#include <QtCore/qdebug.h>
31#include <QtCore/qstack.h>
32#include <QXmlStreamReader>
33#include <QtCore/qdatetime.h>
34#include <QScopedValueRollback>
41enum { MIN_LISTMODEL_UID = 1024 };
48 for (size_t i=0 ; i <
sizeof(T) ; ++i) {
58 static const QString roleTypeNames[] = {
59 QStringLiteral(
"String"), QStringLiteral(
"Number"), QStringLiteral(
"Bool"),
60 QStringLiteral(
"List"), QStringLiteral(
"QObject"), QStringLiteral(
"VariantMap"),
61 QStringLiteral(
"DateTime"), QStringLiteral(
"Url"), QStringLiteral(
"Function")
65 return roleTypeNames[t];
72 QStringHash<Role *>::Node *node = roleHash.findNode(key);
74 const Role &r = *node->value;
76 qmlWarning(
nullptr) << QStringLiteral(
"Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type));
80 return createRole(key, type);
85 QStringHash<Role *>::Node *node = roleHash.findNode(key);
87 const Role &r = *node->value;
89 qmlWarning(
nullptr) << QStringLiteral(
"Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type));
93 QString qkey = key->toQString();
95 return createRole(qkey, type);
100 const int dataSizes[] = {
101 sizeof(StringOrTranslation),
105 sizeof(QV4::PersistentValue),
111 const int dataAlignments[] = {
112 alignof(StringOrTranslation),
115 alignof(ListModel *),
116 alignof(QV4::PersistentValue),
117 alignof(QVariantMap),
133 int dataSize = dataSizes[type];
134 int dataAlignment = dataAlignments[type];
136 int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1);
140 currentBlockOffset = dataSize;
144 currentBlockOffset = dataOffset + dataSize;
147 int roleIndex = roles.size();
151 roleHash.insert(key, r);
158 const int otherRolesCount = other->roles.size();
159 roles.reserve(otherRolesCount);
160 for (
int i=0 ; i < otherRolesCount; ++i) {
161 Role *role =
new Role(other->roles[i]);
163 roleHash.insert(role->name, role);
165 currentBlockOffset = other->currentBlockOffset;
166 currentBlock = other->currentBlock;
176 int roleOffset = target->roles.size();
177 int newRoleCount = src->roles.size() - roleOffset;
179 for (
int i=0 ; i < newRoleCount ; ++i) {
180 Role *role =
new Role(src->roles[roleOffset + i]);
181 target->roles.append(role);
182 target->roleHash.insert(role->name, role);
185 target->currentBlockOffset = src->currentBlockOffset;
186 target->currentBlock = src->currentBlock;
211 switch (data.userType()) {
214 case QMetaType::Bool: type =
Role::Bool;
break;
218 case QMetaType::QUrl: type =
Role::Url;
break;
220 if (data.userType() == qMetaTypeId<QJSValue>() &&
221 data.value<QJSValue>().isCallable()) {
224 }
else if (data.userType() == qMetaTypeId<
const QV4::CompiledData::Binding*>()
225 && data.value<
const QV4::CompiledData::Binding*>()->isTranslationBinding()) {
228 }
else if (data.userType() >= QMetaType::User) {
239 qmlWarning(
nullptr) <<
"Can't create role for unsupported data type";
243 return &getRoleOrCreate(key, type);
249 QStringHash<Role *>::Node *node = roleHash.findNode(key);
258 QStringHash<Role *>::Node *node = roleHash.findNode(key);
274 QString mutableString(s);
275 QString::DataPointer dataPointer = mutableString.data_ptr();
276 arrayData = dataPointer->d_ptr();
277 stringData = dataPointer->data();
278 stringSize = mutableString.size();
286 this->binding = binding;
294 return QString(QStringPrivate(arrayData, stringData, stringSize));
298 return owner->m_compilationUnit->bindingValueAsString(binding);
306 return QString(QStringPrivate(arrayData, stringData, stringSize));
311 if (arrayData && !arrayData->deref())
312 QTypedArrayData<ushort>::deallocate(arrayData);
314 stringData =
nullptr;
322 if (e->m_objectCache ==
nullptr) {
323 void *memory = operator
new(
sizeof(QObject) +
sizeof(QQmlData));
324 void *ddataMemory = ((
char *)memory) +
sizeof(
QObject);
325 e->m_objectCache =
new (memory) QObject;
327 const QAbstractDeclarativeData *old = std::exchange(
328 QObjectPrivate::get(e->m_objectCache)->declarativeData,
329 new (ddataMemory) QQmlData(QQmlData::DoesNotOwnMemory));
334 return e->m_objectCache;
341 bool hasChanges =
false;
344 QHash<
int, ElementSync> elementHash;
345 for (
int i = 0; i < target->elements.count(); ++i) {
347 int uid = e->getUid();
350 sync.targetIndex = i;
351 elementHash.insert(uid, sync);
353 for (
int i = 0; i < src->elements.count(); ++i) {
355 int uid = e->getUid();
357 QHash<
int, ElementSync>::iterator it = elementHash.find(uid);
358 if (it == elementHash.end()) {
362 elementHash.insert(uid, sync);
364 ElementSync &sync = it.value();
370 QQmlListModel *targetModel = target->m_modelCache;
374 for (
int i = 0 ; i < target->elements.count() ; ++i) {
376 ElementSync &s = elementHash.find(element->getUid()).value();
377 Q_ASSERT(s.targetIndex >= 0);
379 s.targetIndex -= rowsRemoved;
380 if (s.src ==
nullptr) {
381 Q_ASSERT(s.targetIndex == i);
384 targetModel->beginRemoveRows(QModelIndex(), i, i);
385 s.target->destroy(target->m_layout);
386 target->elements.removeOne(s.target);
389 targetModel->endRemoveRows();
400 target->elements.clear();
401 for (
int i = 0; i < src->elements.count(); ++i) {
403 ElementSync &s = elementHash.find(srcElement->getUid()).value();
404 Q_ASSERT(s.srcIndex >= 0);
406 if (targetElement ==
nullptr) {
409 s.changedRoles = ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout);
410 target->elements.append(targetElement);
413 target->updateCacheIndices();
416 for (
int i=0 ; i < target->elements.count() ; ++i) {
427 int rowsInserted = 0;
428 const int targetElementCount = target->elements.count();
429 for (
int i = 0 ; i < targetElementCount ; ++i) {
431 ElementSync &s = elementHash.find(element->getUid()).value();
432 Q_ASSERT(s.srcIndex >= 0);
433 s.srcIndex += rowsInserted;
434 if (s.srcIndex != s.targetIndex) {
436 if (s.targetIndex == -1) {
437 targetModel->beginInsertRows(QModelIndex(), i, i);
438 targetModel->endInsertRows();
441 bool validMove = targetModel->beginMoveRows(QModelIndex(), s.targetIndex, s.targetIndex, QModelIndex(), i);
443 targetModel->endMoveRows();
445 for (
int j=i+1; j < targetElementCount; ++j) {
447 ElementSync &sToFix = elementHash.find(eToFix->getUid()).value();
448 if (i < s.targetIndex) {
450 if (sToFix.targetIndex > s.targetIndex || sToFix.targetIndex < i)
453 sToFix.targetIndex += 1;
456 if (sToFix.targetIndex < s.targetIndex || sToFix.targetIndex > i)
459 sToFix.targetIndex -= 1;
466 if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) {
467 QModelIndex idx = targetModel->createIndex(i, 0);
469 targetModel->dataChanged(idx, idx, s.changedRoles);
482 for (
const auto &destroyer : remove(0, elements.count()))
486 if (m_modelCache && m_modelCache->m_primary ==
false)
488 m_modelCache =
nullptr;
493 int elementIndex = elements.count();
494 newElement(elementIndex);
501 updateCacheIndices(index);
515 QPODVector<ListElement *, 4> store;
516 for (
int i=0 ; i < (to-from) ; ++i)
517 store.append(elements[from+n+i]);
518 for (
int i=0 ; i < n ; ++i)
519 store.append(elements[from+i]);
520 for (
int i=0 ; i < store.count() ; ++i)
521 elements[from+i] = store[i];
523 updateCacheIndices(from, to + n);
529 elements.insert(index, e);
532void ListModel::updateCacheIndices(
int start,
int end)
534 int count = elements.count();
536 if (end < 0 || end > count)
539 for (
int i = start; i < end; ++i) {
552 return e->getProperty(r, owner, eng);
558 return e->getListProperty(role);
563 for (
int index = 0; index != elements.count(); ++index) {
572void ListModel::
set(
int elementIndex, QV4::Object *object, QList<
int> *roles)
576 QV4::ExecutionEngine *v4 = object->engine();
577 QV4::Scope scope(v4);
578 QV4::ScopedObject o(scope);
580 QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
581 QV4::ScopedString propertyName(scope);
582 QV4::ScopedValue propertyValue(scope);
584 propertyName = it.nextPropertyNameAsString(propertyValue);
592 if (
const QV4::String *s = propertyValue->as<QV4::String>()) {
594 roleIndex = e->setStringProperty(r, s->toQString());
595 }
else if (propertyValue->isNumber()) {
597 roleIndex = e->setDoubleProperty(r, propertyValue->asDouble());
598 }
else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) {
599 roleIndex = setArrayLike(&o, propertyName, e, a);
600 }
else if (QV4::Sequence *s = propertyValue->as<QV4::Sequence>()) {
601 roleIndex = setArrayLike(&o, propertyName, e, s);
602 }
else if (QV4::QmlListWrapper *l = propertyValue->as<QV4::QmlListWrapper>()) {
603 roleIndex = setArrayLike(&o, propertyName, e, l);
604 }
else if (propertyValue->isBoolean()) {
606 roleIndex = e->setBoolProperty(r, propertyValue->booleanValue());
607 }
else if (QV4::DateObject *dd = propertyValue->as<QV4::DateObject>()) {
609 QDateTime dt = dd->toQDateTime();
610 roleIndex = e->setDateTimeProperty(r, dt);
611 }
else if (QV4::UrlObject *url = propertyValue->as<QV4::UrlObject>()) {
613 QUrl qurl = QUrl(url->href());
614 roleIndex = e->setUrlProperty(r, qurl);
615 }
else if (QV4::FunctionObject *f = propertyValue->as<QV4::FunctionObject>()) {
617 QV4::ScopedFunctionObject func(scope, f);
619 QJSValuePrivate::setValue(&jsv, func);
620 roleIndex = e->setFunctionProperty(r, jsv);
621 }
else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
622 if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) {
625 roleIndex = e->setQObjectProperty(role, wrapper);
626 }
else if (QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
628 QV4::Value::fromReturnedValue(o->asReturnedValue()),
629 QMetaType::fromType<QUrl>(),
true);
630 maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
632 QUrl qurl = maybeUrl.toUrl();
633 roleIndex = e->setUrlProperty(r, qurl);
637 QV4::ScopedObject obj(scope, o);
638 roleIndex = e->setVariantMapProperty(role, obj);
641 }
else if (propertyValue->isNullOrUndefined()) {
644 e->clearProperty(*r);
648 roles->append(roleIndex);
662 QV4::ExecutionEngine *v4 = object->engine();
663 QV4::Scope scope(v4);
665 QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
666 QV4::ScopedString propertyName(scope);
667 QV4::ScopedValue propertyValue(scope);
668 QV4::ScopedObject o(scope);
670 propertyName = it.nextPropertyNameAsString(propertyValue);
675 if (QV4::String *s = propertyValue->stringValue()) {
678 e->setStringPropertyFast(r, s->toQString());
679 }
else if (propertyValue->isNumber()) {
682 e->setDoublePropertyFast(r, propertyValue->asDouble());
684 }
else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) {
685 setArrayLikeFast(&o, propertyName, e, a);
686 }
else if (QV4::Sequence *s = propertyValue->as<QV4::Sequence>()) {
687 setArrayLikeFast(&o, propertyName, e, s);
688 }
else if (QV4::QmlListWrapper *l = propertyValue->as<QV4::QmlListWrapper>()) {
689 setArrayLikeFast(&o, propertyName, e, l);
690 }
else if (propertyValue->isBoolean()) {
693 e->setBoolPropertyFast(r, propertyValue->booleanValue());
695 }
else if (QV4::DateObject *date = propertyValue->as<QV4::DateObject>()) {
698 QDateTime dt = date->toQDateTime();
699 e->setDateTimePropertyFast(r, dt);
701 }
else if (QV4::UrlObject *url = propertyValue->as<QV4::UrlObject>()){
704 QUrl qurl = QUrl(url->href());
705 e->setUrlPropertyFast(r, qurl);
707 }
else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
708 if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) {
711 e->setQObjectPropertyFast(r, wrapper);
713 QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
715 QV4::Value::fromReturnedValue(o->asReturnedValue()),
716 QMetaType::fromType<QUrl>(),
true);
717 if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
718 const QUrl qurl = maybeUrl.toUrl();
721 e->setUrlPropertyFast(r, qurl);
725 e->setVariantMapFast(role, o);
728 }
else if (propertyValue->isNullOrUndefined()) {
731 auto memberName = propertyName->toString(v4)->toQString();
732 err.setDescription(QString::fromLatin1(
"%1 is %2. Adding an object with a %2 member does not create a role for it.").arg(memberName, propertyValue->isNull() ? QLatin1String(
"null") : QLatin1String(
"undefined")));
733 qmlWarning(
nullptr, err);
737 e->clearProperty(*r);
743QList<std::function<
void()>> ListModel::remove(
int index,
int count)
745 QList<std::function<
void()>> toDestroy;
746 auto layout = m_layout;
747 for (
int i=0 ; i < count ; ++i) {
748 auto element = elements[index+i];
749 toDestroy.append([element, layout](){
750 element->destroy(layout);
754 elements.remove(index, count);
755 updateCacheIndices(index);
762 set(elementIndex, object, SetElement::WasJustInserted);
768 set(elementIndex, object, SetElement::WasJustInserted);
772int ListModel::setOrCreateProperty(
int elementIndex,
const QString &key,
const QVariant &data)
776 if (elementIndex >= 0 && elementIndex < elements.count()) {
781 roleIndex = e->setVariantProperty(*r, data);
785 if (roleIndex != -1 && cache)
793int ListModel::setExistingProperty(
int elementIndex,
const QString &key,
const QV4::Value &data, QV4::ExecutionEngine *eng)
797 if (elementIndex >= 0 && elementIndex < elements.count()) {
801 roleIndex = e->setJsProperty(*r, data, eng);
812 if (e->next ==
nullptr) {
828 return ModelNodeMetaObject::get(m_objectCache);
833 char *mem = getPropertyMemory(role);
840 char *mem = getPropertyMemory(role);
841 QV4::PersistentValue *g =
reinterpret_cast<QV4::PersistentValue *>(mem);
842 return g->as<QV4::QObjectWrapper>();
847 QVariantMap *map =
nullptr;
849 char *mem = getPropertyMemory(role);
850 if (isMemoryUsed<QVariantMap>(mem))
851 map =
reinterpret_cast<QVariantMap *>(mem);
858 QDateTime *dt =
nullptr;
860 char *mem = getPropertyMemory(role);
861 if (isMemoryUsed<QDateTime>(mem))
862 dt =
reinterpret_cast<QDateTime *>(mem);
871 char *mem = getPropertyMemory(role);
872 if (isMemoryUsed<QUrl>(mem))
873 url =
reinterpret_cast<QUrl *>(mem);
880 QJSValue *f =
nullptr;
882 char *mem = getPropertyMemory(role);
883 if (isMemoryUsed<QJSValue>(mem))
884 f =
reinterpret_cast<QJSValue *>(mem);
889QV4::PersistentValue *
892 char *mem = getPropertyMemory(role);
894 bool existingGuard =
false;
895 for (size_t i = 0; i <
sizeof(QV4::PersistentValue);
898 existingGuard =
true;
903 QV4::PersistentValue *g =
nullptr;
906 g =
reinterpret_cast<QV4::PersistentValue *>(mem);
913 char *mem = getPropertyMemory(role);
920 char *mem = getPropertyMemory(role);
927 double *value =
reinterpret_cast<
double *>(mem);
935 data = value->toString(owner);
942 bool *value =
reinterpret_cast<
bool *>(mem);
952 if (model->m_modelCache ==
nullptr) {
953 model->m_modelCache =
new QQmlListModel(owner, model, eng);
954 QQmlEngine::setContextForObject(model->m_modelCache, QQmlEngine::contextForObject(owner));
957 QObject *object = model->m_modelCache;
958 data = QVariant::fromValue(object);
964 QV4::PersistentValue *guard =
reinterpret_cast<QV4::PersistentValue *>(mem);
965 data = QVariant::fromValue(guard->as<QV4::QObjectWrapper>()->object());
970 if (isMemoryUsed<QVariantMap>(mem)) {
971 QVariantMap *map =
reinterpret_cast<QVariantMap *>(mem);
978 if (isMemoryUsed<QDateTime>(mem)) {
979 QDateTime *dt =
reinterpret_cast<QDateTime *>(mem);
986 if (isMemoryUsed<QUrl>(mem)) {
987 QUrl *url =
reinterpret_cast<QUrl *>(mem);
994 if (isMemoryUsed<QJSValue>(mem)) {
995 QJSValue *func =
reinterpret_cast<QJSValue *>(mem);
996 data = QVariant::fromValue(*func);
1012 char *mem = getPropertyMemory(role);
1018 changed = c->asString().compare(s) != 0;
1032 char *mem = getPropertyMemory(role);
1033 double *value =
reinterpret_cast<
double *>(mem);
1034 bool changed = *value != d;
1048 char *mem = getPropertyMemory(role);
1049 bool *value =
reinterpret_cast<
bool *>(mem);
1050 bool changed = *value != b;
1064 char *mem = getPropertyMemory(role);
1066 if (*value && *value != m) {
1082 char *mem = getPropertyMemory(role);
1083 if (isMemoryUsed<QVariantMap>(mem))
1084 reinterpret_cast<QV4::PersistentValue *>(mem)->set(o->engine(), *o);
1086 new (mem) QV4::PersistentValue(o->engine(), o);
1098 char *mem = getPropertyMemory(role);
1099 if (isMemoryUsed<QVariantMap>(mem)) {
1100 QVariantMap *map =
reinterpret_cast<QVariantMap *>(mem);
1103 new (mem) QVariantMap(o->engine()->variantMapFromJS(o));
1115 char *mem = getPropertyMemory(role);
1116 if (isMemoryUsed<QVariantMap>(mem)) {
1117 QVariantMap *map =
reinterpret_cast<QVariantMap *>(mem);
1118 if (m && map->isSharedWith(*m))
1125 new (mem) QVariantMap(*m);
1127 new (mem) QVariantMap;
1139 char *mem = getPropertyMemory(role);
1140 if (isMemoryUsed<QDateTime>(mem)) {
1141 QDateTime *dt =
reinterpret_cast<QDateTime *>(mem);
1144 new (mem) QDateTime(dt);
1156 char *mem = getPropertyMemory(role);
1157 if (isMemoryUsed<QUrl>(mem)) {
1158 QUrl *qurl =
reinterpret_cast<QUrl *>(mem);
1161 new (mem) QUrl(url);
1173 char *mem = getPropertyMemory(role);
1174 if (isMemoryUsed<QJSValue>(mem)) {
1175 QJSValue *f =
reinterpret_cast<QJSValue *>(mem);
1178 new (mem) QJSValue(f);
1190 char *mem = getPropertyMemory(role);
1192 s->setTranslation(b);
1202 char *mem = getPropertyMemory(role);
1208 char *mem = getPropertyMemory(role);
1209 double *value =
new (mem)
double;
1215 char *mem = getPropertyMemory(role);
1216 bool *value =
new (mem)
bool;
1222 char *mem = getPropertyMemory(role);
1223 new (mem) QV4::PersistentValue(o->engine(), o);
1228 char *mem = getPropertyMemory(role);
1229 ListModel **value =
new (mem) ListModel *;
1235 char *mem = getPropertyMemory(role);
1236 QVariantMap *map =
new (mem) QVariantMap;
1237 *map = o->engine()->variantMapFromJS(o);
1242 char *mem = getPropertyMemory(role);
1243 new (mem) QDateTime(dt);
1248 char *mem = getPropertyMemory(role);
1249 new (mem) QUrl(url);
1254 char *mem = getPropertyMemory(role);
1255 new (mem) QJSValue(f);
1260 switch (role
.type) {
1261 case ListLayout::Role::String:
1262 setStringProperty(role, QString());
1265 setDoubleProperty(role, 0.0);
1268 setBoolProperty(role,
false);
1271 setListProperty(role,
nullptr);
1273 case ListLayout::Role::QObject:
1274 setQObjectProperty(role,
nullptr);
1276 case ListLayout::Role::DateTime:
1277 setDateTimeProperty(role, QDateTime());
1279 case ListLayout::Role::Url:
1280 setUrlProperty(role, QUrl());
1282 case ListLayout::Role::VariantMap:
1283 setVariantMapProperty(role, (QVariantMap *)
nullptr);
1286 setFunctionProperty(role, QJSValue());
1295 m_objectCache =
nullptr;
1296 uid = uidCounter.fetchAndAddOrdered(1);
1298 memset(data, 0,
sizeof(data));
1303 m_objectCache =
nullptr;
1306 memset(data, 0,
sizeof(data));
1316 QList<
int> changedRoles;
1322 switch (srcRole
.type) {
1325 ListModel *srcSubModel = src->getListProperty(srcRole);
1326 ListModel *targetSubModel = target->getListProperty(targetRole);
1329 if (targetSubModel ==
nullptr) {
1330 targetSubModel =
new ListModel(targetRole.subLayout,
nullptr);
1331 target->setListPropertyFast(targetRole, targetSubModel);
1334 roleIndex = targetRole
.index;
1340 QV4::QObjectWrapper *object = src->getQObjectProperty(srcRole);
1341 roleIndex = target->setQObjectProperty(targetRole, object);
1350 QVariant v = src->getProperty(srcRole,
nullptr,
nullptr);
1351 roleIndex = target->setVariantProperty(targetRole, v);
1356 QVariantMap *map = src->getVariantMapProperty(srcRole);
1357 roleIndex = target->setVariantMapProperty(targetRole, map);
1364 changedRoles << roleIndex;
1367 return changedRoles;
1395 if (QV4::PersistentValue *guard = getGuardProperty(r))
1396 guard->~PersistentValue();
1401 QVariantMap *map = getVariantMapProperty(r);
1408 QDateTime *dt = getDateTimeProperty(r);
1415 QUrl *url = getUrlProperty(r);
1422 QJSValue *f = getFunctionProperty(r);
1433 if (m_objectCache) {
1434 m_objectCache->~QObject();
1435 operator
delete(m_objectCache);
1440 next->destroy(
nullptr);
1448 switch (role
.type) {
1450 roleIndex = setDoubleProperty(role, d.toDouble());
1452 case ListLayout::Role::String:
1453 if (d.userType() == qMetaTypeId<
const QV4::CompiledData::Binding *>())
1454 roleIndex = setTranslationProperty(role, d.value<
const QV4::CompiledData::Binding*>());
1456 roleIndex = setStringProperty(role, d.toString());
1459 roleIndex = setBoolProperty(role, d.toBool());
1462 roleIndex = setListProperty(role, d.value<
ListModel *>());
1465 QVariantMap map = d.toMap();
1466 roleIndex = setVariantMapProperty(role, &map);
1469 case ListLayout::Role::DateTime:
1470 roleIndex = setDateTimeProperty(role, d.toDateTime());
1472 case ListLayout::Role::Url:
1473 roleIndex = setUrlProperty(role, d.toUrl());
1476 roleIndex = setFunctionProperty(role, d.value<QJSValue>());
1490 QV4::Scope scope(eng);
1494 QString qstr = d.toQString();
1495 roleIndex = setStringProperty(role, qstr);
1496 }
else if (d.isNumber()) {
1497 roleIndex = setDoubleProperty(role, d.asDouble());
1498 }
else if (d.as<QV4::ArrayObject>()) {
1499 QV4::ScopedArrayObject a(scope, d);
1501 QV4::Scope scope(a->engine());
1502 QV4::ScopedObject o(scope);
1504 ListModel *subModel =
new ListModel(role.subLayout,
nullptr);
1505 int arrayLength = a->getLength();
1506 for (
int j=0 ; j < arrayLength ; ++j) {
1508 subModel->append(o);
1510 roleIndex = setListProperty(role, subModel);
1512 qmlWarning(
nullptr) << QStringLiteral(
"Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List));
1514 }
else if (d.isBoolean()) {
1515 roleIndex = setBoolProperty(role, d.booleanValue());
1516 }
else if (d.as<QV4::DateObject>()) {
1517 QV4::Scoped<QV4::DateObject> dd(scope, d);
1518 QDateTime dt = dd->toQDateTime();
1519 roleIndex = setDateTimeProperty(role, dt);
1520 }
else if (d.as<QV4::UrlObject>()) {
1521 QV4::Scoped<QV4::UrlObject> url(scope, d);
1522 QUrl qurl = QUrl(url->href());
1523 roleIndex = setUrlProperty(role, qurl);
1524 }
else if (d.as<QV4::FunctionObject>()) {
1525 QV4::ScopedFunctionObject f(scope, d);
1527 QJSValuePrivate::setValue(&jsv, f);
1528 roleIndex = setFunctionProperty(role, jsv);
1529 }
else if (d.isObject()) {
1530 QV4::ScopedObject o(scope, d);
1531 QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>();
1533 roleIndex = setQObjectProperty(role, wrapper);
1535 roleIndex = setVariantMapProperty(role, o);
1537 QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
1539 QV4::Value::fromReturnedValue(o.asReturnedValue()),
1540 QMetaType::fromType<QUrl>(),
true);
1541 if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
1542 roleIndex = setUrlProperty(role, maybeUrl.toUrl());
1545 }
else if (d.isNullOrUndefined()) {
1546 clearProperty(role);
1558 const int roleCount = m_model->m_listModel->roleCount();
1559 QList<QByteArray> properties;
1560 properties.reserve(roleCount);
1561 for (
int i = 0 ; i < roleCount ; ++i) {
1562 const ListLayout::
Role &role = m_model->m_listModel->getExistingRole(i);
1563 QByteArray name = role.name.toUtf8();
1566 type()->createProperties(properties);
1575#if QT_VERSION >= QT_VERSION_CHECK(7
, 0
, 0
)
1576const QMetaObject *ModelNodeMetaObject::toDynamicMetaObject(QObject *object)
const
1581 if (!m_initialized) {
1582 m_initialized =
true;
1583 const_cast<ModelNodeMetaObject *>(
this)->initialize();
1585 return QQmlOpenMetaObject::toDynamicMetaObject(object);
1590 QObjectPrivate *op = QObjectPrivate::get(obj);
1591 return static_cast<ModelNodeMetaObject*>(op->metaObject);
1596 const int roleCount = m_model->m_listModel->roleCount();
1597 if (!m_initialized) {
1599 Q_ALLOCA_VAR(
int, changedRoles, roleCount *
sizeof(
int));
1600 for (
int i = 0; i < roleCount; ++i)
1601 changedRoles[i] = i;
1602 emitDirectNotifies(changedRoles, roleCount);
1606 for (
int i=0 ; i < roleCount ; ++i) {
1607 const ListLayout::
Role &role = m_model->m_listModel->getExistingRole(i);
1608 QByteArray name = role.name.toUtf8();
1609 const QVariant &data = m_model->data(m_elementIndex, i);
1616 if (!m_initialized) {
1617 emitDirectNotifies(roles.constData(), roles.size());
1620 int roleCount = roles.size();
1621 for (
int i=0 ; i < roleCount ; ++i) {
1622 int roleIndex = roles.at(i);
1623 const ListLayout::
Role &role = m_model->m_listModel->getExistingRole(roleIndex);
1624 QByteArray name = role.name.toUtf8();
1625 const QVariant &data = m_model->data(m_elementIndex, roleIndex);
1635 QString propName = QString::fromUtf8(name(index));
1636 const QVariant value =
this->value(index);
1638 QV4::Scope scope(m_model->engine());
1639 QV4::ScopedValue v(scope, scope.engine->fromVariant(value));
1641 int roleIndex = m_model->m_listModel->setExistingProperty(m_elementIndex, propName, v, scope.engine);
1642 if (roleIndex != -1)
1643 m_model->emitItemsChanged(m_elementIndex, 1, QList<
int>(1, roleIndex));
1649 Q_ASSERT(!m_initialized);
1650 QQmlData *ddata = QQmlData::get(object(),
false);
1654 if (!qmlEngine(m_model))
1656 for (
int i = 0; i < roleCount; ++i) {
1657 const int changedRole = changedRoles[i];
1658 QQmlNotifier::notify(ddata, changedRole);
1664bool ModelObject::virtualPut(Managed *m, PropertyKey id,
const Value &value, Value *receiver)
1667 return Object::virtualPut(m, id, value, receiver);
1668 QString propName = id.toQString();
1672 ExecutionEngine *eng = that->engine();
1673 const int elementIndex = that->d()->elementIndex();
1674 if (QQmlListModel *model = that->d()->m_model) {
1676 = model->listModel()->setExistingProperty(elementIndex, propName, value, eng);
1677 if (roleIndex != -1)
1678 model->emitItemsChanged(elementIndex, 1, QList<
int>(1, roleIndex));
1683 mo->emitPropertyNotification(propName.toUtf8());
1687ReturnedValue
ModelObject::virtualGet(
const Managed *m, PropertyKey id,
const Value *receiver,
bool *hasProperty)
1690 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1694 ScopedString name(scope, id.asStringOrSymbol());
1695 QQmlListModel *model = that->d()->m_model;
1697 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1699 const ListLayout::
Role *role = model->listModel()->getExistingRole(name);
1701 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1703 *hasProperty =
true;
1705 if (QQmlEngine *qmlEngine = that->engine()->qmlEngine()) {
1706 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine);
1707 if (ep && ep->propertyCapture)
1708 ep->propertyCapture->captureProperty(that->object(), -1, role
->index,
false);
1711 const int elementIndex = that->d()->elementIndex();
1712 QVariant value = model->data(elementIndex, role
->index);
1713 return that->engine()->fromVariant(value);
1716ReturnedValue
ModelObject::virtualResolveLookupGetter(
const Object *object, ExecutionEngine *engine, Lookup *lookup)
1718 lookup->call = Lookup::Call::GetterQObjectPropertyFallback;
1719 return lookup->getter(engine, *object);
1726 PropertyKey next(
const Object *o, Property *pd =
nullptr, PropertyAttributes *attrs =
nullptr)
override;
1734 ExecutionEngine *v4 = that->engine();
1736 QQmlListModel *model = that->d()->m_model;
1737 ListModel *listModel = model ? model->listModel() :
nullptr;
1739 Scope scope(that->engine());
1742 ScopedString roleName(scope, v4->newString(role.name));
1744 *attrs = QV4::Attr_Data;
1747 QVariant value = model->data(that->d()->elementIndex(), role
.index);
1748 if (
auto recursiveListModel = qvariant_cast<QQmlListModel*>(value)) {
1749 auto size = recursiveListModel->count();
1750 auto array = ScopedArrayObject{scope, v4->newArrayObject(size)};
1751 QV4::ScopedValue val(scope);
1752 for (
auto i = 0; i < size; i++) {
1753 val = QJSValuePrivate::convertToReturnedValue(
1754 v4, recursiveListModel->get(i));
1755 array->arrayPut(i, val);
1759 pd->value = v4->fromVariant(value);
1762 return roleName->toPropertyKey();
1768 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
1771OwnPropertyKeyIterator *
ModelObject::virtualOwnPropertyKeys(
const Object *m, Value *target)
1790 object->updateValues(obj, roles);
1796 QList<
int> changedRoles;
1797 for (
int i = 0; i < src->m_meta->count(); ++i) {
1798 const QByteArray &name = src->m_meta->name(i);
1799 QVariant value = src->m_meta->value(i);
1801 QQmlListModel *srcModel = qobject_cast<QQmlListModel *>(value.value<QObject *>());
1802 QQmlListModel *targetModel = qobject_cast<QQmlListModel *>(target->m_meta->value(i).value<QObject *>());
1804 bool modelHasChanges =
false;
1806 if (targetModel ==
nullptr)
1807 targetModel = QQmlListModel::createWithOwner(target->m_owner);
1809 modelHasChanges = QQmlListModel::sync(srcModel, targetModel);
1811 QObject *targetModelObject = targetModel;
1812 value = QVariant::fromValue(targetModelObject);
1813 }
else if (targetModel) {
1817 if (target->setValue(name, value) || modelHasChanges)
1818 changedRoles << target->m_owner->m_roles.indexOf(QString::fromUtf8(name));
1820 return changedRoles;
1825 for (
auto it = object.cbegin(), end = object.cend(); it != end; ++it) {
1826 const QString &key = it.key();
1828 int roleIndex = m_owner->m_roles.indexOf(key);
1829 if (roleIndex == -1) {
1830 roleIndex = m_owner->m_roles.size();
1831 m_owner->m_roles.append(key);
1834 QVariant value = it.value();
1838 if (value.userType() == qMetaTypeId<QJSValue>())
1839 value = value.value<QJSValue>().toVariant();
1841 if (value.userType() == QMetaType::QVariantList) {
1842 QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner);
1844 QVariantList subArray = value.toList();
1845 QVariantList::const_iterator subIt = subArray.cbegin();
1846 QVariantList::const_iterator subEnd = subArray.cend();
1847 while (subIt != subEnd) {
1848 const QVariantMap &subObject = subIt->toMap();
1849 subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1853 QObject *subModelObject = subModel;
1854 value = QVariant::fromValue(subModelObject);
1857 const QByteArray &keyUtf8 = key.toUtf8();
1859 QQmlListModel *existingModel = qobject_cast<QQmlListModel *>(m_meta->value(keyUtf8).value<QObject *>());
1860 delete existingModel;
1862 if (m_meta->setValue(keyUtf8, value))
1874 for (
int i=0 ; i < count() ; ++i) {
1875 QQmlListModel *subModel = qobject_cast<QQmlListModel *>(value(i).value<QObject *>());
1885 QVariant v = value(index);
1886 QQmlListModel *model = qobject_cast<QQmlListModel *>(v.value<QObject *>());
1895 QQmlListModel *parentModel = m_owner->m_owner;
1897 QVariant v = value(index);
1901 if (v.userType() == qMetaTypeId<QJSValue>())
1902 v= v.value<QJSValue>().toVariant();
1904 if (v.userType() == QMetaType::QVariantList) {
1905 QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel);
1907 QVariantList subArray = v.toList();
1908 QVariantList::const_iterator subIt = subArray.cbegin();
1909 QVariantList::const_iterator subEnd = subArray.cend();
1910 while (subIt != subEnd) {
1911 const QVariantMap &subObject = subIt->toMap();
1912 subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1916 QObject *subModelObject = subModel;
1917 v = QVariant::fromValue(subModelObject);
1922 int elementIndex = parentModel->m_modelObjects.indexOf(m_owner);
1923 if (elementIndex != -1) {
1924 int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData()));
1925 if (roleIndex != -1)
1926 parentModel->emitItemsChanged(elementIndex, 1, QList<
int>(1, roleIndex));
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2031QQmlListModel::QQmlListModel(QObject *parent)
2032: QAbstractListModel(parent)
2034 m_mainThread =
true;
2037 m_dynamicRoles =
false;
2039 m_layout =
new ListLayout;
2040 m_listModel =
new ListModel(m_layout,
this);
2045QQmlListModel::QQmlListModel(
const QQmlListModel *owner, ListModel *data, QV4::ExecutionEngine *engine, QObject *parent)
2046: QAbstractListModel(parent)
2048 m_mainThread = owner->m_mainThread;
2050 m_agent = owner->m_agent;
2052 Q_ASSERT(owner->m_dynamicRoles ==
false);
2053 m_dynamicRoles =
false;
2058 m_compilationUnit = owner->m_compilationUnit;
2061QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent)
2062: QAbstractListModel(agent)
2064 m_mainThread =
false;
2067 m_dynamicRoles = orig->m_dynamicRoles;
2069 if (ListLayout *layout = orig->m_layout)
2070 m_layout =
new ListLayout(layout);
2072 m_layout =
new ListLayout;
2074 m_listModel =
new ListModel(m_layout,
this);
2079 ListModel::sync(orig->m_listModel, m_listModel);
2082 m_compilationUnit = orig->m_compilationUnit;
2085QQmlListModel::~QQmlListModel()
2087 qDeleteAll(m_modelObjects);
2090 m_listModel->destroy();
2093 if (m_mainThread && m_agent)
2094 m_agent->modelDestroyed();
2097 if (m_mainThread && m_agent)
2100 m_listModel =
nullptr;
2106QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner)
2108 QQmlListModel *model =
new QQmlListModel;
2110 model->m_mainThread = newOwner->m_mainThread;
2111 model->m_engine = newOwner->m_engine;
2112 model->m_agent = newOwner->m_agent;
2113 model->m_dynamicRoles = newOwner->m_dynamicRoles;
2115 if (model->m_mainThread && model->m_agent)
2116 model->m_agent->addref();
2118 QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner));
2123QV4::ExecutionEngine *QQmlListModel::engine()
const
2125 if (m_engine ==
nullptr) {
2126 m_engine = qmlEngine(
this)->handle();
2132bool QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target)
2134 Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles);
2136 bool hasChanges =
false;
2138 target->m_roles = src->m_roles;
2141 QHash<
int, ElementSync> elementHash;
2142 for (
int i = 0 ; i < target->m_modelObjects.size(); ++i) {
2143 DynamicRoleModelNode *e = target->m_modelObjects.at(i);
2144 int uid = e->getUid();
2147 sync.targetIndex = i;
2148 elementHash.insert(uid, sync);
2150 for (
int i = 0 ; i < src->m_modelObjects.size(); ++i) {
2151 DynamicRoleModelNode *e = src->m_modelObjects.at(i);
2152 int uid = e->getUid();
2154 QHash<
int, ElementSync>::iterator it = elementHash.find(uid);
2155 if (it == elementHash.end()) {
2159 elementHash.insert(uid, sync);
2161 ElementSync &sync = it.value();
2168 int rowsRemoved = 0;
2169 for (
int i = 0 ; i < target->m_modelObjects.size() ; ++i) {
2170 DynamicRoleModelNode *element = target->m_modelObjects.at(i);
2171 ElementSync &s = elementHash.find(element->getUid()).value();
2172 Q_ASSERT(s.targetIndex >= 0);
2174 s.targetIndex -= rowsRemoved;
2175 if (s.src ==
nullptr) {
2176 Q_ASSERT(s.targetIndex == i);
2178 target->beginRemoveRows(QModelIndex(), i, i);
2179 target->m_modelObjects.remove(i, 1);
2180 target->endRemoveRows();
2189 target->m_modelObjects.clear();
2190 for (
int i = 0 ; i < src->m_modelObjects.size() ; ++i) {
2191 DynamicRoleModelNode *element = src->m_modelObjects.at(i);
2192 ElementSync &s = elementHash.find(element->getUid()).value();
2193 Q_ASSERT(s.srcIndex >= 0);
2194 DynamicRoleModelNode *targetElement = s.target;
2195 if (targetElement ==
nullptr) {
2196 targetElement =
new DynamicRoleModelNode(target, element->getUid());
2198 s.changedRoles = DynamicRoleModelNode::sync(element, targetElement);
2199 target->m_modelObjects.append(targetElement);
2207 int rowsInserted = 0;
2208 for (
int i = 0 ; i < target->m_modelObjects.size() ; ++i) {
2209 DynamicRoleModelNode *element = target->m_modelObjects.at(i);
2210 ElementSync &s = elementHash.find(element->getUid()).value();
2211 Q_ASSERT(s.srcIndex >= 0);
2212 s.srcIndex += rowsInserted;
2213 if (s.srcIndex != s.targetIndex) {
2214 if (s.targetIndex == -1) {
2215 target->beginInsertRows(QModelIndex(), i, i);
2216 target->endInsertRows();
2218 target->beginMoveRows(QModelIndex(), i, i, QModelIndex(), s.srcIndex);
2219 target->endMoveRows();
2224 if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) {
2225 QModelIndex idx = target->createIndex(i, 0);
2226 emit target->dataChanged(idx, idx, s.changedRoles);
2233void QQmlListModel::emitItemsChanged(
int index,
int count,
const QList<
int> &roles)
2239 emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);;
2242void QQmlListModel::emitItemsAboutToBeInserted(
int index,
int count)
2244 Q_ASSERT(index >= 0 && count >= 0);
2246 beginInsertRows(QModelIndex(), index, index + count - 1);
2249void QQmlListModel::emitItemsInserted()
2253 emit countChanged();
2257QQmlListModelWorkerAgent *QQmlListModel::agent()
2262 m_agent =
new QQmlListModelWorkerAgent(
this);
2266QModelIndex QQmlListModel::index(
int row,
int column,
const QModelIndex &parent)
const
2268 return row >= 0 && row < count() && column == 0 && !parent.isValid()
2269 ? createIndex(row, column)
2273int QQmlListModel::rowCount(
const QModelIndex &parent)
const
2275 return !parent.isValid() ? count() : 0;
2278QVariant QQmlListModel::data(
const QModelIndex &index,
int role)
const
2280 return data(index.row(), role);
2283bool QQmlListModel::setData(
const QModelIndex &index,
const QVariant &value,
int role)
2285 const int row = index.row();
2286 if (row >= count() || row < 0)
2289 if (m_dynamicRoles) {
2290 const QByteArray property = m_roles.at(role).toUtf8();
2291 if (m_modelObjects[row]->setValue(property, value)) {
2292 emitItemsChanged(row, 1, QList<
int>(1, role));
2296 const ListLayout::Role &r = m_listModel->getExistingRole(role);
2297 const int roleIndex = m_listModel->setOrCreateProperty(row, r.name, value);
2298 if (roleIndex != -1) {
2299 emitItemsChanged(row, 1, QList<
int>(1, role));
2307QVariant QQmlListModel::data(
int index,
int role)
const
2311 if (index >= count() || index < 0)
2315 v = m_modelObjects[index]->getValue(m_roles[role]);
2317 v = m_listModel->getProperty(index, role,
this, engine());
2322QHash<
int, QByteArray> QQmlListModel::roleNames()
const
2324 QHash<
int, QByteArray> roleNames;
2326 if (m_dynamicRoles) {
2327 const auto size = m_roles.size();
2328 roleNames.reserve(size);
2329 for (
int i = 0 ; i < size ; ++i)
2330 roleNames.insert(i, m_roles.at(i).toUtf8());
2332 const auto size = m_listModel->roleCount();
2333 roleNames.reserve(size);
2334 for (
int i = 0 ; i < size; ++i) {
2335 const ListLayout::Role &r = m_listModel->getExistingRole(i);
2336 roleNames.insert(i, r.name.toUtf8());
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369void QQmlListModel::setDynamicRoles(
bool enableDynamicRoles)
2371 if (m_mainThread && m_agent ==
nullptr) {
2372 if (enableDynamicRoles) {
2373 if (m_layout && m_layout->roleCount())
2374 qmlWarning(
this) << tr(
"unable to enable dynamic roles as this model is not empty");
2376 m_dynamicRoles =
true;
2378 if (m_roles.size()) {
2379 qmlWarning(
this) << tr(
"unable to enable static roles as this model is not empty");
2381 m_dynamicRoles =
false;
2385 qmlWarning(
this) << tr(
"dynamic role setting must be made from the main thread, before any worker scripts are created");
2390
2391
2392
2393int QQmlListModel::count()
const
2395 return m_dynamicRoles ? m_modelObjects.size() : m_listModel->elementCount();
2399
2400
2401
2402
2403
2404
2405
2406void QQmlListModel::clear()
2408 removeElements(0, count());
2412
2413
2414
2415
2416
2417
2418void QQmlListModel::remove(QQmlV4FunctionPtr args)
2420 int argLength = args->length();
2422 if (argLength == 1 || argLength == 2) {
2423 QV4::Scope scope(args->v4engine());
2424 int index = QV4::ScopedValue(scope, (*args)[0])->toInt32();
2425 int removeCount = (argLength == 2 ? QV4::ScopedValue(scope, (*args)[1])->toInt32() : 1);
2427 if (index < 0 || index+removeCount > count() || removeCount <= 0) {
2428 qmlWarning(
this) << tr(
"remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count());
2432 removeElements(index, removeCount);
2434 qmlWarning(
this) << tr(
"remove: incorrect number of arguments");
2438void QQmlListModel::removeElements(
int index,
int removeCount)
2440 Q_ASSERT(index >= 0 && removeCount >= 0);
2446 beginRemoveRows(QModelIndex(), index, index + removeCount - 1);
2448 QList<std::function<
void()>> toDestroy;
2449 if (m_dynamicRoles) {
2450 for (
int i=0 ; i < removeCount ; ++i) {
2451 auto modelObject = m_modelObjects[index+i];
2452 toDestroy.append([modelObject](){
2456 m_modelObjects.remove(index, removeCount);
2458 toDestroy = m_listModel->remove(index, removeCount);
2463 emit countChanged();
2465 for (
const auto &destroyer : std::as_const(toDestroy))
2469void QQmlListModel::updateTranslations()
2476 Q_ASSERT(m_listModel);
2479 for (
int i = 0, end = m_listModel->roleCount(); i != end; ++i) {
2480 if (m_listModel->getExistingRole(i).type == ListLayout::Role::String)
2484 if (!roles.isEmpty())
2485 emitItemsChanged(0, rowCount(QModelIndex()), roles);
2487 m_listModel->updateTranslations();
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2506void QQmlListModel::insert(QQmlV4FunctionPtr args)
2508 if (args->length() == 2) {
2509 QV4::Scope scope(args->v4engine());
2510 QV4::ScopedValue arg0(scope, (*args)[0]);
2511 int index = arg0->toInt32();
2513 if (index < 0 || index > count()) {
2514 qmlWarning(
this) << tr(
"insert: index %1 out of range").arg(index);
2518 QV4::ScopedObject argObject(scope, (*args)[1]);
2519 QV4::ScopedArrayObject objectArray(scope, (*args)[1]);
2521 QV4::ScopedObject argObject(scope);
2523 int objectArrayLength = objectArray->getLength();
2524 emitItemsAboutToBeInserted(index, objectArrayLength);
2525 for (
int i=0 ; i < objectArrayLength ; ++i) {
2526 argObject = objectArray->get(i);
2528 if (m_dynamicRoles) {
2529 m_modelObjects.insert(index+i, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2531 m_listModel->insert(index+i, argObject);
2534 emitItemsInserted();
2535 }
else if (argObject) {
2536 emitItemsAboutToBeInserted(index, 1);
2538 if (m_dynamicRoles) {
2539 m_modelObjects.insert(index, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2541 m_listModel->insert(index, argObject);
2544 emitItemsInserted();
2546 qmlWarning(
this) << tr(
"insert: value is not an object");
2549 qmlWarning(
this) << tr(
"insert: value is not an object");
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567void QQmlListModel::move(
int from,
int to,
int n)
2569 if (n == 0 || from == to)
2571 if (!canMove(from, to, n)) {
2572 qmlWarning(
this) << tr(
"move: out of range");
2577 beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to);
2579 if (m_dynamicRoles) {
2581 int realFrom = from;
2594 QPODVector<DynamicRoleModelNode *, 4> store;
2595 for (
int i=0 ; i < (realTo-realFrom) ; ++i)
2596 store.append(m_modelObjects[realFrom+realN+i]);
2597 for (
int i=0 ; i < realN ; ++i)
2598 store.append(m_modelObjects[realFrom+i]);
2599 for (
int i=0 ; i < store.count() ; ++i)
2600 m_modelObjects[realFrom+i] = store[i];
2603 m_listModel->move(from, to, n);
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622void QQmlListModel::append(QQmlV4FunctionPtr args)
2624 if (args->length() == 1) {
2625 QV4::Scope scope(args->v4engine());
2626 QV4::ScopedObject argObject(scope, (*args)[0]);
2627 QV4::ScopedArrayObject objectArray(scope, (*args)[0]);
2630 QV4::ScopedObject argObject(scope);
2632 int objectArrayLength = objectArray->getLength();
2633 if (objectArrayLength > 0) {
2634 int index = count();
2635 emitItemsAboutToBeInserted(index, objectArrayLength);
2637 for (
int i=0 ; i < objectArrayLength ; ++i) {
2638 argObject = objectArray->get(i);
2640 if (m_dynamicRoles) {
2641 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2643 m_listModel->append(argObject);
2647 emitItemsInserted();
2649 }
else if (argObject) {
2652 if (m_dynamicRoles) {
2653 index = m_modelObjects.size();
2654 emitItemsAboutToBeInserted(index, 1);
2655 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2657 index = m_listModel->elementCount();
2658 emitItemsAboutToBeInserted(index, 1);
2659 m_listModel->append(argObject);
2662 emitItemsInserted();
2664 qmlWarning(
this) << tr(
"append: value is not an object");
2667 qmlWarning(
this) << tr(
"append: value is not an object");
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703QJSValue QQmlListModel::get(
int index)
const
2705 QV4::Scope scope(engine());
2706 QV4::ScopedValue result(scope, QV4::Value::undefinedValue());
2708 if (index >= 0 && index < count()) {
2710 if (m_dynamicRoles) {
2711 DynamicRoleModelNode *object = m_modelObjects[index];
2712 result = QV4::QObjectWrapper::wrap(scope.engine, object);
2714 QObject *object = m_listModel->getOrCreateModelObject(
const_cast<QQmlListModel *>(
this), index);
2715 QQmlData *ddata = QQmlData::get(object);
2716 if (ddata->jsWrapper.isNullOrUndefined()) {
2717 result = scope.engine->memoryManager->allocate<QV4::ModelObject>(object,
const_cast<QQmlListModel *>(
this));
2719 ddata->jsWrapper.set(scope.engine, result);
2721 result = ddata->jsWrapper.value();
2726 return QJSValuePrivate::fromReturnedValue(result->asReturnedValue());
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745void QQmlListModel::set(
int index,
const QJSValue &value)
2747 QV4::Scope scope(engine());
2748 QV4::ScopedObject object(scope, QJSValuePrivate::asReturnedValue(&value));
2751 qmlWarning(
this) << tr(
"set: value is not an object");
2754 if (index > count() || index < 0) {
2755 qmlWarning(
this) << tr(
"set: index %1 out of range").arg(index);
2760 if (index == count()) {
2761 emitItemsAboutToBeInserted(index, 1);
2763 if (m_dynamicRoles) {
2764 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(object),
this));
2766 m_listModel->insert(index, object);
2769 emitItemsInserted();
2774 if (m_dynamicRoles) {
2775 m_modelObjects[index]->updateValues(scope.engine->variantMapFromJS(object), roles);
2777 m_listModel->set(index, object, &roles);
2781 emitItemsChanged(index, 1, roles);
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798void QQmlListModel::setProperty(
int index,
const QString& property,
const QVariant& value)
2800 if (count() == 0 || index >= count() || index < 0) {
2801 qmlWarning(
this) << tr(
"set: index %1 out of range").arg(index);
2805 if (m_dynamicRoles) {
2806 int roleIndex = m_roles.indexOf(property);
2807 if (roleIndex == -1) {
2808 roleIndex = m_roles.size();
2809 m_roles.append(property);
2811 if (m_modelObjects[index]->setValue(property.toUtf8(), value))
2812 emitItemsChanged(index, 1, QList<
int>(1, roleIndex));
2814 int roleIndex = m_listModel->setOrCreateProperty(index, property, value);
2815 if (roleIndex != -1)
2816 emitItemsChanged(index, 1, QList<
int>(1, roleIndex));
2821
2822
2823
2824
2825
2826void QQmlListModel::sync()
2831 qmlWarning(
this) <<
"List sync() can only be called from a WorkerScript";
2835 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
2836 const QV4::CompiledData::Binding *binding)
2838 if (binding->type() >= QV4::CompiledData::Binding::Type_Object) {
2839 const quint32 targetObjectIndex = binding->value.objectIndex;
2840 const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
2841 QString objName = compilationUnit->stringAt(target->inheritedTypeNameIndex);
2842 if (objName != listElementTypeName) {
2843 const QMetaObject *mo = resolveType(objName);
2844 if (mo != &QQmlListElement::staticMetaObject) {
2845 error(target, QQmlListModel::tr(
"ListElement: cannot contain nested elements"));
2848 listElementTypeName = objName;
2851 if (!compilationUnit->stringAt(target->idNameIndex).isEmpty()) {
2852 error(target->locationOfIdProperty, QQmlListModel::tr(
"ListElement: cannot use reserved \"id\" property"));
2856 const QV4::CompiledData::Binding *binding = target->bindingTable();
2857 for (quint32 i = 0; i < target->nBindings; ++i, ++binding) {
2858 QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
2859 if (propName.isEmpty()) {
2860 error(binding, QQmlListModel::tr(
"ListElement: cannot contain nested elements"));
2863 if (!verifyProperty(compilationUnit, binding))
2866 }
else if (binding->type() == QV4::CompiledData::Binding::Type_Script) {
2867 QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
2868 if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) {
2870 evaluateEnum(scriptStr, &ok);
2872 error(binding, QQmlListModel::tr(
"ListElement: cannot use script for property value"));
2882 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
2883 const QV4::CompiledData::Binding *binding,
ListModel *model, QQmlListModel *owner,
2884 int outterElementIndex)
2886 const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex);
2888 bool roleSet =
false;
2889 const QV4::CompiledData::Binding::Type bindingType = binding->type();
2890 if (bindingType >= QV4::CompiledData::Binding::Type_Object) {
2891 const quint32 targetObjectIndex = binding->value.objectIndex;
2892 const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
2895 if (outterElementIndex == -1) {
2898 const ListLayout::
Role &role = model->getOrCreateListRole(elementName);
2901 if (subModel ==
nullptr) {
2902 subModel =
new ListModel(role.subLayout,
nullptr);
2903 model->setOrCreateProperty(
2904 outterElementIndex, elementName,
2905 QVariant::fromValue(subModel));
2912 const QV4::CompiledData::Binding *subBinding = target->bindingTable();
2913 for (quint32 i = 0; i < target->nBindings; ++i, ++subBinding) {
2914 roleSet |= applyProperty(compilationUnit, subBinding, subModel, owner, elementIndex);
2916 }
else if (!model) {
2921 const bool isTranslationBinding = binding->isTranslationBinding();
2922 if (isTranslationBinding) {
2923 value = QVariant::fromValue<
const QV4::CompiledData::Binding*>(binding);
2924 }
else if (binding->evaluatesToString()) {
2925 value = compilationUnit->bindingValueAsString(binding);
2926 }
else if (bindingType == QV4::CompiledData::Binding::Type_Number) {
2927 value = compilationUnit->bindingValueAsNumber(binding);
2928 }
else if (bindingType == QV4::CompiledData::Binding::Type_Boolean) {
2929 value = binding->valueAsBoolean();
2930 }
else if (bindingType == QV4::CompiledData::Binding::Type_Null) {
2931 value = QVariant::fromValue(
nullptr);
2932 }
else if (bindingType == QV4::CompiledData::Binding::Type_Script) {
2933 QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
2934 if (definesEmptyList(scriptStr)) {
2935 value = QVariant::fromValue(
2936 new ListModel(model->getOrCreateListRole(elementName).subLayout,
nullptr));
2937 }
else if (binding->isFunctionExpression()) {
2938 QQmlBinding::Identifier id = binding->value.compiledScriptIndex;
2939 Q_ASSERT(id != QQmlBinding::Invalid);
2941 auto v4 = compilationUnit->engine;
2942 QV4::Scope scope(v4);
2944 if (model->m_modelCache ==
nullptr) {
2945 model->m_modelCache =
new QQmlListModel(owner, model, v4);
2946 QQmlEngine::setContextForObject(
2947 model->m_modelCache, QQmlEngine::contextForObject(owner));
2951 QV4::ScopedContext context(scope, QV4::QmlContext::create(v4->rootContext(), QQmlContextData::get(qmlContext(model->m_modelCache)),
nullptr));
2952 QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(context, compilationUnit->runtimeFunctions[id]));
2955 QV4::ScopedValue result(scope, function->call(v4->globalObject,
nullptr, 0));
2956 if (v4->hasException)
2957 v4->catchException();
2959 QJSValuePrivate::setValue(&v, result);
2963 value = evaluateEnum(scriptStr, &ok);
2969 model->setOrCreateProperty(outterElementIndex, elementName, value);
2970 auto listModel = model->m_modelCache;
2971 if (isTranslationBinding && listModel) {
2972 if (!listModel->translationChangeHandler) {
2973 auto ep = QQmlEnginePrivate::get(compilationUnit->engine);
2974 model->m_modelCache->translationChangeHandler = std::make_unique<QPropertyNotifier>(
2975 ep->translationLanguage.addNotifier([listModel](){ listModel->updateTranslations(); }));
2984 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
2985 const QList<
const QV4::CompiledData::Binding *> &bindings)
2987 listElementTypeName = QString();
2989 for (
const QV4::CompiledData::Binding *binding : bindings) {
2990 QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
2991 if (!propName.isEmpty()) {
2992 error(binding, QQmlListModel::tr(
"ListModel: undefined property '%1'").arg(propName));
2995 if (!verifyProperty(compilationUnit, binding))
3001 QObject *obj,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
3002 const QList<
const QV4::CompiledData::Binding *> &bindings)
3004 QQmlListModel *rv =
static_cast<QQmlListModel *>(obj);
3006 rv->m_engine = qmlEngine(rv)->handle();
3007 rv->m_compilationUnit = compilationUnit;
3009 bool setRoles =
false;
3011 for (
const QV4::CompiledData::Binding *binding : bindings) {
3012 if (binding->type() != QV4::CompiledData::Binding::Type_Object)
3014 setRoles |= applyProperty(
3015 compilationUnit, binding, rv->m_listModel, rv, -1);
3018 if (setRoles ==
false)
3019 qmlWarning(obj) <<
"All ListElement declarations are empty, no roles can be created unless dynamicRoles is set.";
3024 if (s.startsWith(QLatin1Char(
'[')) && s.endsWith(QLatin1Char(
']'))) {
3025 for (
auto c : s.sliced(1).chopped(1)) {
3036
3037
3038
3039
3040
3041
3042
3043
3044
3045
3046
3047
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3084#include "moc_qqmllistmodel_p_p.cpp"
3086#include "moc_qqmllistmodel_p.cpp"
void updateValues(const QVariantMap &object, QList< int > &roles)
void setNodeUpdatesEnabled(bool enable)
ListElement(int existingUid)
ListLayout(const ListLayout *other)
const Role * getExistingRole(QV4::String *key) const
static void sync(ListLayout *src, ListLayout *target)
const Role & getExistingRole(int index) const
const Role & getRoleOrCreate(QV4::String *key, Role::DataType type)
void set(int elementIndex, QV4::Object *object, SetElement reason=SetElement::IsCurrentlyUpdated)
static bool sync(ListModel *src, ListModel *target)
int append(QV4::Object *object)
QVariant getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV4::ExecutionEngine *eng)
void move(int from, int to, int n)
void insertElement(int index)
ListModel(ListLayout *layout, QQmlListModel *modelCache)
void insert(int elementIndex, QV4::Object *object)
void updateTranslations()
QObject * getOrCreateModelObject(QQmlListModel *model, int elementIndex)
ListModel * getListProperty(int elementIndex, const ListLayout::Role &role)
void set(int elementIndex, QV4::Object *object, QList< int > *roles)
const ListLayout::Role & getExistingRole(int index) const
void verifyBindings(const QQmlRefPointer< QV4::CompiledData::CompilationUnit > &compilationUnit, const QList< const QV4::CompiledData::Binding * > &bindings) override
DEFINE_OBJECT_VTABLE(ModelObject)
static QAtomicInt uidCounter(MIN_LISTMODEL_UID)
Q_DECLARE_METATYPE(const QV4::CompiledData::Binding *)
static bool isMemoryUsed(const char *mem)
static QString roleTypeName(ListLayout::Role::DataType t)
PropertyKey next(const Object *o, Property *pd=nullptr, PropertyAttributes *attrs=nullptr) override
~ModelObjectOwnPropertyKeyIterator() override=default
bool isTranslation() const
QString toString(const QQmlListModel *owner) const
void setTranslation(const QV4::CompiledData::Binding *binding)