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
2029QQmlListModel::QQmlListModel(QObject *parent)
2030: QAbstractListModel(parent)
2032 m_mainThread =
true;
2035 m_dynamicRoles =
false;
2037 m_layout =
new ListLayout;
2038 m_listModel =
new ListModel(m_layout,
this);
2043QQmlListModel::QQmlListModel(
const QQmlListModel *owner, ListModel *data, QV4::ExecutionEngine *engine, QObject *parent)
2044: QAbstractListModel(parent)
2046 m_mainThread = owner->m_mainThread;
2048 m_agent = owner->m_agent;
2050 Q_ASSERT(owner->m_dynamicRoles ==
false);
2051 m_dynamicRoles =
false;
2056 m_compilationUnit = owner->m_compilationUnit;
2059QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent)
2060: QAbstractListModel(agent)
2062 m_mainThread =
false;
2065 m_dynamicRoles = orig->m_dynamicRoles;
2067 if (ListLayout *layout = orig->m_layout)
2068 m_layout =
new ListLayout(layout);
2070 m_layout =
new ListLayout;
2072 m_listModel =
new ListModel(m_layout,
this);
2077 ListModel::sync(orig->m_listModel, m_listModel);
2080 m_compilationUnit = orig->m_compilationUnit;
2083QQmlListModel::~QQmlListModel()
2085 qDeleteAll(m_modelObjects);
2088 m_listModel->destroy();
2091 if (m_mainThread && m_agent)
2092 m_agent->modelDestroyed();
2095 if (m_mainThread && m_agent)
2098 m_listModel =
nullptr;
2104QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner)
2106 QQmlListModel *model =
new QQmlListModel;
2108 model->m_mainThread = newOwner->m_mainThread;
2109 model->m_engine = newOwner->m_engine;
2110 model->m_agent = newOwner->m_agent;
2111 model->m_dynamicRoles = newOwner->m_dynamicRoles;
2113 if (model->m_mainThread && model->m_agent)
2114 model->m_agent->addref();
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);
2264QModelIndex QQmlListModel::index(
int row,
int column,
const QModelIndex &parent)
const
2266 return row >= 0 && row < count() && column == 0 && !parent.isValid()
2267 ? createIndex(row, column)
2271int QQmlListModel::rowCount(
const QModelIndex &parent)
const
2273 return !parent.isValid() ? count() : 0;
2276QVariant QQmlListModel::data(
const QModelIndex &index,
int role)
const
2278 return data(index.row(), role);
2281bool QQmlListModel::setData(
const QModelIndex &index,
const QVariant &value,
int role)
2283 const int row = index.row();
2284 if (row >= count() || row < 0)
2287 if (m_dynamicRoles) {
2288 const QByteArray property = m_roles.at(role).toUtf8();
2289 if (m_modelObjects[row]->setValue(property, value)) {
2290 emitItemsChanged(row, 1, QList<
int>(1, role));
2294 const ListLayout::Role &r = m_listModel->getExistingRole(role);
2295 const int roleIndex = m_listModel->setOrCreateProperty(row, r.name, value);
2296 if (roleIndex != -1) {
2297 emitItemsChanged(row, 1, QList<
int>(1, role));
2305QVariant QQmlListModel::data(
int index,
int role)
const
2309 if (index >= count() || index < 0)
2313 v = m_modelObjects[index]->getValue(m_roles[role]);
2315 v = m_listModel->getProperty(index, role,
this, engine());
2320QHash<
int, QByteArray> QQmlListModel::roleNames()
const
2322 QHash<
int, QByteArray> roleNames;
2324 if (m_dynamicRoles) {
2325 const auto size = m_roles.size();
2326 roleNames.reserve(size);
2327 for (
int i = 0 ; i < size ; ++i)
2328 roleNames.insert(i, m_roles.at(i).toUtf8());
2330 const auto size = m_listModel->roleCount();
2331 roleNames.reserve(size);
2332 for (
int i = 0 ; i < size; ++i) {
2333 const ListLayout::Role &r = m_listModel->getExistingRole(i);
2334 roleNames.insert(i, r.name.toUtf8());
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367void QQmlListModel::setDynamicRoles(
bool enableDynamicRoles)
2369 if (m_mainThread && m_agent ==
nullptr) {
2370 if (enableDynamicRoles) {
2371 if (m_layout && m_layout->roleCount())
2372 qmlWarning(
this) << tr(
"unable to enable dynamic roles as this model is not empty");
2374 m_dynamicRoles =
true;
2376 if (m_roles.size()) {
2377 qmlWarning(
this) << tr(
"unable to enable static roles as this model is not empty");
2379 m_dynamicRoles =
false;
2383 qmlWarning(
this) << tr(
"dynamic role setting must be made from the main thread, before any worker scripts are created");
2388
2389
2390
2391int QQmlListModel::count()
const
2393 return m_dynamicRoles ? m_modelObjects.size() : m_listModel->elementCount();
2397
2398
2399
2400
2401
2402
2403
2404void QQmlListModel::clear()
2406 removeElements(0, count());
2410
2411
2412
2413
2414
2415
2416void QQmlListModel::remove(QQmlV4FunctionPtr args)
2418 int argLength = args->length();
2420 if (argLength == 1 || argLength == 2) {
2421 QV4::Scope scope(args->v4engine());
2422 int index = QV4::ScopedValue(scope, (*args)[0])->toInt32();
2423 int removeCount = (argLength == 2 ? QV4::ScopedValue(scope, (*args)[1])->toInt32() : 1);
2425 if (index < 0 || index+removeCount > count() || removeCount <= 0) {
2426 qmlWarning(
this) << tr(
"remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count());
2430 removeElements(index, removeCount);
2432 qmlWarning(
this) << tr(
"remove: incorrect number of arguments");
2436void QQmlListModel::removeElements(
int index,
int removeCount)
2438 Q_ASSERT(index >= 0 && removeCount >= 0);
2444 beginRemoveRows(QModelIndex(), index, index + removeCount - 1);
2446 QList<std::function<
void()>> toDestroy;
2447 if (m_dynamicRoles) {
2448 for (
int i=0 ; i < removeCount ; ++i) {
2449 auto modelObject = m_modelObjects[index+i];
2450 toDestroy.append([modelObject](){
2454 m_modelObjects.remove(index, removeCount);
2456 toDestroy = m_listModel->remove(index, removeCount);
2461 emit countChanged();
2463 for (
const auto &destroyer : std::as_const(toDestroy))
2467void QQmlListModel::updateTranslations()
2474 Q_ASSERT(m_listModel);
2477 for (
int i = 0, end = m_listModel->roleCount(); i != end; ++i) {
2478 if (m_listModel->getExistingRole(i).type == ListLayout::Role::String)
2482 if (!roles.isEmpty())
2483 emitItemsChanged(0, rowCount(QModelIndex()), roles);
2485 m_listModel->updateTranslations();
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2504void QQmlListModel::insert(QQmlV4FunctionPtr args)
2506 if (args->length() == 2) {
2507 QV4::Scope scope(args->v4engine());
2508 QV4::ScopedValue arg0(scope, (*args)[0]);
2509 int index = arg0->toInt32();
2511 if (index < 0 || index > count()) {
2512 qmlWarning(
this) << tr(
"insert: index %1 out of range").arg(index);
2516 QV4::ScopedObject argObject(scope, (*args)[1]);
2517 QV4::ScopedArrayObject objectArray(scope, (*args)[1]);
2519 QV4::ScopedObject argObject(scope);
2521 int objectArrayLength = objectArray->getLength();
2522 emitItemsAboutToBeInserted(index, objectArrayLength);
2523 for (
int i=0 ; i < objectArrayLength ; ++i) {
2524 argObject = objectArray->get(i);
2526 if (m_dynamicRoles) {
2527 m_modelObjects.insert(index+i, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2529 m_listModel->insert(index+i, argObject);
2532 emitItemsInserted();
2533 }
else if (argObject) {
2534 emitItemsAboutToBeInserted(index, 1);
2536 if (m_dynamicRoles) {
2537 m_modelObjects.insert(index, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2539 m_listModel->insert(index, argObject);
2542 emitItemsInserted();
2544 qmlWarning(
this) << tr(
"insert: value is not an object");
2547 qmlWarning(
this) << tr(
"insert: value is not an object");
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565void QQmlListModel::move(
int from,
int to,
int n)
2567 if (n == 0 || from == to)
2569 if (!canMove(from, to, n)) {
2570 qmlWarning(
this) << tr(
"move: out of range");
2575 beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to);
2577 if (m_dynamicRoles) {
2579 int realFrom = from;
2592 QPODVector<DynamicRoleModelNode *, 4> store;
2593 for (
int i=0 ; i < (realTo-realFrom) ; ++i)
2594 store.append(m_modelObjects[realFrom+realN+i]);
2595 for (
int i=0 ; i < realN ; ++i)
2596 store.append(m_modelObjects[realFrom+i]);
2597 for (
int i=0 ; i < store.count() ; ++i)
2598 m_modelObjects[realFrom+i] = store[i];
2601 m_listModel->move(from, to, n);
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620void QQmlListModel::append(QQmlV4FunctionPtr args)
2622 if (args->length() == 1) {
2623 QV4::Scope scope(args->v4engine());
2624 QV4::ScopedObject argObject(scope, (*args)[0]);
2625 QV4::ScopedArrayObject objectArray(scope, (*args)[0]);
2628 QV4::ScopedObject argObject(scope);
2630 int objectArrayLength = objectArray->getLength();
2631 if (objectArrayLength > 0) {
2632 int index = count();
2633 emitItemsAboutToBeInserted(index, objectArrayLength);
2635 for (
int i=0 ; i < objectArrayLength ; ++i) {
2636 argObject = objectArray->get(i);
2638 if (m_dynamicRoles) {
2639 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2641 m_listModel->append(argObject);
2645 emitItemsInserted();
2647 }
else if (argObject) {
2650 if (m_dynamicRoles) {
2651 index = m_modelObjects.size();
2652 emitItemsAboutToBeInserted(index, 1);
2653 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2655 index = m_listModel->elementCount();
2656 emitItemsAboutToBeInserted(index, 1);
2657 m_listModel->append(argObject);
2660 emitItemsInserted();
2662 qmlWarning(
this) << tr(
"append: value is not an object");
2665 qmlWarning(
this) << tr(
"append: value is not an object");
2670
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
2701QJSValue QQmlListModel::get(
int index)
const
2703 QV4::Scope scope(engine());
2704 QV4::ScopedValue result(scope, QV4::Value::undefinedValue());
2706 if (index >= 0 && index < count()) {
2708 if (m_dynamicRoles) {
2709 DynamicRoleModelNode *object = m_modelObjects[index];
2710 result = QV4::QObjectWrapper::wrap(scope.engine, object);
2712 QObject *object = m_listModel->getOrCreateModelObject(
const_cast<QQmlListModel *>(
this), index);
2713 QQmlData *ddata = QQmlData::get(object);
2714 if (ddata->jsWrapper.isNullOrUndefined()) {
2715 result = scope.engine->memoryManager->allocate<QV4::ModelObject>(object,
const_cast<QQmlListModel *>(
this));
2717 ddata->jsWrapper.set(scope.engine, result);
2719 result = ddata->jsWrapper.value();
2724 return QJSValuePrivate::fromReturnedValue(result->asReturnedValue());
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743void QQmlListModel::set(
int index,
const QJSValue &value)
2745 QV4::Scope scope(engine());
2746 QV4::ScopedObject object(scope, QJSValuePrivate::asReturnedValue(&value));
2749 qmlWarning(
this) << tr(
"set: value is not an object");
2752 if (index > count() || index < 0) {
2753 qmlWarning(
this) << tr(
"set: index %1 out of range").arg(index);
2758 if (index == count()) {
2759 emitItemsAboutToBeInserted(index, 1);
2761 if (m_dynamicRoles) {
2762 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(object),
this));
2764 m_listModel->insert(index, object);
2767 emitItemsInserted();
2772 if (m_dynamicRoles) {
2773 m_modelObjects[index]->updateValues(scope.engine->variantMapFromJS(object), roles);
2775 m_listModel->set(index, object, &roles);
2779 emitItemsChanged(index, 1, roles);
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796void QQmlListModel::setProperty(
int index,
const QString& property,
const QVariant& value)
2798 if (count() == 0 || index >= count() || index < 0) {
2799 qmlWarning(
this) << tr(
"set: index %1 out of range").arg(index);
2803 if (m_dynamicRoles) {
2804 int roleIndex = m_roles.indexOf(property);
2805 if (roleIndex == -1) {
2806 roleIndex = m_roles.size();
2807 m_roles.append(property);
2809 if (m_modelObjects[index]->setValue(property.toUtf8(), value))
2810 emitItemsChanged(index, 1, QList<
int>(1, roleIndex));
2812 int roleIndex = m_listModel->setOrCreateProperty(index, property, value);
2813 if (roleIndex != -1)
2814 emitItemsChanged(index, 1, QList<
int>(1, roleIndex));
2819
2820
2821
2822
2823
2824void QQmlListModel::sync()
2829 qmlWarning(
this) <<
"List sync() can only be called from a WorkerScript";
2833 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
2834 const QV4::CompiledData::Binding *binding)
2836 if (binding->type() >= QV4::CompiledData::Binding::Type_Object) {
2837 const quint32 targetObjectIndex = binding->value.objectIndex;
2838 const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
2839 QString objName = compilationUnit->stringAt(target->inheritedTypeNameIndex);
2840 if (objName != listElementTypeName) {
2841 const QMetaObject *mo = resolveType(objName);
2842 if (mo != &QQmlListElement::staticMetaObject) {
2843 error(target, QQmlListModel::tr(
"ListElement: cannot contain nested elements"));
2846 listElementTypeName = objName;
2849 if (!compilationUnit->stringAt(target->idNameIndex).isEmpty()) {
2850 error(target->locationOfIdProperty, QQmlListModel::tr(
"ListElement: cannot use reserved \"id\" property"));
2854 const QV4::CompiledData::Binding *binding = target->bindingTable();
2855 for (quint32 i = 0; i < target->nBindings; ++i, ++binding) {
2856 QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
2857 if (propName.isEmpty()) {
2858 error(binding, QQmlListModel::tr(
"ListElement: cannot contain nested elements"));
2861 if (!verifyProperty(compilationUnit, binding))
2864 }
else if (binding->type() == QV4::CompiledData::Binding::Type_Script) {
2865 QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
2866 if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) {
2868 evaluateEnum(scriptStr, &ok);
2870 error(binding, QQmlListModel::tr(
"ListElement: cannot use script for property value"));
2880 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
2881 const QV4::CompiledData::Binding *binding,
ListModel *model, QQmlListModel *owner,
2882 int outterElementIndex)
2884 const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex);
2886 bool roleSet =
false;
2887 const QV4::CompiledData::Binding::Type bindingType = binding->type();
2888 if (bindingType >= QV4::CompiledData::Binding::Type_Object) {
2889 const quint32 targetObjectIndex = binding->value.objectIndex;
2890 const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
2893 if (outterElementIndex == -1) {
2896 const ListLayout::
Role &role = model->getOrCreateListRole(elementName);
2899 if (subModel ==
nullptr) {
2900 subModel =
new ListModel(role.subLayout,
nullptr);
2901 model->setOrCreateProperty(
2902 outterElementIndex, elementName,
2903 QVariant::fromValue(subModel));
2910 const QV4::CompiledData::Binding *subBinding = target->bindingTable();
2911 for (quint32 i = 0; i < target->nBindings; ++i, ++subBinding) {
2912 roleSet |= applyProperty(compilationUnit, subBinding, subModel, owner, elementIndex);
2914 }
else if (!model) {
2919 const bool isTranslationBinding = binding->isTranslationBinding();
2920 if (isTranslationBinding) {
2921 value = QVariant::fromValue<
const QV4::CompiledData::Binding*>(binding);
2922 }
else if (binding->evaluatesToString()) {
2923 value = compilationUnit->bindingValueAsString(binding);
2924 }
else if (bindingType == QV4::CompiledData::Binding::Type_Number) {
2925 value = compilationUnit->bindingValueAsNumber(binding);
2926 }
else if (bindingType == QV4::CompiledData::Binding::Type_Boolean) {
2927 value = binding->valueAsBoolean();
2928 }
else if (bindingType == QV4::CompiledData::Binding::Type_Null) {
2929 value = QVariant::fromValue(
nullptr);
2930 }
else if (bindingType == QV4::CompiledData::Binding::Type_Script) {
2931 QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
2932 if (definesEmptyList(scriptStr)) {
2933 value = QVariant::fromValue(
2934 new ListModel(model->getOrCreateListRole(elementName).subLayout,
nullptr));
2935 }
else if (binding->isFunctionExpression()) {
2936 QQmlBinding::Identifier id = binding->value.compiledScriptIndex;
2937 Q_ASSERT(id != QQmlBinding::Invalid);
2939 auto v4 = compilationUnit->engine;
2940 QV4::Scope scope(v4);
2942 if (model->m_modelCache ==
nullptr) {
2943 model->m_modelCache =
new QQmlListModel(owner, model, v4);
2944 QQmlEngine::setContextForObject(
2945 model->m_modelCache, QQmlEngine::contextForObject(owner));
2949 QV4::ScopedContext context(scope, QV4::QmlContext::create(v4->rootContext(), QQmlContextData::get(qmlContext(model->m_modelCache)),
nullptr));
2950 QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(context, compilationUnit->runtimeFunctions[id]));
2953 QV4::ScopedValue result(scope, function->call(v4->globalObject,
nullptr, 0));
2954 if (v4->hasException)
2955 v4->catchException();
2957 QJSValuePrivate::setValue(&v, result);
2961 value = evaluateEnum(scriptStr, &ok);
2967 model->setOrCreateProperty(outterElementIndex, elementName, value);
2968 auto listModel = model->m_modelCache;
2969 if (isTranslationBinding && listModel) {
2970 if (!listModel->translationChangeHandler) {
2971 auto ep = QQmlEnginePrivate::get(compilationUnit->engine);
2972 model->m_modelCache->translationChangeHandler = std::make_unique<QPropertyNotifier>(
2973 ep->translationLanguage.addNotifier([listModel](){ listModel->updateTranslations(); }));
2982 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
2983 const QList<
const QV4::CompiledData::Binding *> &bindings)
2985 listElementTypeName = QString();
2987 for (
const QV4::CompiledData::Binding *binding : bindings) {
2988 QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
2989 if (!propName.isEmpty()) {
2990 error(binding, QQmlListModel::tr(
"ListModel: undefined property '%1'").arg(propName));
2993 if (!verifyProperty(compilationUnit, binding))
2999 QObject *obj,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
3000 const QList<
const QV4::CompiledData::Binding *> &bindings)
3002 QQmlListModel *rv =
static_cast<QQmlListModel *>(obj);
3004 rv->m_engine = qmlEngine(rv)->handle();
3005 rv->m_compilationUnit = compilationUnit;
3007 bool setRoles =
false;
3009 for (
const QV4::CompiledData::Binding *binding : bindings) {
3010 if (binding->type() != QV4::CompiledData::Binding::Type_Object)
3012 setRoles |= applyProperty(
3013 compilationUnit, binding, rv->m_listModel, rv, -1);
3016 if (setRoles ==
false)
3017 qmlWarning(obj) <<
"All ListElement declarations are empty, no roles can be created unless dynamicRoles is set.";
3022 if (s.startsWith(QLatin1Char(
'[')) && s.endsWith(QLatin1Char(
']'))) {
3023 for (
auto c : s.sliced(1).chopped(1)) {
3034
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
3082#include "moc_qqmllistmodel_p_p.cpp"
3084#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)