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);
1597 const int roleCount = m_model->m_listModel->roleCount();
1598 if (!m_initialized) {
1600 Q_ALLOCA_VAR(
int, changedRoles, roleCount *
sizeof(
int));
1601 for (
int i = 0; i < roleCount; ++i)
1602 changedRoles[i] = i;
1603 emitDirectNotifies(changedRoles, roleCount);
1607 for (
int i=0 ; i < roleCount ; ++i) {
1608 const ListLayout::
Role &role = m_model->m_listModel->getExistingRole(i);
1609 QByteArray name = role.name.toUtf8();
1610 const QVariant &data = m_model->data(m_elementIndex, i);
1617 if (!m_initialized) {
1618 emitDirectNotifies(roles.constData(), roles.size());
1621 int roleCount = roles.size();
1622 for (
int i=0 ; i < roleCount ; ++i) {
1623 int roleIndex = roles.at(i);
1624 const ListLayout::
Role &role = m_model->m_listModel->getExistingRole(roleIndex);
1625 QByteArray name = role.name.toUtf8();
1626 const QVariant &data = m_model->data(m_elementIndex, roleIndex);
1636 QString propName = QString::fromUtf8(name(index));
1637 const QVariant value =
this->value(index);
1639 QV4::Scope scope(m_model->engine());
1640 QV4::ScopedValue v(scope, scope.engine->fromVariant(value));
1642 int roleIndex = m_model->m_listModel->setExistingProperty(m_elementIndex, propName, v, scope.engine);
1643 if (roleIndex != -1)
1644 m_model->emitItemsChanged(m_elementIndex, 1, QList<
int>(1, roleIndex));
1650 Q_ASSERT(!m_initialized);
1651 QQmlData *ddata = QQmlData::get(object(),
false);
1655 if (!qmlEngine(m_model))
1657 for (
int i = 0; i < roleCount; ++i) {
1658 const int changedRole = changedRoles[i];
1659 QQmlNotifier::notify(ddata, changedRole);
1665bool ModelObject::virtualPut(Managed *m, PropertyKey id,
const Value &value, Value *receiver)
1668 return Object::virtualPut(m, id, value, receiver);
1669 QString propName = id.toQString();
1673 ExecutionEngine *eng = that->engine();
1674 const int elementIndex = that->d()->elementIndex();
1675 if (QQmlListModel *model = that->d()->m_model) {
1677 = model->listModel()->setExistingProperty(elementIndex, propName, value, eng);
1678 if (roleIndex != -1)
1679 model->emitItemsChanged(elementIndex, 1, QList<
int>(1, roleIndex));
1684 mo->emitPropertyNotification(propName.toUtf8());
1688ReturnedValue
ModelObject::virtualGet(
const Managed *m, PropertyKey id,
const Value *receiver,
bool *hasProperty)
1691 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1695 ScopedString name(scope, id.asStringOrSymbol());
1696 QQmlListModel *model = that->d()->m_model;
1698 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1700 const ListLayout::
Role *role = model->listModel()->getExistingRole(name);
1702 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1704 *hasProperty =
true;
1706 if (QQmlEngine *qmlEngine = that->engine()->qmlEngine()) {
1707 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine);
1708 if (ep && ep->propertyCapture)
1709 ep->propertyCapture->captureProperty(that->object(), -1, role
->index,
false);
1712 const int elementIndex = that->d()->elementIndex();
1713 QVariant value = model->data(elementIndex, role
->index);
1714 return that->engine()->fromVariant(value);
1717ReturnedValue
ModelObject::virtualResolveLookupGetter(
const Object *object, ExecutionEngine *engine, Lookup *lookup)
1719 lookup->call = Lookup::Call::GetterQObjectPropertyFallback;
1720 return lookup->getter(engine, *object);
1727 PropertyKey next(
const Object *o, Property *pd =
nullptr, PropertyAttributes *attrs =
nullptr)
override;
1735 ExecutionEngine *v4 = that->engine();
1737 QQmlListModel *model = that->d()->m_model;
1738 ListModel *listModel = model ? model->listModel() :
nullptr;
1740 Scope scope(that->engine());
1743 ScopedString roleName(scope, v4->newString(role.name));
1745 *attrs = QV4::Attr_Data;
1748 QVariant value = model->data(that->d()->elementIndex(), role
.index);
1749 if (
auto recursiveListModel = qvariant_cast<QQmlListModel*>(value)) {
1750 auto size = recursiveListModel->count();
1751 auto array = ScopedArrayObject{scope, v4->newArrayObject(size)};
1752 QV4::ScopedValue val(scope);
1753 for (
auto i = 0; i < size; i++) {
1754 val = QJSValuePrivate::convertToReturnedValue(
1755 v4, recursiveListModel->get(i));
1756 array->arrayPut(i, val);
1760 pd->value = v4->fromVariant(value);
1763 return roleName->toPropertyKey();
1769 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
1772OwnPropertyKeyIterator *
ModelObject::virtualOwnPropertyKeys(
const Object *m, Value *target)
1791 object->updateValues(obj, roles);
1797 QList<
int> changedRoles;
1798 for (
int i = 0; i < src->m_meta->count(); ++i) {
1799 const QByteArray &name = src->m_meta->name(i);
1800 QVariant value = src->m_meta->value(i);
1802 QQmlListModel *srcModel = qobject_cast<QQmlListModel *>(value.value<QObject *>());
1803 QQmlListModel *targetModel = qobject_cast<QQmlListModel *>(target->m_meta->value(i).value<QObject *>());
1805 bool modelHasChanges =
false;
1807 if (targetModel ==
nullptr)
1808 targetModel = QQmlListModel::createWithOwner(target->m_owner);
1810 modelHasChanges = QQmlListModel::sync(srcModel, targetModel);
1812 QObject *targetModelObject = targetModel;
1813 value = QVariant::fromValue(targetModelObject);
1814 }
else if (targetModel) {
1818 if (target->setValue(name, value) || modelHasChanges)
1819 changedRoles << target->m_owner->m_roles.indexOf(QString::fromUtf8(name));
1821 return changedRoles;
1826 for (
auto it = object.cbegin(), end = object.cend(); it != end; ++it) {
1827 const QString &key = it.key();
1829 int roleIndex = m_owner->m_roles.indexOf(key);
1830 if (roleIndex == -1) {
1831 roleIndex = m_owner->m_roles.size();
1832 m_owner->m_roles.append(key);
1835 QVariant value = it.value();
1839 if (value.userType() == qMetaTypeId<QJSValue>())
1840 value = value.value<QJSValue>().toVariant();
1842 if (value.userType() == QMetaType::QVariantList) {
1843 QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner);
1845 QVariantList subArray = value.toList();
1846 QVariantList::const_iterator subIt = subArray.cbegin();
1847 QVariantList::const_iterator subEnd = subArray.cend();
1848 while (subIt != subEnd) {
1849 const QVariantMap &subObject = subIt->toMap();
1850 subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1854 QObject *subModelObject = subModel;
1855 value = QVariant::fromValue(subModelObject);
1858 const QByteArray &keyUtf8 = key.toUtf8();
1860 QQmlListModel *existingModel = qobject_cast<QQmlListModel *>(m_meta->value(keyUtf8).value<QObject *>());
1861 delete existingModel;
1863 if (m_meta->setValue(keyUtf8, value))
1875 for (
int i=0 ; i < count() ; ++i) {
1876 QQmlListModel *subModel = qobject_cast<QQmlListModel *>(value(i).value<QObject *>());
1886 QVariant v = value(index);
1887 QQmlListModel *model = qobject_cast<QQmlListModel *>(v.value<QObject *>());
1896 QQmlListModel *parentModel = m_owner->m_owner;
1898 QVariant v = value(index);
1902 if (v.userType() == qMetaTypeId<QJSValue>())
1903 v= v.value<QJSValue>().toVariant();
1905 if (v.userType() == QMetaType::QVariantList) {
1906 QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel);
1908 QVariantList subArray = v.toList();
1909 QVariantList::const_iterator subIt = subArray.cbegin();
1910 QVariantList::const_iterator subEnd = subArray.cend();
1911 while (subIt != subEnd) {
1912 const QVariantMap &subObject = subIt->toMap();
1913 subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1917 QObject *subModelObject = subModel;
1918 v = QVariant::fromValue(subModelObject);
1923 int elementIndex = parentModel->m_modelObjects.indexOf(m_owner);
1924 if (elementIndex != -1) {
1925 int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData()));
1926 if (roleIndex != -1)
1927 parentModel->emitItemsChanged(elementIndex, 1, QList<
int>(1, roleIndex));
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
2030
2032QQmlListModel::QQmlListModel(QObject *parent)
2033: QAbstractListModel(parent)
2035 m_mainThread =
true;
2038 m_dynamicRoles =
false;
2040 m_layout =
new ListLayout;
2041 m_listModel =
new ListModel(m_layout,
this);
2046QQmlListModel::QQmlListModel(
const QQmlListModel *owner, ListModel *data, QV4::ExecutionEngine *engine, QObject *parent)
2047: QAbstractListModel(parent)
2049 m_mainThread = owner->m_mainThread;
2051 m_agent = owner->m_agent;
2053 Q_ASSERT(owner->m_dynamicRoles ==
false);
2054 m_dynamicRoles =
false;
2059 m_compilationUnit = owner->m_compilationUnit;
2062QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent)
2063: QAbstractListModel(agent)
2065 m_mainThread =
false;
2068 m_dynamicRoles = orig->m_dynamicRoles;
2070 if (ListLayout *layout = orig->m_layout)
2071 m_layout =
new ListLayout(layout);
2073 m_layout =
new ListLayout;
2075 m_listModel =
new ListModel(m_layout,
this);
2080 ListModel::sync(orig->m_listModel, m_listModel);
2083 m_compilationUnit = orig->m_compilationUnit;
2086QQmlListModel::~QQmlListModel()
2088 qDeleteAll(m_modelObjects);
2091 m_listModel->destroy();
2094 if (m_mainThread && m_agent)
2095 m_agent->modelDestroyed();
2098 if (m_agent && m_ownAgent)
2101 m_listModel =
nullptr;
2107QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner)
2109 QQmlListModel *model =
new QQmlListModel;
2111 model->m_mainThread = newOwner->m_mainThread;
2112 model->m_engine = newOwner->m_engine;
2113 model->m_agent = newOwner->m_agent;
2114 model->m_dynamicRoles = newOwner->m_dynamicRoles;
2116 QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner));
2121QV4::ExecutionEngine *QQmlListModel::engine()
const
2123 if (m_engine ==
nullptr) {
2124 m_engine = qmlEngine(
this)->handle();
2130bool QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target)
2132 Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles);
2134 bool hasChanges =
false;
2136 target->m_roles = src->m_roles;
2139 QHash<
int, ElementSync> elementHash;
2140 for (
int i = 0 ; i < target->m_modelObjects.size(); ++i) {
2141 DynamicRoleModelNode *e = target->m_modelObjects.at(i);
2142 int uid = e->getUid();
2145 sync.targetIndex = i;
2146 elementHash.insert(uid, sync);
2148 for (
int i = 0 ; i < src->m_modelObjects.size(); ++i) {
2149 DynamicRoleModelNode *e = src->m_modelObjects.at(i);
2150 int uid = e->getUid();
2152 QHash<
int, ElementSync>::iterator it = elementHash.find(uid);
2153 if (it == elementHash.end()) {
2157 elementHash.insert(uid, sync);
2159 ElementSync &sync = it.value();
2166 int rowsRemoved = 0;
2167 for (
int i = 0 ; i < target->m_modelObjects.size() ; ++i) {
2168 DynamicRoleModelNode *element = target->m_modelObjects.at(i);
2169 ElementSync &s = elementHash.find(element->getUid()).value();
2170 Q_ASSERT(s.targetIndex >= 0);
2172 s.targetIndex -= rowsRemoved;
2173 if (s.src ==
nullptr) {
2174 Q_ASSERT(s.targetIndex == i);
2176 target->beginRemoveRows(QModelIndex(), i, i);
2177 target->m_modelObjects.remove(i, 1);
2178 target->endRemoveRows();
2187 target->m_modelObjects.clear();
2188 for (
int i = 0 ; i < src->m_modelObjects.size() ; ++i) {
2189 DynamicRoleModelNode *element = src->m_modelObjects.at(i);
2190 ElementSync &s = elementHash.find(element->getUid()).value();
2191 Q_ASSERT(s.srcIndex >= 0);
2192 DynamicRoleModelNode *targetElement = s.target;
2193 if (targetElement ==
nullptr) {
2194 targetElement =
new DynamicRoleModelNode(target, element->getUid());
2196 s.changedRoles = DynamicRoleModelNode::sync(element, targetElement);
2197 target->m_modelObjects.append(targetElement);
2205 int rowsInserted = 0;
2206 for (
int i = 0 ; i < target->m_modelObjects.size() ; ++i) {
2207 DynamicRoleModelNode *element = target->m_modelObjects.at(i);
2208 ElementSync &s = elementHash.find(element->getUid()).value();
2209 Q_ASSERT(s.srcIndex >= 0);
2210 s.srcIndex += rowsInserted;
2211 if (s.srcIndex != s.targetIndex) {
2212 if (s.targetIndex == -1) {
2213 target->beginInsertRows(QModelIndex(), i, i);
2214 target->endInsertRows();
2216 target->beginMoveRows(QModelIndex(), i, i, QModelIndex(), s.srcIndex);
2217 target->endMoveRows();
2222 if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) {
2223 QModelIndex idx = target->createIndex(i, 0);
2224 emit target->dataChanged(idx, idx, s.changedRoles);
2231void QQmlListModel::emitItemsChanged(
int index,
int count,
const QList<
int> &roles)
2237 emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);;
2240void QQmlListModel::emitItemsAboutToBeInserted(
int index,
int count)
2242 Q_ASSERT(index >= 0 && count >= 0);
2244 beginInsertRows(QModelIndex(), index, index + count - 1);
2247void QQmlListModel::emitItemsInserted()
2251 emit countChanged();
2255QQmlListModelWorkerAgent *QQmlListModel::agent()
2260 m_agent =
new QQmlListModelWorkerAgent(
this);
2265QModelIndex QQmlListModel::index(
int row,
int column,
const QModelIndex &parent)
const
2267 return row >= 0 && row < count() && column == 0 && !parent.isValid()
2268 ? createIndex(row, column)
2272int QQmlListModel::rowCount(
const QModelIndex &parent)
const
2274 return !parent.isValid() ? count() : 0;
2277QVariant QQmlListModel::data(
const QModelIndex &index,
int role)
const
2279 return data(index.row(), role);
2282bool QQmlListModel::setData(
const QModelIndex &index,
const QVariant &value,
int role)
2284 const int row = index.row();
2285 if (row >= count() || row < 0)
2288 if (m_dynamicRoles) {
2289 const QByteArray property = m_roles.at(role).toUtf8();
2290 if (m_modelObjects[row]->setValue(property, value)) {
2291 emitItemsChanged(row, 1, QList<
int>(1, role));
2295 const ListLayout::Role &r = m_listModel->getExistingRole(role);
2296 const int roleIndex = m_listModel->setOrCreateProperty(row, r.name, value);
2297 if (roleIndex != -1) {
2298 emitItemsChanged(row, 1, QList<
int>(1, role));
2306QVariant QQmlListModel::data(
int index,
int role)
const
2310 if (index >= count() || index < 0)
2314 v = m_modelObjects[index]->getValue(m_roles[role]);
2316 v = m_listModel->getProperty(index, role,
this, engine());
2321QHash<
int, QByteArray> QQmlListModel::roleNames()
const
2323 QHash<
int, QByteArray> roleNames;
2325 if (m_dynamicRoles) {
2326 const auto size = m_roles.size();
2327 roleNames.reserve(size);
2328 for (
int i = 0 ; i < size ; ++i)
2329 roleNames.insert(i, m_roles.at(i).toUtf8());
2331 const auto size = m_listModel->roleCount();
2332 roleNames.reserve(size);
2333 for (
int i = 0 ; i < size; ++i) {
2334 const ListLayout::Role &r = m_listModel->getExistingRole(i);
2335 roleNames.insert(i, r.name.toUtf8());
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368void QQmlListModel::setDynamicRoles(
bool enableDynamicRoles)
2370 if (m_mainThread && m_agent ==
nullptr) {
2371 if (enableDynamicRoles) {
2372 if (m_layout && m_layout->roleCount())
2373 qmlWarning(
this) << tr(
"unable to enable dynamic roles as this model is not empty");
2375 m_dynamicRoles =
true;
2377 if (m_roles.size()) {
2378 qmlWarning(
this) << tr(
"unable to enable static roles as this model is not empty");
2380 m_dynamicRoles =
false;
2384 qmlWarning(
this) << tr(
"dynamic role setting must be made from the main thread, before any worker scripts are created");
2389
2390
2391
2392int QQmlListModel::count()
const
2394 return m_dynamicRoles ? m_modelObjects.size() : m_listModel->elementCount();
2398
2399
2400
2401
2402
2403
2404
2405void QQmlListModel::clear()
2407 removeElements(0, count());
2411
2412
2413
2414
2415
2416
2417void QQmlListModel::remove(QQmlV4FunctionPtr args)
2419 int argLength = args->length();
2421 if (argLength == 1 || argLength == 2) {
2422 QV4::Scope scope(args->v4engine());
2423 int index = QV4::ScopedValue(scope, (*args)[0])->toInt32();
2424 int removeCount = (argLength == 2 ? QV4::ScopedValue(scope, (*args)[1])->toInt32() : 1);
2426 if (index < 0 || index+removeCount > count() || removeCount <= 0) {
2427 qmlWarning(
this) << tr(
"remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count());
2431 removeElements(index, removeCount);
2433 qmlWarning(
this) << tr(
"remove: incorrect number of arguments");
2437void QQmlListModel::removeElements(
int index,
int removeCount)
2439 Q_ASSERT(index >= 0 && removeCount >= 0);
2445 beginRemoveRows(QModelIndex(), index, index + removeCount - 1);
2447 QList<std::function<
void()>> toDestroy;
2448 if (m_dynamicRoles) {
2449 for (
int i=0 ; i < removeCount ; ++i) {
2450 auto modelObject = m_modelObjects[index+i];
2451 toDestroy.append([modelObject](){
2455 m_modelObjects.remove(index, removeCount);
2457 toDestroy = m_listModel->remove(index, removeCount);
2462 emit countChanged();
2464 for (
const auto &destroyer : std::as_const(toDestroy))
2468void QQmlListModel::updateTranslations()
2475 Q_ASSERT(m_listModel);
2478 for (
int i = 0, end = m_listModel->roleCount(); i != end; ++i) {
2479 if (m_listModel->getExistingRole(i).type == ListLayout::Role::String)
2483 if (!roles.isEmpty())
2484 emitItemsChanged(0, rowCount(QModelIndex()), roles);
2486 m_listModel->updateTranslations();
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2505void QQmlListModel::insert(QQmlV4FunctionPtr args)
2507 if (args->length() == 2) {
2508 QV4::Scope scope(args->v4engine());
2509 QV4::ScopedValue arg0(scope, (*args)[0]);
2510 int index = arg0->toInt32();
2512 if (index < 0 || index > count()) {
2513 qmlWarning(
this) << tr(
"insert: index %1 out of range").arg(index);
2517 QV4::ScopedObject argObject(scope, (*args)[1]);
2518 QV4::ScopedArrayObject objectArray(scope, (*args)[1]);
2520 QV4::ScopedObject argObject(scope);
2522 int objectArrayLength = objectArray->getLength();
2523 emitItemsAboutToBeInserted(index, objectArrayLength);
2524 for (
int i=0 ; i < objectArrayLength ; ++i) {
2525 argObject = objectArray->get(i);
2527 if (m_dynamicRoles) {
2528 m_modelObjects.insert(index+i, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2530 m_listModel->insert(index+i, argObject);
2533 emitItemsInserted();
2534 }
else if (argObject) {
2535 emitItemsAboutToBeInserted(index, 1);
2537 if (m_dynamicRoles) {
2538 m_modelObjects.insert(index, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2540 m_listModel->insert(index, argObject);
2543 emitItemsInserted();
2545 qmlWarning(
this) << tr(
"insert: value is not an object");
2548 qmlWarning(
this) << tr(
"insert: value is not an object");
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566void QQmlListModel::move(
int from,
int to,
int n)
2568 if (n == 0 || from == to)
2570 if (!canMove(from, to, n)) {
2571 qmlWarning(
this) << tr(
"move: out of range");
2576 beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to);
2578 if (m_dynamicRoles) {
2580 int realFrom = from;
2593 QPODVector<DynamicRoleModelNode *, 4> store;
2594 for (
int i=0 ; i < (realTo-realFrom) ; ++i)
2595 store.append(m_modelObjects[realFrom+realN+i]);
2596 for (
int i=0 ; i < realN ; ++i)
2597 store.append(m_modelObjects[realFrom+i]);
2598 for (
int i=0 ; i < store.count() ; ++i)
2599 m_modelObjects[realFrom+i] = store[i];
2602 m_listModel->move(from, to, n);
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621void QQmlListModel::append(QQmlV4FunctionPtr args)
2623 if (args->length() == 1) {
2624 QV4::Scope scope(args->v4engine());
2625 QV4::ScopedObject argObject(scope, (*args)[0]);
2626 QV4::ScopedArrayObject objectArray(scope, (*args)[0]);
2629 QV4::ScopedObject argObject(scope);
2631 int objectArrayLength = objectArray->getLength();
2632 if (objectArrayLength > 0) {
2633 int index = count();
2634 emitItemsAboutToBeInserted(index, objectArrayLength);
2636 for (
int i=0 ; i < objectArrayLength ; ++i) {
2637 argObject = objectArray->get(i);
2639 if (m_dynamicRoles) {
2640 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2642 m_listModel->append(argObject);
2646 emitItemsInserted();
2648 }
else if (argObject) {
2651 if (m_dynamicRoles) {
2652 index = m_modelObjects.size();
2653 emitItemsAboutToBeInserted(index, 1);
2654 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2656 index = m_listModel->elementCount();
2657 emitItemsAboutToBeInserted(index, 1);
2658 m_listModel->append(argObject);
2661 emitItemsInserted();
2663 qmlWarning(
this) << tr(
"append: value is not an object");
2666 qmlWarning(
this) << tr(
"append: value is not an object");
2671
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
2702QJSValue QQmlListModel::get(
int index)
const
2704 QV4::Scope scope(engine());
2705 QV4::ScopedValue result(scope, QV4::Value::undefinedValue());
2707 if (index >= 0 && index < count()) {
2709 if (m_dynamicRoles) {
2710 DynamicRoleModelNode *object = m_modelObjects[index];
2711 result = QV4::QObjectWrapper::wrap(scope.engine, object);
2713 QObject *object = m_listModel->getOrCreateModelObject(
const_cast<QQmlListModel *>(
this), index);
2714 QQmlData *ddata = QQmlData::get(object);
2715 if (ddata->jsWrapper.isNullOrUndefined()) {
2716 result = scope.engine->memoryManager->allocate<QV4::ModelObject>(object,
const_cast<QQmlListModel *>(
this));
2718 ddata->jsWrapper.set(scope.engine, result);
2720 result = ddata->jsWrapper.value();
2725 return QJSValuePrivate::fromReturnedValue(result->asReturnedValue());
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744void QQmlListModel::set(
int index,
const QJSValue &value)
2746 QV4::Scope scope(engine());
2747 QV4::ScopedObject object(scope, QJSValuePrivate::asReturnedValue(&value));
2750 qmlWarning(
this) << tr(
"set: value is not an object");
2753 if (index > count() || index < 0) {
2754 qmlWarning(
this) << tr(
"set: index %1 out of range").arg(index);
2759 if (index == count()) {
2760 emitItemsAboutToBeInserted(index, 1);
2762 if (m_dynamicRoles) {
2763 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(object),
this));
2765 m_listModel->insert(index, object);
2768 emitItemsInserted();
2773 if (m_dynamicRoles) {
2774 m_modelObjects[index]->updateValues(scope.engine->variantMapFromJS(object), roles);
2776 m_listModel->set(index, object, &roles);
2780 emitItemsChanged(index, 1, roles);
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797void QQmlListModel::setProperty(
int index,
const QString& property,
const QVariant& value)
2799 if (count() == 0 || index >= count() || index < 0) {
2800 qmlWarning(
this) << tr(
"set: index %1 out of range").arg(index);
2804 if (m_dynamicRoles) {
2805 int roleIndex = m_roles.indexOf(property);
2806 if (roleIndex == -1) {
2807 roleIndex = m_roles.size();
2808 m_roles.append(property);
2810 if (m_modelObjects[index]->setValue(property.toUtf8(), value))
2811 emitItemsChanged(index, 1, QList<
int>(1, roleIndex));
2813 int roleIndex = m_listModel->setOrCreateProperty(index, property, value);
2814 if (roleIndex != -1)
2815 emitItemsChanged(index, 1, QList<
int>(1, roleIndex));
2820
2821
2822
2823
2824
2825void QQmlListModel::sync()
2830 qmlWarning(
this) <<
"List sync() can only be called from a WorkerScript";
2834 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
2835 const QV4::CompiledData::Binding *binding)
2837 if (binding->type() >= QV4::CompiledData::Binding::Type_Object) {
2838 const quint32 targetObjectIndex = binding->value.objectIndex;
2839 const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
2840 QString objName = compilationUnit->stringAt(target->inheritedTypeNameIndex);
2841 if (objName != listElementTypeName) {
2842 const QMetaObject *mo = resolveType(objName);
2843 if (mo != &QQmlListElement::staticMetaObject) {
2844 error(target, QQmlListModel::tr(
"ListElement: cannot contain nested elements"));
2847 listElementTypeName = objName;
2850 if (!compilationUnit->stringAt(target->idNameIndex).isEmpty()) {
2851 error(target->locationOfIdProperty, QQmlListModel::tr(
"ListElement: cannot use reserved \"id\" property"));
2855 const QV4::CompiledData::Binding *binding = target->bindingTable();
2856 for (quint32 i = 0; i < target->nBindings; ++i, ++binding) {
2857 QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
2858 if (propName.isEmpty()) {
2859 error(binding, QQmlListModel::tr(
"ListElement: cannot contain nested elements"));
2862 if (!verifyProperty(compilationUnit, binding))
2865 }
else if (binding->type() == QV4::CompiledData::Binding::Type_Script) {
2866 QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
2867 if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) {
2869 evaluateEnum(scriptStr, &ok);
2871 error(binding, QQmlListModel::tr(
"ListElement: cannot use script for property value"));
2881 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
2882 const QV4::CompiledData::Binding *binding,
ListModel *model, QQmlListModel *owner,
2883 int outterElementIndex)
2885 const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex);
2887 bool roleSet =
false;
2888 const QV4::CompiledData::Binding::Type bindingType = binding->type();
2889 if (bindingType >= QV4::CompiledData::Binding::Type_Object) {
2890 const quint32 targetObjectIndex = binding->value.objectIndex;
2891 const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
2894 if (outterElementIndex == -1) {
2897 const ListLayout::
Role &role = model->getOrCreateListRole(elementName);
2900 if (subModel ==
nullptr) {
2901 subModel =
new ListModel(role.subLayout,
nullptr);
2902 model->setOrCreateProperty(
2903 outterElementIndex, elementName,
2904 QVariant::fromValue(subModel));
2911 const QV4::CompiledData::Binding *subBinding = target->bindingTable();
2912 for (quint32 i = 0; i < target->nBindings; ++i, ++subBinding) {
2913 roleSet |= applyProperty(compilationUnit, subBinding, subModel, owner, elementIndex);
2915 }
else if (!model) {
2920 const bool isTranslationBinding = binding->isTranslationBinding();
2921 if (isTranslationBinding) {
2922 value = QVariant::fromValue<
const QV4::CompiledData::Binding*>(binding);
2923 }
else if (binding->evaluatesToString()) {
2924 value = compilationUnit->bindingValueAsString(binding);
2925 }
else if (bindingType == QV4::CompiledData::Binding::Type_Number) {
2926 value = compilationUnit->bindingValueAsNumber(binding);
2927 }
else if (bindingType == QV4::CompiledData::Binding::Type_Boolean) {
2928 value = binding->valueAsBoolean();
2929 }
else if (bindingType == QV4::CompiledData::Binding::Type_Null) {
2930 value = QVariant::fromValue(
nullptr);
2931 }
else if (bindingType == QV4::CompiledData::Binding::Type_Script) {
2932 QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
2933 if (definesEmptyList(scriptStr)) {
2934 value = QVariant::fromValue(
2935 new ListModel(model->getOrCreateListRole(elementName).subLayout,
nullptr));
2936 }
else if (binding->isFunctionExpression()) {
2937 QQmlBinding::Identifier id = binding->value.compiledScriptIndex;
2938 Q_ASSERT(id != QQmlBinding::Invalid);
2940 auto v4 = compilationUnit->engine;
2941 QV4::Scope scope(v4);
2943 if (model->m_modelCache ==
nullptr) {
2944 model->m_modelCache =
new QQmlListModel(owner, model, v4);
2945 QQmlEngine::setContextForObject(
2946 model->m_modelCache, QQmlEngine::contextForObject(owner));
2950 QV4::ScopedContext context(scope, QV4::QmlContext::create(v4->rootContext(), QQmlContextData::get(qmlContext(model->m_modelCache)),
nullptr));
2951 QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(context, compilationUnit->runtimeFunctions[id]));
2954 QV4::ScopedValue result(scope, function->call(v4->globalObject,
nullptr, 0));
2955 if (v4->hasException)
2956 v4->catchException();
2958 QJSValuePrivate::setValue(&v, result);
2962 value = evaluateEnum(scriptStr, &ok);
2968 model->setOrCreateProperty(outterElementIndex, elementName, value);
2969 auto listModel = model->m_modelCache;
2970 if (isTranslationBinding && listModel) {
2971 if (!listModel->translationChangeHandler) {
2972 auto ep = QQmlEnginePrivate::get(compilationUnit->engine);
2973 model->m_modelCache->translationChangeHandler = std::make_unique<QPropertyNotifier>(
2974 ep->translationLanguage.addNotifier([listModel](){ listModel->updateTranslations(); }));
2983 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
2984 const QList<
const QV4::CompiledData::Binding *> &bindings)
2986 listElementTypeName = QString();
2988 for (
const QV4::CompiledData::Binding *binding : bindings) {
2989 QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
2990 if (!propName.isEmpty()) {
2991 error(binding, QQmlListModel::tr(
"ListModel: undefined property '%1'").arg(propName));
2994 if (!verifyProperty(compilationUnit, binding))
3000 QObject *obj,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
3001 const QList<
const QV4::CompiledData::Binding *> &bindings)
3003 QQmlListModel *rv =
static_cast<QQmlListModel *>(obj);
3005 rv->m_engine = qmlEngine(rv)->handle();
3006 rv->m_compilationUnit = compilationUnit;
3008 bool setRoles =
false;
3010 for (
const QV4::CompiledData::Binding *binding : bindings) {
3011 if (binding->type() != QV4::CompiledData::Binding::Type_Object)
3013 setRoles |= applyProperty(
3014 compilationUnit, binding, rv->m_listModel, rv, -1);
3017 if (setRoles ==
false)
3018 qmlWarning(obj) <<
"All ListElement declarations are empty, no roles can be created unless dynamicRoles is set.";
3023 if (s.startsWith(QLatin1Char(
'[')) && s.endsWith(QLatin1Char(
']'))) {
3024 for (
auto c : s.sliced(1).chopped(1)) {
3035
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
3083#include "moc_qqmllistmodel_p_p.cpp"
3085#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)