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>
44enum { MIN_LISTMODEL_UID = 1024 };
51 for (size_t i=0 ; i <
sizeof(T) ; ++i) {
61 static const QString roleTypeNames[] = {
62 QStringLiteral(
"String"), QStringLiteral(
"Number"), QStringLiteral(
"Bool"),
63 QStringLiteral(
"List"), QStringLiteral(
"QObject"), QStringLiteral(
"VariantMap"),
64 QStringLiteral(
"DateTime"), QStringLiteral(
"Url"), QStringLiteral(
"Function")
68 return roleTypeNames[t];
75 QStringHash<Role *>::Node *node = roleHash.findNode(key);
77 const Role &r = *node->value;
79 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));
83 return createRole(key, type);
88 QStringHash<Role *>::Node *node = roleHash.findNode(key);
90 const Role &r = *node->value;
92 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));
96 QString qkey = key->toQString();
98 return createRole(qkey, type);
103 const int dataSizes[] = {
104 sizeof(StringOrTranslation),
108 sizeof(QV4::PersistentValue),
114 const int dataAlignments[] = {
115 alignof(StringOrTranslation),
118 alignof(ListModel *),
119 alignof(QV4::PersistentValue),
120 alignof(QVariantMap),
136 int dataSize = dataSizes[type];
137 int dataAlignment = dataAlignments[type];
139 int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1);
143 currentBlockOffset = dataSize;
147 currentBlockOffset = dataOffset + dataSize;
150 int roleIndex = roles.size();
154 roleHash.insert(key, r);
161 const int otherRolesCount = other->roles.size();
162 roles.reserve(otherRolesCount);
163 for (
int i=0 ; i < otherRolesCount; ++i) {
164 Role *role =
new Role(other->roles[i]);
166 roleHash.insert(role->name, role);
168 currentBlockOffset = other->currentBlockOffset;
169 currentBlock = other->currentBlock;
179 int roleOffset = target->roles.size();
180 int newRoleCount = src->roles.size() - roleOffset;
182 for (
int i=0 ; i < newRoleCount ; ++i) {
183 Role *role =
new Role(src->roles[roleOffset + i]);
184 target->roles.append(role);
185 target->roleHash.insert(role->name, role);
188 target->currentBlockOffset = src->currentBlockOffset;
189 target->currentBlock = src->currentBlock;
214 switch (data.userType()) {
217 case QMetaType::Bool: type =
Role::Bool;
break;
221 case QMetaType::QUrl: type =
Role::Url;
break;
223 if (data.userType() == qMetaTypeId<QJSValue>() &&
224 data.value<QJSValue>().isCallable()) {
227 }
else if (data.userType() == qMetaTypeId<
const QV4::CompiledData::Binding*>()
228 && data.value<
const QV4::CompiledData::Binding*>()->isTranslationBinding()) {
231 }
else if (data.userType() >= QMetaType::User) {
242 qmlWarning(
nullptr) <<
"Can't create role for unsupported data type";
246 return &getRoleOrCreate(key, type);
252 QStringHash<Role *>::Node *node = roleHash.findNode(key);
261 QStringHash<Role *>::Node *node = roleHash.findNode(key);
277 QString::DataPointer dataPointer = s.data_ptr();
278 arrayData = dataPointer.d_ptr();
279 stringData = dataPointer.data();
280 stringSize = dataPointer.size;
288 this->binding = binding;
296 return QString(QStringPrivate(arrayData, stringData, stringSize));
300 return owner->m_compilationUnit->bindingValueAsString(binding);
308 return QString(QStringPrivate(arrayData, stringData, stringSize));
313 if (arrayData && !arrayData->deref())
314 QTypedArrayData<ushort>::deallocate(arrayData);
316 stringData =
nullptr;
324 if (e->m_objectCache ==
nullptr) {
325 void *memory = operator
new(
sizeof(QObject) +
sizeof(QQmlData));
326 void *ddataMemory = ((
char *)memory) +
sizeof(
QObject);
327 e->m_objectCache =
new (memory) QObject;
329 const QAbstractDeclarativeData *old = std::exchange(
330 QObjectPrivate::get(e->m_objectCache)->declarativeData,
331 new (ddataMemory) QQmlData(QQmlData::DoesNotOwnMemory));
336 return e->m_objectCache;
343 bool hasChanges =
false;
346 QHash<
int, ElementSync> elementHash;
349 int uid = e->getUid();
352 sync.targetIndex = i;
353 elementHash.insert(uid, sync);
357 int uid = e->getUid();
359 QHash<
int, ElementSync>::iterator it = elementHash.find(uid);
360 if (it == elementHash.end()) {
364 elementHash.insert(uid, sync);
366 ElementSync &sync = it.value();
372 QQmlListModel *targetModel = target->m_modelCache;
378 ElementSync &s = elementHash.find(element->getUid()).value();
379 Q_ASSERT(s.targetIndex >= 0);
381 s.targetIndex -= rowsRemoved;
382 if (s.src ==
nullptr) {
383 Q_ASSERT(s.targetIndex == i);
386 targetModel->beginRemoveRows(QModelIndex(), i, i);
387 s.target->destroy(target->m_layout);
388 if (
auto it =
std::find(target->elements.begin(), target->elements.end(), s.target);
389 it != target->elements.end()) {
390 target->elements.erase(it);
394 targetModel->endRemoveRows();
405 target->elements.clear();
408 ElementSync &s = elementHash.find(srcElement->getUid()).value();
409 Q_ASSERT(s.srcIndex >= 0);
411 if (targetElement ==
nullptr) {
414 s.changedRoles = ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout);
415 target->elements.push_back(targetElement);
418 target->updateCacheIndices();
421 for (ListElement *e : target->elements) {
422 if (ModelNodeMetaObject *mo = e->objectCache())
435 for (
int i = 0 ; i < targetElementCount ; ++i) {
437 ElementSync &s = elementHash.find(element->getUid()).value();
438 Q_ASSERT(s.srcIndex >= 0);
439 const bool inserted = s.targetIndex == -1;
440 if (s.targetIndex != i) {
442 const int oldIndex = s.targetIndex;
445 targetModel->beginInsertRows(QModelIndex(), i, i);
446 targetModel->endInsertRows();
448 Q_ASSERT(oldIndex > i);
449 targetModel->beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), i);
450 targetModel->endMoveRows();
453 for (
int j = i + 1; j < targetElementCount; ++j) {
454 ElementSync &sToFix = elementHash.find(target->elements[j]->getUid()).value();
455 if (sToFix.targetIndex != -1 && (inserted || sToFix.targetIndex < oldIndex))
456 sToFix.targetIndex += 1;
461 if (!inserted && !s.changedRoles.isEmpty()) {
462 QModelIndex idx = targetModel->createIndex(i, 0);
464 targetModel->dataChanged(idx, idx, s.changedRoles);
477 for (
const auto &destroyer : remove(0, elementCount()))
481 if (m_modelCache && m_modelCache->m_primary ==
false)
483 m_modelCache =
nullptr;
489 newElement(elementIndex);
496 updateCacheIndices(index);
512 std::rotate(elements.begin() + from,
513 elements.begin() + from + n,
514 elements.begin() + to + n);
516 updateCacheIndices(from, to + n);
522 elements.insert(elements.begin() + index, e);
525void ListModel::updateCacheIndices(
int start,
int end)
529 if (end < 0 || end > count)
532 for (
int i = start; i < end; ++i) {
545 return e->getProperty(r, owner, eng);
551 return e->getListProperty(role);
556 for (ListElement *e : elements) {
557 if (ModelNodeMetaObject *cache = e->objectCache()) {
559 cache->updateValues();
564void ListModel::
set(
int elementIndex, QV4::Object *object, QList<
int> *roles)
568 QV4::ExecutionEngine *v4 = object->engine();
569 QV4::Scope scope(v4);
570 QV4::ScopedObject o(scope);
572 QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
573 QV4::ScopedString propertyName(scope);
574 QV4::ScopedValue propertyValue(scope);
575 QV4::ScopedFunctionObject func(scope);
576 QV4::ScopedObject obj(scope);
578 propertyName = it.nextPropertyNameAsString(propertyValue);
586 if (
const QV4::String *s = propertyValue->as<QV4::String>()) {
588 roleIndex = e->setStringProperty(r, s->toQString());
589 }
else if (propertyValue->isNumber()) {
591 roleIndex = e->setDoubleProperty(r, propertyValue->asDouble());
592 }
else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) {
593 roleIndex = setArrayLike(&o, propertyName, e, a);
594 }
else if (QV4::Sequence *s = propertyValue->as<QV4::Sequence>()) {
595 roleIndex = setArrayLike(&o, propertyName, e, s);
596 }
else if (QV4::QmlListWrapper *l = propertyValue->as<QV4::QmlListWrapper>()) {
597 roleIndex = setArrayLike(&o, propertyName, e, l);
598 }
else if (propertyValue->isBoolean()) {
600 roleIndex = e->setBoolProperty(r, propertyValue->booleanValue());
601 }
else if (QV4::DateObject *dd = propertyValue->as<QV4::DateObject>()) {
603 QDateTime dt = dd->toQDateTime();
604 roleIndex = e->setDateTimeProperty(r, dt);
605 }
else if (QV4::UrlObject *url = propertyValue->as<QV4::UrlObject>()) {
607 QUrl qurl = QUrl(url->href());
608 roleIndex = e->setUrlProperty(r, qurl);
609 }
else if (QV4::FunctionObject *f = propertyValue->as<QV4::FunctionObject>()) {
613 QJSValuePrivate::setValue(&jsv, func);
614 roleIndex = e->setFunctionProperty(r, jsv);
615 }
else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
616 if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) {
619 roleIndex = e->setQObjectProperty(role, wrapper);
620 }
else if (QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
622 QV4::Value::fromReturnedValue(o->asReturnedValue()),
623 QMetaType::fromType<QUrl>(),
true);
624 maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
626 QUrl qurl = maybeUrl.toUrl();
627 roleIndex = e->setUrlProperty(r, qurl);
632 roleIndex = e->setVariantMapProperty(role, obj);
635 }
else if (propertyValue->isNullOrUndefined()) {
638 e->clearProperty(*r);
642 roles->append(roleIndex);
656 QV4::ExecutionEngine *v4 = object->engine();
657 QV4::Scope scope(v4);
659 QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
660 QV4::ScopedString propertyName(scope);
661 QV4::ScopedValue propertyValue(scope);
662 QV4::ScopedObject o(scope);
664 propertyName = it.nextPropertyNameAsString(propertyValue);
669 if (QV4::String *s = propertyValue->stringValue()) {
672 e->setStringPropertyFast(r, s->toQString());
673 }
else if (propertyValue->isNumber()) {
676 e->setDoublePropertyFast(r, propertyValue->asDouble());
678 }
else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) {
679 setArrayLikeFast(&o, propertyName, e, a);
680 }
else if (QV4::Sequence *s = propertyValue->as<QV4::Sequence>()) {
681 setArrayLikeFast(&o, propertyName, e, s);
682 }
else if (QV4::QmlListWrapper *l = propertyValue->as<QV4::QmlListWrapper>()) {
683 setArrayLikeFast(&o, propertyName, e, l);
684 }
else if (propertyValue->isBoolean()) {
687 e->setBoolPropertyFast(r, propertyValue->booleanValue());
689 }
else if (QV4::DateObject *date = propertyValue->as<QV4::DateObject>()) {
692 QDateTime dt = date->toQDateTime();
693 e->setDateTimePropertyFast(r, dt);
695 }
else if (QV4::UrlObject *url = propertyValue->as<QV4::UrlObject>()){
698 QUrl qurl = QUrl(url->href());
699 e->setUrlPropertyFast(r, qurl);
701 }
else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
702 if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) {
705 e->setQObjectPropertyFast(r, wrapper);
707 QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
709 QV4::Value::fromReturnedValue(o->asReturnedValue()),
710 QMetaType::fromType<QUrl>(),
true);
711 if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
712 const QUrl qurl = maybeUrl.toUrl();
715 e->setUrlPropertyFast(r, qurl);
719 e->setVariantMapFast(role, o);
722 }
else if (propertyValue->isNullOrUndefined()) {
725 auto memberName = propertyName->toString(v4)->toQString();
726 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")));
727 qmlWarning(
nullptr, err);
731 e->clearProperty(*r);
737QList<std::function<
void()>> ListModel::remove(
int index,
int count)
739 QList<std::function<
void()>> toDestroy;
740 auto layout = m_layout;
741 for (
int i=0 ; i < count ; ++i) {
742 auto element = elements[index+i];
743 toDestroy.append([element, layout](){
744 element->destroy(layout);
748 elements.erase(elements.begin() + index, elements.begin() + index + count);
749 updateCacheIndices(index);
756 set(elementIndex, object, SetElement::WasJustInserted);
762 set(elementIndex, object, SetElement::WasJustInserted);
766int ListModel::setOrCreateProperty(
int elementIndex,
const QString &key,
const QVariant &data)
775 roleIndex = e->setVariantProperty(*r, data);
779 if (roleIndex != -1 && cache)
787int ListModel::setExistingProperty(
int elementIndex,
const QString &key,
const QV4::Value &data, QV4::ExecutionEngine *eng)
795 roleIndex = e->setJsProperty(*r, data, eng);
806 if (e->next ==
nullptr) {
822 return ModelNodeMetaObject::get(m_objectCache);
827 char *mem = getPropertyMemory(role);
834 char *mem = getPropertyMemory(role);
835 QV4::PersistentValue *g =
reinterpret_cast<QV4::PersistentValue *>(mem);
836 return g->as<QV4::QObjectWrapper>();
841 QVariantMap *map =
nullptr;
843 char *mem = getPropertyMemory(role);
844 if (isMemoryUsed<QVariantMap>(mem))
845 map =
reinterpret_cast<QVariantMap *>(mem);
852 QDateTime *dt =
nullptr;
854 char *mem = getPropertyMemory(role);
855 if (isMemoryUsed<QDateTime>(mem))
856 dt =
reinterpret_cast<QDateTime *>(mem);
865 char *mem = getPropertyMemory(role);
866 if (isMemoryUsed<QUrl>(mem))
867 url =
reinterpret_cast<QUrl *>(mem);
874 QJSValue *f =
nullptr;
876 char *mem = getPropertyMemory(role);
877 if (isMemoryUsed<QJSValue>(mem))
878 f =
reinterpret_cast<QJSValue *>(mem);
883QV4::PersistentValue *
886 char *mem = getPropertyMemory(role);
888 bool existingGuard =
false;
889 for (size_t i = 0; i <
sizeof(QV4::PersistentValue);
892 existingGuard =
true;
897 QV4::PersistentValue *g =
nullptr;
900 g =
reinterpret_cast<QV4::PersistentValue *>(mem);
907 char *mem = getPropertyMemory(role);
914 char *mem = getPropertyMemory(role);
921 double *value =
reinterpret_cast<
double *>(mem);
929 data = value->toString(owner);
936 bool *value =
reinterpret_cast<
bool *>(mem);
946 if (model->m_modelCache ==
nullptr) {
947 model->m_modelCache =
new QQmlListModel(owner, model, eng);
948 QQmlEngine::setContextForObject(model->m_modelCache, QQmlEngine::contextForObject(owner));
951 QObject *object = model->m_modelCache;
952 data = QVariant::fromValue(object);
958 QV4::PersistentValue *guard =
reinterpret_cast<QV4::PersistentValue *>(mem);
959 data = QVariant::fromValue(guard->as<QV4::QObjectWrapper>()->object());
964 if (isMemoryUsed<QVariantMap>(mem)) {
965 QVariantMap *map =
reinterpret_cast<QVariantMap *>(mem);
972 if (isMemoryUsed<QDateTime>(mem)) {
973 QDateTime *dt =
reinterpret_cast<QDateTime *>(mem);
980 if (isMemoryUsed<QUrl>(mem)) {
981 QUrl *url =
reinterpret_cast<QUrl *>(mem);
988 if (isMemoryUsed<QJSValue>(mem)) {
989 QJSValue *func =
reinterpret_cast<QJSValue *>(mem);
990 data = QVariant::fromValue(*func);
1006 char *mem = getPropertyMemory(role);
1012 changed = c->asString().compare(s) != 0;
1026 char *mem = getPropertyMemory(role);
1027 double *value =
reinterpret_cast<
double *>(mem);
1028 bool changed = *value != d;
1042 char *mem = getPropertyMemory(role);
1043 bool *value =
reinterpret_cast<
bool *>(mem);
1044 bool changed = *value != b;
1058 char *mem = getPropertyMemory(role);
1060 if (*value && *value != m) {
1076 char *mem = getPropertyMemory(role);
1077 if (isMemoryUsed<QVariantMap>(mem))
1078 reinterpret_cast<QV4::PersistentValue *>(mem)->set(o->engine(), *o);
1080 new (mem) QV4::PersistentValue(o->engine(), o);
1092 char *mem = getPropertyMemory(role);
1093 if (isMemoryUsed<QVariantMap>(mem)) {
1094 QVariantMap *map =
reinterpret_cast<QVariantMap *>(mem);
1097 new (mem) QVariantMap(o->engine()->variantMapFromJS(o));
1109 char *mem = getPropertyMemory(role);
1110 if (isMemoryUsed<QVariantMap>(mem)) {
1111 QVariantMap *map =
reinterpret_cast<QVariantMap *>(mem);
1112 if (m && map->isSharedWith(*m))
1119 new (mem) QVariantMap(*m);
1121 new (mem) QVariantMap;
1133 char *mem = getPropertyMemory(role);
1134 if (isMemoryUsed<QDateTime>(mem)) {
1135 QDateTime *dt =
reinterpret_cast<QDateTime *>(mem);
1138 new (mem) QDateTime(dt);
1150 char *mem = getPropertyMemory(role);
1151 if (isMemoryUsed<QUrl>(mem)) {
1152 QUrl *qurl =
reinterpret_cast<QUrl *>(mem);
1155 new (mem) QUrl(url);
1167 char *mem = getPropertyMemory(role);
1168 if (isMemoryUsed<QJSValue>(mem)) {
1169 QJSValue *f =
reinterpret_cast<QJSValue *>(mem);
1172 new (mem) QJSValue(f);
1184 char *mem = getPropertyMemory(role);
1186 s->setTranslation(b);
1196 char *mem = getPropertyMemory(role);
1202 char *mem = getPropertyMemory(role);
1203 double *value =
new (mem)
double;
1209 char *mem = getPropertyMemory(role);
1210 bool *value =
new (mem)
bool;
1216 char *mem = getPropertyMemory(role);
1217 new (mem) QV4::PersistentValue(o->engine(), o);
1222 char *mem = getPropertyMemory(role);
1229 char *mem = getPropertyMemory(role);
1230 QVariantMap *map =
new (mem) QVariantMap;
1231 *map = o->engine()->variantMapFromJS(o);
1236 char *mem = getPropertyMemory(role);
1237 new (mem) QDateTime(dt);
1242 char *mem = getPropertyMemory(role);
1243 new (mem) QUrl(url);
1248 char *mem = getPropertyMemory(role);
1249 new (mem) QJSValue(f);
1254 switch (role
.type) {
1255 case ListLayout::Role::String:
1256 setStringProperty(role, QString());
1259 setDoubleProperty(role, 0.0);
1262 setBoolProperty(role,
false);
1265 setListProperty(role,
nullptr);
1267 case ListLayout::Role::QObject:
1268 setQObjectProperty(role,
nullptr);
1270 case ListLayout::Role::DateTime:
1271 setDateTimeProperty(role, QDateTime());
1273 case ListLayout::Role::Url:
1274 setUrlProperty(role, QUrl());
1276 case ListLayout::Role::VariantMap:
1277 setVariantMapProperty(role, (QVariantMap *)
nullptr);
1280 setFunctionProperty(role, QJSValue());
1289 m_objectCache =
nullptr;
1290 uid = uidCounter.fetchAndAddOrdered(1);
1292 memset(data, 0,
sizeof(data));
1297 m_objectCache =
nullptr;
1300 memset(data, 0,
sizeof(data));
1310 QList<
int> changedRoles;
1316 switch (srcRole
.type) {
1319 ListModel *srcSubModel = src->getListProperty(srcRole);
1320 ListModel *targetSubModel = target->getListProperty(targetRole);
1323 if (targetSubModel ==
nullptr) {
1324 targetSubModel =
new ListModel(targetRole.subLayout,
nullptr);
1325 target->setListPropertyFast(targetRole, targetSubModel);
1328 roleIndex = targetRole
.index;
1334 QV4::QObjectWrapper *object = src->getQObjectProperty(srcRole);
1335 roleIndex = target->setQObjectProperty(targetRole, object);
1344 QVariant v = src->getProperty(srcRole,
nullptr,
nullptr);
1345 roleIndex = target->setVariantProperty(targetRole, v);
1350 QVariantMap *map = src->getVariantMapProperty(srcRole);
1351 roleIndex = target->setVariantMapProperty(targetRole, map);
1358 changedRoles << roleIndex;
1361 return changedRoles;
1389 if (QV4::PersistentValue *guard = getGuardProperty(r))
1390 guard->~PersistentValue();
1395 QVariantMap *map = getVariantMapProperty(r);
1402 QDateTime *dt = getDateTimeProperty(r);
1409 QUrl *url = getUrlProperty(r);
1416 QJSValue *f = getFunctionProperty(r);
1427 if (m_objectCache) {
1428 m_objectCache->~QObject();
1429 operator
delete(m_objectCache);
1434 next->destroy(
nullptr);
1442 switch (role
.type) {
1444 roleIndex = setDoubleProperty(role, d.toDouble());
1447 if (d.userType() == qMetaTypeId<
const QV4::CompiledData::Binding *>())
1448 roleIndex = setTranslationProperty(role, d.value<
const QV4::CompiledData::Binding*>());
1450 roleIndex = setStringProperty(role, d.toString());
1453 roleIndex = setBoolProperty(role, d.toBool());
1456 roleIndex = setListProperty(role, d.value<
ListModel *>());
1459 QVariantMap map = d.toMap();
1460 roleIndex = setVariantMapProperty(role, &map);
1464 roleIndex = setDateTimeProperty(role, d.toDateTime());
1467 roleIndex = setUrlProperty(role, d.toUrl());
1470 roleIndex = setFunctionProperty(role, d.value<QJSValue>());
1484 QV4::Scope scope(eng);
1488 QString qstr = d.toQString();
1489 roleIndex = setStringProperty(role, qstr);
1490 }
else if (d.isNumber()) {
1491 roleIndex = setDoubleProperty(role, d.asDouble());
1492 }
else if (d.as<QV4::ArrayObject>()) {
1493 QV4::ScopedArrayObject a(scope, d);
1495 QV4::Scope scope(a->engine());
1496 QV4::ScopedObject o(scope);
1498 ListModel *subModel =
new ListModel(role.subLayout,
nullptr);
1499 int arrayLength = a->getLength();
1500 for (
int j=0 ; j < arrayLength ; ++j) {
1502 subModel->append(o);
1504 roleIndex = setListProperty(role, subModel);
1506 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));
1508 }
else if (d.isBoolean()) {
1509 roleIndex = setBoolProperty(role, d.booleanValue());
1510 }
else if (d.as<QV4::DateObject>()) {
1511 QV4::Scoped<QV4::DateObject> dd(scope, d);
1512 QDateTime dt = dd->toQDateTime();
1513 roleIndex = setDateTimeProperty(role, dt);
1514 }
else if (d.as<QV4::UrlObject>()) {
1515 QV4::Scoped<QV4::UrlObject> url(scope, d);
1516 QUrl qurl = QUrl(url->href());
1517 roleIndex = setUrlProperty(role, qurl);
1518 }
else if (d.as<QV4::FunctionObject>()) {
1519 QV4::ScopedFunctionObject f(scope, d);
1521 QJSValuePrivate::setValue(&jsv, f);
1522 roleIndex = setFunctionProperty(role, jsv);
1523 }
else if (d.isObject()) {
1524 QV4::ScopedObject o(scope, d);
1525 QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>();
1527 roleIndex = setQObjectProperty(role, wrapper);
1529 roleIndex = setVariantMapProperty(role, o);
1531 QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
1533 QV4::Value::fromReturnedValue(o.asReturnedValue()),
1534 QMetaType::fromType<QUrl>(),
true);
1535 if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
1536 roleIndex = setUrlProperty(role, maybeUrl.toUrl());
1539 }
else if (d.isNullOrUndefined()) {
1540 clearProperty(role);
1552 const int roleCount = m_model->m_listModel->roleCount();
1553 QList<QByteArray> properties;
1554 properties.reserve(roleCount);
1555 for (
int i = 0 ; i < roleCount ; ++i) {
1556 const ListLayout::
Role &role = m_model->m_listModel->getExistingRole(i);
1557 QByteArray name = role.name.toUtf8();
1560 type()->createProperties(properties);
1569#if QT_VERSION >= QT_VERSION_CHECK(7
, 0
, 0
)
1570const QMetaObject *ModelNodeMetaObject::toDynamicMetaObject(QObject *object)
const
1575 if (!m_initialized) {
1576 m_initialized =
true;
1577 const_cast<ModelNodeMetaObject *>(
this)->initialize();
1579 return QQmlOpenMetaObject::toDynamicMetaObject(object);
1584 QObjectPrivate *op = QObjectPrivate::get(obj);
1585 return static_cast<ModelNodeMetaObject*>(op->metaObject);
1591 const int roleCount = m_model->m_listModel->roleCount();
1592 if (!m_initialized) {
1594 Q_ALLOCA_VAR(
int, changedRoles, roleCount *
sizeof(
int));
1595 for (
int i = 0; i < roleCount; ++i)
1596 changedRoles[i] = i;
1597 emitDirectNotifies(changedRoles, roleCount);
1601 for (
int i=0 ; i < roleCount ; ++i) {
1602 const ListLayout::
Role &role = m_model->m_listModel->getExistingRole(i);
1603 QByteArray name = role.name.toUtf8();
1604 const QVariant &data = m_model->data(m_elementIndex, i);
1611 if (!m_initialized) {
1612 emitDirectNotifies(roles.constData(), roles.size());
1615 int roleCount = roles.size();
1616 for (
int i=0 ; i < roleCount ; ++i) {
1617 int roleIndex = roles.at(i);
1618 const ListLayout::
Role &role = m_model->m_listModel->getExistingRole(roleIndex);
1619 QByteArray name = role.name.toUtf8();
1620 const QVariant &data = m_model->data(m_elementIndex, roleIndex);
1630 QString propName = QString::fromUtf8(name(index));
1631 const QVariant value =
this->value(index);
1633 QV4::Scope scope(m_model->engine());
1634 QV4::ScopedValue v(scope, scope.engine->fromVariant(value));
1636 int roleIndex = m_model->m_listModel->setExistingProperty(m_elementIndex, propName, v, scope.engine);
1637 if (roleIndex != -1)
1638 m_model->emitItemsChanged(m_elementIndex, 1, QList<
int>(1, roleIndex));
1644 Q_ASSERT(!m_initialized);
1645 QQmlData *ddata = QQmlData::get(object(),
false);
1649 if (!qmlEngine(m_model))
1651 for (
int i = 0; i < roleCount; ++i) {
1652 const int changedRole = changedRoles[i];
1653 QQmlNotifier::notify(ddata, changedRole);
1659bool ModelObject::virtualPut(Managed *m, PropertyKey id,
const Value &value, Value *receiver)
1662 return Object::virtualPut(m, id, value, receiver);
1663 QString propName = id.toQString();
1666 if (!that->object())
1669 ExecutionEngine *eng = that->engine();
1670 const int elementIndex = that->d()->elementIndex();
1671 if (QQmlListModel *model = that->d()->m_model) {
1673 = model->listModel()->setExistingProperty(elementIndex, propName, value, eng);
1674 if (roleIndex != -1)
1675 model->emitItemsChanged(elementIndex, 1, QList<
int>(1, roleIndex));
1680 mo->emitPropertyNotification(propName.toUtf8());
1684ReturnedValue
ModelObject::virtualGet(
const Managed *m, PropertyKey id,
const Value *receiver,
bool *hasProperty)
1687 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1691 ScopedString name(scope, id.asStringOrSymbol());
1692 QQmlListModel *model = that->d()->m_model;
1693 if (!model || !that->object())
1694 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1696 const ListLayout::
Role *role = model->listModel()->getExistingRole(name);
1698 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1700 *hasProperty =
true;
1702 if (QQmlEngine *qmlEngine = that->engine()->qmlEngine()) {
1703 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine);
1704 if (ep && ep->propertyCapture)
1705 ep->propertyCapture->captureProperty(that->object(), -1, role
->index,
false);
1708 const int elementIndex = that->d()->elementIndex();
1709 QVariant value = model->data(elementIndex, role
->index);
1710 return that->engine()->fromVariant(value);
1713ReturnedValue
ModelObject::virtualResolveLookupGetter(
const Object *object, ExecutionEngine *engine, Lookup *lookup)
1715 lookup->call = Lookup::Call::GetterQObjectPropertyFallback;
1716 return lookup->getter(engine, *object);
1723 PropertyKey next(
const Object *o, Property *pd =
nullptr, PropertyAttributes *attrs =
nullptr)
override;
1731 ExecutionEngine *v4 = that->engine();
1733 QQmlListModel *model = that->d()->m_model;
1734 ListModel *listModel = model ? model->listModel() :
nullptr;
1736 Scope scope(that->engine());
1739 ScopedString roleName(scope, v4->newString(role.name));
1741 *attrs = QV4::Attr_Data;
1744 QVariant value = model->data(that->d()->elementIndex(), role
.index);
1745 if (
auto recursiveListModel = qvariant_cast<QQmlListModel*>(value)) {
1746 auto size = recursiveListModel->count();
1747 auto array = ScopedArrayObject{scope, v4->newArrayObject(size)};
1748 QV4::ScopedValue val(scope);
1749 for (
auto i = 0; i < size; i++) {
1750 val = QJSValuePrivate::convertToReturnedValue(
1751 v4, recursiveListModel->get(i));
1752 array->arrayPut(i, val);
1756 pd->value = v4->fromVariant(value);
1759 return roleName->toPropertyKey();
1765 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
1768OwnPropertyKeyIterator *
ModelObject::virtualOwnPropertyKeys(
const Object *m, Value *target)
1787 object->updateValues(obj, roles);
1793 QList<
int> changedRoles;
1794 for (
int i = 0; i < src->m_meta->count(); ++i) {
1795 const QByteArray &name = src->m_meta->name(i);
1796 QVariant value = src->m_meta->value(i);
1798 QQmlListModel *srcModel = qobject_cast<QQmlListModel *>(value.value<QObject *>());
1799 QQmlListModel *targetModel = qobject_cast<QQmlListModel *>(target->m_meta->value(i).value<QObject *>());
1801 bool modelHasChanges =
false;
1803 if (targetModel ==
nullptr)
1804 targetModel = QQmlListModel::createWithOwner(target->m_owner);
1806 modelHasChanges = QQmlListModel::sync(srcModel, targetModel);
1808 QObject *targetModelObject = targetModel;
1809 value = QVariant::fromValue(targetModelObject);
1810 }
else if (targetModel) {
1814 if (target->setValue(name, value) || modelHasChanges)
1815 changedRoles << target->m_owner->m_roles.indexOf(QString::fromUtf8(name));
1817 return changedRoles;
1822 for (
auto it = object.cbegin(), end = object.cend(); it != end; ++it) {
1823 const QString &key = it.key();
1825 int roleIndex = m_owner->m_roles.indexOf(key);
1826 if (roleIndex == -1) {
1827 roleIndex = m_owner->m_roles.size();
1828 m_owner->m_roles.append(key);
1831 QVariant value = it.value();
1835 if (value.userType() == qMetaTypeId<QJSValue>())
1836 value = value.value<QJSValue>().toVariant();
1838 if (value.userType() == QMetaType::QVariantList) {
1839 QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner);
1841 QVariantList subArray = value.toList();
1842 QVariantList::const_iterator subIt = subArray.cbegin();
1843 QVariantList::const_iterator subEnd = subArray.cend();
1844 while (subIt != subEnd) {
1845 const QVariantMap &subObject = subIt->toMap();
1846 subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1850 QObject *subModelObject = subModel;
1851 value = QVariant::fromValue(subModelObject);
1854 const QByteArray &keyUtf8 = key.toUtf8();
1856 QQmlListModel *existingModel = qobject_cast<QQmlListModel *>(m_meta->value(keyUtf8).value<QObject *>());
1857 delete existingModel;
1859 if (m_meta->setValue(keyUtf8, value))
1871 for (
int i=0 ; i < count() ; ++i) {
1872 QQmlListModel *subModel = qobject_cast<QQmlListModel *>(value(i).value<QObject *>());
1882 QVariant v = value(index);
1883 QQmlListModel *model = qobject_cast<QQmlListModel *>(v.value<QObject *>());
1892 QQmlListModel *parentModel = m_owner->m_owner;
1894 QVariant v = value(index);
1898 if (v.userType() == qMetaTypeId<QJSValue>())
1899 v= v.value<QJSValue>().toVariant();
1901 if (v.userType() == QMetaType::QVariantList) {
1902 QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel);
1904 QVariantList subArray = v.toList();
1905 QVariantList::const_iterator subIt = subArray.cbegin();
1906 QVariantList::const_iterator subEnd = subArray.cend();
1907 while (subIt != subEnd) {
1908 const QVariantMap &subObject = subIt->toMap();
1909 subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1913 QObject *subModelObject = subModel;
1914 v = QVariant::fromValue(subModelObject);
1919 int elementIndex = parentModel->m_modelObjects.indexOf(m_owner);
1920 if (elementIndex != -1) {
1921 int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData()));
1922 if (roleIndex != -1)
1923 parentModel->emitItemsChanged(elementIndex, 1, QList<
int>(1, roleIndex));
1928
1929
1930
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
2028QQmlListModel::QQmlListModel(QObject *parent)
2029: QAbstractListModel(parent)
2031 m_mainThread =
true;
2034 m_dynamicRoles =
false;
2036 m_layout =
new ListLayout;
2037 m_listModel =
new ListModel(m_layout,
this);
2042QQmlListModel::QQmlListModel(
const QQmlListModel *owner, ListModel *data, QV4::ExecutionEngine *engine, QObject *parent)
2043: QAbstractListModel(parent)
2045 m_mainThread = owner->m_mainThread;
2047 m_agent = owner->m_agent;
2049 Q_ASSERT(owner->m_dynamicRoles ==
false);
2050 m_dynamicRoles =
false;
2055 m_compilationUnit = owner->m_compilationUnit;
2058QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent)
2059: QAbstractListModel(agent)
2061 m_mainThread =
false;
2064 m_dynamicRoles = orig->m_dynamicRoles;
2066 if (ListLayout *layout = orig->m_layout)
2067 m_layout =
new ListLayout(layout);
2069 m_layout =
new ListLayout;
2071 m_listModel =
new ListModel(m_layout,
this);
2076 ListModel::sync(orig->m_listModel, m_listModel);
2079 m_compilationUnit = orig->m_compilationUnit;
2082QQmlListModel::~QQmlListModel()
2084 qDeleteAll(m_modelObjects);
2087 m_listModel->destroy();
2090 if (m_mainThread && m_agent)
2091 m_agent->modelDestroyed();
2094 if (m_agent && m_ownAgent)
2097 m_listModel =
nullptr;
2103QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner)
2105 QQmlListModel *model =
new QQmlListModel;
2107 model->m_mainThread = newOwner->m_mainThread;
2108 model->m_engine = newOwner->m_engine;
2109 model->m_agent = newOwner->m_agent;
2110 model->m_dynamicRoles = newOwner->m_dynamicRoles;
2112 QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner));
2117QV4::ExecutionEngine *QQmlListModel::engine()
const
2119 if (m_engine ==
nullptr) {
2120 m_engine = qmlEngine(
this)->handle();
2126bool QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target)
2128 Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles);
2130 bool hasChanges =
false;
2132 target->m_roles = src->m_roles;
2135 QHash<
int, ElementSync> elementHash;
2136 for (
int i = 0 ; i < target->m_modelObjects.size(); ++i) {
2137 DynamicRoleModelNode *e = target->m_modelObjects.at(i);
2138 int uid = e->getUid();
2141 sync.targetIndex = i;
2142 elementHash.insert(uid, sync);
2144 for (
int i = 0 ; i < src->m_modelObjects.size(); ++i) {
2145 DynamicRoleModelNode *e = src->m_modelObjects.at(i);
2146 int uid = e->getUid();
2148 QHash<
int, ElementSync>::iterator it = elementHash.find(uid);
2149 if (it == elementHash.end()) {
2153 elementHash.insert(uid, sync);
2155 ElementSync &sync = it.value();
2162 int rowsRemoved = 0;
2163 for (
int i = 0 ; i < target->m_modelObjects.size() ; ++i) {
2164 DynamicRoleModelNode *element = target->m_modelObjects.at(i);
2165 ElementSync &s = elementHash.find(element->getUid()).value();
2166 Q_ASSERT(s.targetIndex >= 0);
2168 s.targetIndex -= rowsRemoved;
2169 if (s.src ==
nullptr) {
2170 Q_ASSERT(s.targetIndex == i);
2172 target->beginRemoveRows(QModelIndex(), i, i);
2173 target->m_modelObjects.remove(i, 1);
2174 target->endRemoveRows();
2183 target->m_modelObjects.clear();
2184 for (
int i = 0 ; i < src->m_modelObjects.size() ; ++i) {
2185 DynamicRoleModelNode *element = src->m_modelObjects.at(i);
2186 ElementSync &s = elementHash.find(element->getUid()).value();
2187 Q_ASSERT(s.srcIndex >= 0);
2188 DynamicRoleModelNode *targetElement = s.target;
2189 if (targetElement ==
nullptr) {
2190 targetElement =
new DynamicRoleModelNode(target, element->getUid());
2192 s.changedRoles = DynamicRoleModelNode::sync(element, targetElement);
2193 target->m_modelObjects.append(targetElement);
2204 const int targetElementCount = target->m_modelObjects.size();
2205 for (
int i = 0 ; i < targetElementCount ; ++i) {
2206 DynamicRoleModelNode *element = target->m_modelObjects.at(i);
2207 ElementSync &s = elementHash.find(element->getUid()).value();
2208 Q_ASSERT(s.srcIndex >= 0);
2209 const bool inserted = s.targetIndex == -1;
2210 if (s.targetIndex != i) {
2212 const int oldIndex = s.targetIndex;
2214 target->beginInsertRows(QModelIndex(), i, i);
2215 target->endInsertRows();
2217 Q_ASSERT(oldIndex > i);
2218 target->beginMoveRows(QModelIndex(), oldIndex, oldIndex, QModelIndex(), i);
2219 target->endMoveRows();
2221 for (
int j = i + 1; j < targetElementCount; ++j) {
2223 = elementHash.find(target->m_modelObjects.at(j)->getUid()).value();
2224 if (sToFix.targetIndex != -1 && (inserted || sToFix.targetIndex < oldIndex))
2225 sToFix.targetIndex += 1;
2230 if (!inserted && !s.changedRoles.isEmpty()) {
2231 QModelIndex idx = target->createIndex(i, 0);
2232 emit target->dataChanged(idx, idx, s.changedRoles);
2239void QQmlListModel::emitItemsChanged(
int index,
int count,
const QList<
int> &roles)
2245 emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);;
2248void QQmlListModel::emitItemsAboutToBeInserted(
int index,
int count)
2250 Q_ASSERT(index >= 0 && count >= 0);
2252 beginInsertRows(QModelIndex(), index, index + count - 1);
2255void QQmlListModel::emitItemsInserted()
2259 emit countChanged();
2263QQmlListModelWorkerAgent *QQmlListModel::agent()
2268 m_agent =
new QQmlListModelWorkerAgent(
this);
2273QModelIndex QQmlListModel::index(
int row,
int column,
const QModelIndex &parent)
const
2275 return row >= 0 && row < count() && column == 0 && !parent.isValid()
2276 ? createIndex(row, column)
2280int QQmlListModel::rowCount(
const QModelIndex &parent)
const
2282 return !parent.isValid() ? count() : 0;
2285QVariant QQmlListModel::data(
const QModelIndex &index,
int role)
const
2287 return data(index.row(), role);
2290bool QQmlListModel::setData(
const QModelIndex &index,
const QVariant &value,
int role)
2292 const int row = index.row();
2293 if (row >= count() || row < 0)
2296 if (m_dynamicRoles) {
2297 const QByteArray property = m_roles.at(role).toUtf8();
2298 if (m_modelObjects[row]->setValue(property, value)) {
2299 emitItemsChanged(row, 1, QList<
int>(1, role));
2303 const ListLayout::Role &r = m_listModel->getExistingRole(role);
2304 const int roleIndex = m_listModel->setOrCreateProperty(row, r.name, value);
2305 if (roleIndex != -1) {
2306 emitItemsChanged(row, 1, QList<
int>(1, role));
2314QVariant QQmlListModel::data(
int index,
int role)
const
2318 if (index >= count() || index < 0)
2322 v = m_modelObjects[index]->getValue(m_roles[role]);
2324 v = m_listModel->getProperty(index, role,
this, engine());
2329QHash<
int, QByteArray> QQmlListModel::roleNames()
const
2331 QHash<
int, QByteArray> roleNames;
2333 if (m_dynamicRoles) {
2334 const auto size = m_roles.size();
2335 roleNames.reserve(size);
2336 for (
int i = 0 ; i < size ; ++i)
2337 roleNames.insert(i, m_roles.at(i).toUtf8());
2339 const auto size = m_listModel->roleCount();
2340 roleNames.reserve(size);
2341 for (
int i = 0 ; i < size; ++i) {
2342 const ListLayout::Role &r = m_listModel->getExistingRole(i);
2343 roleNames.insert(i, r.name.toUtf8());
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376void QQmlListModel::setDynamicRoles(
bool enableDynamicRoles)
2378 if (m_mainThread && m_agent ==
nullptr) {
2379 if (enableDynamicRoles) {
2380 if (m_layout && m_layout->roleCount())
2381 qmlWarning(
this) << tr(
"unable to enable dynamic roles as this model is not empty");
2383 m_dynamicRoles =
true;
2385 if (m_roles.size()) {
2386 qmlWarning(
this) << tr(
"unable to enable static roles as this model is not empty");
2388 m_dynamicRoles =
false;
2392 qmlWarning(
this) << tr(
"dynamic role setting must be made from the main thread, before any worker scripts are created");
2397
2398
2399
2400int QQmlListModel::count()
const
2402 return m_dynamicRoles ? m_modelObjects.size() : m_listModel->elementCount();
2406
2407
2408
2409
2410
2411
2412
2413void QQmlListModel::clear()
2415 removeElements(0, count());
2419
2420
2421
2422
2423
2424
2425void QQmlListModel::remove(QQmlV4FunctionPtr args)
2427 int argLength = args->length();
2429 if (argLength == 1 || argLength == 2) {
2430 QV4::Scope scope(args->v4engine());
2431 int index = QV4::ScopedValue(scope, (*args)[0])->toInt32();
2432 int removeCount = (argLength == 2 ? QV4::ScopedValue(scope, (*args)[1])->toInt32() : 1);
2434 if (index < 0 || index+removeCount > count() || removeCount <= 0) {
2435 qmlWarning(
this) << tr(
"remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count());
2439 removeElements(index, removeCount);
2441 qmlWarning(
this) << tr(
"remove: incorrect number of arguments");
2445void QQmlListModel::removeElements(
int index,
int removeCount)
2447 Q_ASSERT(index >= 0 && removeCount >= 0);
2453 beginRemoveRows(QModelIndex(), index, index + removeCount - 1);
2455 QList<std::function<
void()>> toDestroy;
2456 if (m_dynamicRoles) {
2457 for (
int i=0 ; i < removeCount ; ++i) {
2458 auto modelObject = m_modelObjects[index+i];
2459 toDestroy.append([modelObject](){
2463 m_modelObjects.remove(index, removeCount);
2465 toDestroy = m_listModel->remove(index, removeCount);
2470 emit countChanged();
2472 for (
const auto &destroyer : std::as_const(toDestroy))
2476void QQmlListModel::updateTranslations()
2483 Q_ASSERT(m_listModel);
2486 for (
int i = 0, end = m_listModel->roleCount(); i != end; ++i) {
2487 if (m_listModel->getExistingRole(i).type == ListLayout::Role::String)
2491 if (!roles.isEmpty())
2492 emitItemsChanged(0, rowCount(QModelIndex()), roles);
2494 m_listModel->updateTranslations();
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2513void QQmlListModel::insert(QQmlV4FunctionPtr args)
2515 if (args->length() == 2) {
2516 QV4::Scope scope(args->v4engine());
2517 QV4::ScopedValue arg0(scope, (*args)[0]);
2518 int index = arg0->toInt32();
2520 if (index < 0 || index > count()) {
2521 qmlWarning(
this) << tr(
"insert: index %1 out of range").arg(index);
2525 QV4::ScopedObject argObject(scope, (*args)[1]);
2526 QV4::ScopedArrayObject objectArray(scope, (*args)[1]);
2528 QV4::ScopedObject argObject(scope);
2530 int objectArrayLength = objectArray->getLength();
2531 emitItemsAboutToBeInserted(index, objectArrayLength);
2532 for (
int i=0 ; i < objectArrayLength ; ++i) {
2533 argObject = objectArray->get(i);
2535 if (m_dynamicRoles) {
2536 m_modelObjects.insert(index+i, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2538 m_listModel->insert(index+i, argObject);
2541 emitItemsInserted();
2542 }
else if (argObject) {
2543 emitItemsAboutToBeInserted(index, 1);
2545 if (m_dynamicRoles) {
2546 m_modelObjects.insert(index, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2548 m_listModel->insert(index, argObject);
2551 emitItemsInserted();
2553 qmlWarning(
this) << tr(
"insert: value is not an object");
2556 qmlWarning(
this) << tr(
"insert: value is not an object");
2560bool QQmlListModel::canMove(
int from,
int to,
int n)
const
2562 if (from < 0 || to < 0 || n < 0 || n > count())
2565 const int maxIndex = count() - n;
2566 return from <= maxIndex && to <= maxIndex;
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583void QQmlListModel::move(
int from,
int to,
int n)
2585 if (n == 0 || from == to)
2587 if (!canMove(from, to, n)) {
2588 qmlWarning(
this) << tr(
"move: out of range");
2593 beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to);
2595 if (m_dynamicRoles) {
2597 int realFrom = from;
2610 std::rotate(m_modelObjects.begin() + realFrom,
2611 m_modelObjects.begin() + realFrom + realN,
2612 m_modelObjects.begin() + realTo + realN);
2615 m_listModel->move(from, to, n);
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634void QQmlListModel::append(QQmlV4FunctionPtr args)
2636 if (args->length() == 1) {
2637 QV4::Scope scope(args->v4engine());
2638 QV4::ScopedObject argObject(scope, (*args)[0]);
2639 QV4::ScopedArrayObject objectArray(scope, (*args)[0]);
2642 QV4::ScopedObject argObject(scope);
2644 int objectArrayLength = objectArray->getLength();
2645 if (objectArrayLength > 0) {
2646 int index = count();
2647 emitItemsAboutToBeInserted(index, objectArrayLength);
2649 for (
int i=0 ; i < objectArrayLength ; ++i) {
2650 argObject = objectArray->get(i);
2652 if (m_dynamicRoles) {
2653 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2655 m_listModel->append(argObject);
2659 emitItemsInserted();
2661 }
else if (argObject) {
2664 if (m_dynamicRoles) {
2665 index = m_modelObjects.size();
2666 emitItemsAboutToBeInserted(index, 1);
2667 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2669 index = m_listModel->elementCount();
2670 emitItemsAboutToBeInserted(index, 1);
2671 m_listModel->append(argObject);
2674 emitItemsInserted();
2676 qmlWarning(
this) << tr(
"append: value is not an object");
2679 qmlWarning(
this) << tr(
"append: value is not an object");
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715QJSValue QQmlListModel::get(
int index)
const
2717 QV4::Scope scope(engine());
2718 QV4::ScopedValue result(scope, QV4::Value::undefinedValue());
2720 if (index >= 0 && index < count()) {
2722 if (m_dynamicRoles) {
2723 DynamicRoleModelNode *object = m_modelObjects[index];
2724 result = QV4::QObjectWrapper::wrap(scope.engine, object);
2726 QObject *object = m_listModel->getOrCreateModelObject(
const_cast<QQmlListModel *>(
this), index);
2727 QQmlData *ddata = QQmlData::get(object);
2728 if (ddata->jsWrapper.isNullOrUndefined()) {
2729 result = scope.engine->memoryManager->allocate<QV4::ModelObject>(object,
const_cast<QQmlListModel *>(
this));
2731 ddata->jsWrapper.set(scope.engine, result);
2733 result = ddata->jsWrapper.value();
2738 return QJSValuePrivate::fromReturnedValue(result->asReturnedValue());
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757void QQmlListModel::set(
int index,
const QJSValue &value)
2759 QV4::Scope scope(engine());
2760 QV4::ScopedObject object(scope, QJSValuePrivate::asReturnedValue(&value));
2763 qmlWarning(
this) << tr(
"set: value is not an object");
2766 if (index > count() || index < 0) {
2767 qmlWarning(
this) << tr(
"set: index %1 out of range").arg(index);
2772 if (index == count()) {
2773 emitItemsAboutToBeInserted(index, 1);
2775 if (m_dynamicRoles) {
2776 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(object),
this));
2778 m_listModel->insert(index, object);
2781 emitItemsInserted();
2786 if (m_dynamicRoles) {
2787 m_modelObjects[index]->updateValues(scope.engine->variantMapFromJS(object), roles);
2789 m_listModel->set(index, object, &roles);
2793 emitItemsChanged(index, 1, roles);
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810void QQmlListModel::setProperty(
int index,
const QString& property,
const QVariant& value)
2812 if (count() == 0 || index >= count() || index < 0) {
2813 qmlWarning(
this) << tr(
"set: index %1 out of range").arg(index);
2817 if (m_dynamicRoles) {
2818 int roleIndex = m_roles.indexOf(property);
2819 if (roleIndex == -1) {
2820 roleIndex = m_roles.size();
2821 m_roles.append(property);
2823 if (m_modelObjects[index]->setValue(property.toUtf8(), value))
2824 emitItemsChanged(index, 1, QList<
int>(1, roleIndex));
2826 int roleIndex = m_listModel->setOrCreateProperty(index, property, value);
2827 if (roleIndex != -1)
2828 emitItemsChanged(index, 1, QList<
int>(1, roleIndex));
2833
2834
2835
2836
2837
2838void QQmlListModel::sync()
2843 qmlWarning(
this) <<
"List sync() can only be called from a WorkerScript";
2847 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
2848 const QV4::CompiledData::Binding *binding)
2850 if (binding->type() >= QV4::CompiledData::Binding::Type_Object) {
2851 const quint32 targetObjectIndex = binding->value.objectIndex;
2852 const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
2853 QString objName = compilationUnit->stringAt(target->inheritedTypeNameIndex);
2854 if (objName != listElementTypeName) {
2855 const QMetaObject *mo = resolveType(objName);
2856 if (mo != &QQmlListElement::staticMetaObject) {
2857 error(target, QQmlListModel::tr(
"ListElement: cannot contain nested elements"));
2860 listElementTypeName = objName;
2863 if (!compilationUnit->stringAt(target->idNameIndex).isEmpty()) {
2864 error(target->locationOfIdProperty, QQmlListModel::tr(
"ListElement: cannot use reserved \"id\" property"));
2868 const QV4::CompiledData::Binding *binding = target->bindingTable();
2869 for (quint32 i = 0; i < target->nBindings; ++i, ++binding) {
2870 QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
2871 if (propName.isEmpty()) {
2872 error(binding, QQmlListModel::tr(
"ListElement: cannot contain nested elements"));
2875 if (!verifyProperty(compilationUnit, binding))
2878 }
else if (binding->type() == QV4::CompiledData::Binding::Type_Script) {
2879 QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
2880 if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) {
2882 evaluateEnum(scriptStr, &ok);
2884 error(binding, QQmlListModel::tr(
"ListElement: cannot use script for property value"));
2894 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
2895 const QV4::CompiledData::Binding *binding,
ListModel *model, QQmlListModel *owner,
2896 int outterElementIndex)
2898 const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex);
2900 bool roleSet =
false;
2901 const QV4::CompiledData::Binding::Type bindingType = binding->type();
2902 if (bindingType >= QV4::CompiledData::Binding::Type_Object) {
2903 const quint32 targetObjectIndex = binding->value.objectIndex;
2904 const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
2907 if (outterElementIndex == -1) {
2910 const ListLayout::
Role &role = model->getOrCreateListRole(elementName);
2913 if (subModel ==
nullptr) {
2914 subModel =
new ListModel(role.subLayout,
nullptr);
2915 model->setOrCreateProperty(
2916 outterElementIndex, elementName,
2917 QVariant::fromValue(subModel));
2924 const QV4::CompiledData::Binding *subBinding = target->bindingTable();
2925 for (quint32 i = 0; i < target->nBindings; ++i, ++subBinding) {
2926 roleSet |= applyProperty(compilationUnit, subBinding, subModel, owner, elementIndex);
2928 }
else if (!model) {
2933 const bool isTranslationBinding = binding->isTranslationBinding();
2934 if (isTranslationBinding) {
2935 value = QVariant::fromValue<
const QV4::CompiledData::Binding*>(binding);
2936 }
else if (binding->evaluatesToString()) {
2937 value = compilationUnit->bindingValueAsString(binding);
2938 }
else if (bindingType == QV4::CompiledData::Binding::Type_Number) {
2939 value = compilationUnit->bindingValueAsNumber(binding);
2940 }
else if (bindingType == QV4::CompiledData::Binding::Type_Boolean) {
2941 value = binding->valueAsBoolean();
2942 }
else if (bindingType == QV4::CompiledData::Binding::Type_Null) {
2943 value = QVariant::fromValue(
nullptr);
2944 }
else if (bindingType == QV4::CompiledData::Binding::Type_Script) {
2945 QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
2946 if (definesEmptyList(scriptStr)) {
2947 value = QVariant::fromValue(
2948 new ListModel(model->getOrCreateListRole(elementName).subLayout,
nullptr));
2949 }
else if (binding->isFunctionExpression()) {
2950 QQmlBinding::Identifier id = binding->value.compiledScriptIndex;
2951 Q_ASSERT(id != QQmlBinding::Invalid);
2953 auto v4 = compilationUnit->engine;
2954 QV4::Scope scope(v4);
2956 if (model->m_modelCache ==
nullptr) {
2957 model->m_modelCache =
new QQmlListModel(owner, model, v4);
2958 QQmlEngine::setContextForObject(
2959 model->m_modelCache, QQmlEngine::contextForObject(owner));
2963 QV4::ScopedContext context(scope, QV4::QmlContext::create(v4->rootContext(), QQmlContextData::get(qmlContext(model->m_modelCache)),
nullptr));
2964 QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(context, compilationUnit->runtimeFunctions[id]));
2967 QV4::ScopedValue result(scope, function->call(v4->globalObject,
nullptr, 0));
2968 if (v4->hasException)
2969 v4->catchException();
2971 QJSValuePrivate::setValue(&v, result);
2975 value = evaluateEnum(scriptStr, &ok);
2981 model->setOrCreateProperty(outterElementIndex, elementName, value);
2982 auto listModel = model->m_modelCache;
2983 if (isTranslationBinding && listModel) {
2984 if (!listModel->translationChangeHandler) {
2985 auto ep = QQmlEnginePrivate::get(compilationUnit->engine);
2986 model->m_modelCache->translationChangeHandler = std::make_unique<QPropertyNotifier>(
2987 ep->translationLanguage.addNotifier([listModel](){ listModel->updateTranslations(); }));
2996 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
2997 const QList<
const QV4::CompiledData::Binding *> &bindings)
2999 listElementTypeName = QString();
3001 for (
const QV4::CompiledData::Binding *binding : bindings) {
3002 QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
3003 if (!propName.isEmpty()) {
3004 error(binding, QQmlListModel::tr(
"ListModel: undefined property '%1'").arg(propName));
3007 if (!verifyProperty(compilationUnit, binding))
3013 QObject *obj,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
3014 const QList<
const QV4::CompiledData::Binding *> &bindings)
3016 QQmlListModel *rv =
static_cast<QQmlListModel *>(obj);
3018 rv->m_engine = qmlEngine(rv)->handle();
3019 rv->m_compilationUnit = compilationUnit;
3021 bool setRoles =
false;
3023 for (
const QV4::CompiledData::Binding *binding : bindings) {
3024 if (binding->type() != QV4::CompiledData::Binding::Type_Object)
3026 setRoles |= applyProperty(
3027 compilationUnit, binding, rv->m_listModel, rv, -1);
3030 if (setRoles ==
false)
3031 qmlWarning(obj) <<
"All ListElement declarations are empty, no roles can be created unless dynamicRoles is set.";
3036 if (s.startsWith(QLatin1Char(
'[')) && s.endsWith(QLatin1Char(
']'))) {
3037 for (
auto c : s.sliced(1).chopped(1)) {
3048
3049
3050
3051
3052
3053
3054
3055
3056
3057
3058
3059
3060
3061
3062
3063
3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3096#include "moc_qqmllistmodel_p_p.cpp"
3098#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)