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())
431 int rowsInserted = 0;
433 for (
int i = 0 ; i < targetElementCount ; ++i) {
435 ElementSync &s = elementHash.find(element->getUid()).value();
436 Q_ASSERT(s.srcIndex >= 0);
437 s.srcIndex += rowsInserted;
438 if (s.srcIndex != s.targetIndex) {
440 if (s.targetIndex == -1) {
441 targetModel->beginInsertRows(QModelIndex(), i, i);
442 targetModel->endInsertRows();
445 bool validMove = targetModel->beginMoveRows(QModelIndex(), s.targetIndex, s.targetIndex, QModelIndex(), i);
447 targetModel->endMoveRows();
449 for (
int j=i+1; j < targetElementCount; ++j) {
451 ElementSync &sToFix = elementHash.find(eToFix->getUid()).value();
452 if (i < s.targetIndex) {
454 if (sToFix.targetIndex > s.targetIndex || sToFix.targetIndex < i)
457 sToFix.targetIndex += 1;
460 if (sToFix.targetIndex < s.targetIndex || sToFix.targetIndex > i)
463 sToFix.targetIndex -= 1;
470 if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) {
471 QModelIndex idx = targetModel->createIndex(i, 0);
473 targetModel->dataChanged(idx, idx, s.changedRoles);
486 for (
const auto &destroyer : remove(0, elementCount()))
490 if (m_modelCache && m_modelCache->m_primary ==
false)
492 m_modelCache =
nullptr;
498 newElement(elementIndex);
505 updateCacheIndices(index);
521 std::rotate(elements.begin() + from,
522 elements.begin() + from + n,
523 elements.begin() + to + n);
525 updateCacheIndices(from, to + n);
531 elements.insert(elements.begin() + index, e);
534void ListModel::updateCacheIndices(
int start,
int end)
538 if (end < 0 || end > count)
541 for (
int i = start; i < end; ++i) {
554 return e->getProperty(r, owner, eng);
560 return e->getListProperty(role);
565 for (ListElement *e : elements) {
566 if (ModelNodeMetaObject *cache = e->objectCache()) {
568 cache->updateValues();
573void ListModel::
set(
int elementIndex, QV4::Object *object, QList<
int> *roles)
577 QV4::ExecutionEngine *v4 = object->engine();
578 QV4::Scope scope(v4);
579 QV4::ScopedObject o(scope);
581 QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
582 QV4::ScopedString propertyName(scope);
583 QV4::ScopedValue propertyValue(scope);
585 propertyName = it.nextPropertyNameAsString(propertyValue);
593 if (
const QV4::String *s = propertyValue->as<QV4::String>()) {
595 roleIndex = e->setStringProperty(r, s->toQString());
596 }
else if (propertyValue->isNumber()) {
598 roleIndex = e->setDoubleProperty(r, propertyValue->asDouble());
599 }
else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) {
600 roleIndex = setArrayLike(&o, propertyName, e, a);
601 }
else if (QV4::Sequence *s = propertyValue->as<QV4::Sequence>()) {
602 roleIndex = setArrayLike(&o, propertyName, e, s);
603 }
else if (QV4::QmlListWrapper *l = propertyValue->as<QV4::QmlListWrapper>()) {
604 roleIndex = setArrayLike(&o, propertyName, e, l);
605 }
else if (propertyValue->isBoolean()) {
607 roleIndex = e->setBoolProperty(r, propertyValue->booleanValue());
608 }
else if (QV4::DateObject *dd = propertyValue->as<QV4::DateObject>()) {
610 QDateTime dt = dd->toQDateTime();
611 roleIndex = e->setDateTimeProperty(r, dt);
612 }
else if (QV4::UrlObject *url = propertyValue->as<QV4::UrlObject>()) {
614 QUrl qurl = QUrl(url->href());
615 roleIndex = e->setUrlProperty(r, qurl);
616 }
else if (QV4::FunctionObject *f = propertyValue->as<QV4::FunctionObject>()) {
618 QV4::ScopedFunctionObject func(scope, f);
620 QJSValuePrivate::setValue(&jsv, func);
621 roleIndex = e->setFunctionProperty(r, jsv);
622 }
else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
623 if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) {
626 roleIndex = e->setQObjectProperty(role, wrapper);
627 }
else if (QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
629 QV4::Value::fromReturnedValue(o->asReturnedValue()),
630 QMetaType::fromType<QUrl>(),
true);
631 maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
633 QUrl qurl = maybeUrl.toUrl();
634 roleIndex = e->setUrlProperty(r, qurl);
638 QV4::ScopedObject obj(scope, o);
639 roleIndex = e->setVariantMapProperty(role, obj);
642 }
else if (propertyValue->isNullOrUndefined()) {
645 e->clearProperty(*r);
649 roles->append(roleIndex);
663 QV4::ExecutionEngine *v4 = object->engine();
664 QV4::Scope scope(v4);
666 QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
667 QV4::ScopedString propertyName(scope);
668 QV4::ScopedValue propertyValue(scope);
669 QV4::ScopedObject o(scope);
671 propertyName = it.nextPropertyNameAsString(propertyValue);
676 if (QV4::String *s = propertyValue->stringValue()) {
679 e->setStringPropertyFast(r, s->toQString());
680 }
else if (propertyValue->isNumber()) {
683 e->setDoublePropertyFast(r, propertyValue->asDouble());
685 }
else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) {
686 setArrayLikeFast(&o, propertyName, e, a);
687 }
else if (QV4::Sequence *s = propertyValue->as<QV4::Sequence>()) {
688 setArrayLikeFast(&o, propertyName, e, s);
689 }
else if (QV4::QmlListWrapper *l = propertyValue->as<QV4::QmlListWrapper>()) {
690 setArrayLikeFast(&o, propertyName, e, l);
691 }
else if (propertyValue->isBoolean()) {
694 e->setBoolPropertyFast(r, propertyValue->booleanValue());
696 }
else if (QV4::DateObject *date = propertyValue->as<QV4::DateObject>()) {
699 QDateTime dt = date->toQDateTime();
700 e->setDateTimePropertyFast(r, dt);
702 }
else if (QV4::UrlObject *url = propertyValue->as<QV4::UrlObject>()){
705 QUrl qurl = QUrl(url->href());
706 e->setUrlPropertyFast(r, qurl);
708 }
else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
709 if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) {
712 e->setQObjectPropertyFast(r, wrapper);
714 QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
716 QV4::Value::fromReturnedValue(o->asReturnedValue()),
717 QMetaType::fromType<QUrl>(),
true);
718 if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
719 const QUrl qurl = maybeUrl.toUrl();
722 e->setUrlPropertyFast(r, qurl);
726 e->setVariantMapFast(role, o);
729 }
else if (propertyValue->isNullOrUndefined()) {
732 auto memberName = propertyName->toString(v4)->toQString();
733 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")));
734 qmlWarning(
nullptr, err);
738 e->clearProperty(*r);
744QList<std::function<
void()>> ListModel::remove(
int index,
int count)
746 QList<std::function<
void()>> toDestroy;
747 auto layout = m_layout;
748 for (
int i=0 ; i < count ; ++i) {
749 auto element = elements[index+i];
750 toDestroy.append([element, layout](){
751 element->destroy(layout);
755 elements.erase(elements.begin() + index, elements.begin() + index + count);
756 updateCacheIndices(index);
763 set(elementIndex, object, SetElement::WasJustInserted);
769 set(elementIndex, object, SetElement::WasJustInserted);
773int ListModel::setOrCreateProperty(
int elementIndex,
const QString &key,
const QVariant &data)
782 roleIndex = e->setVariantProperty(*r, data);
786 if (roleIndex != -1 && cache)
794int ListModel::setExistingProperty(
int elementIndex,
const QString &key,
const QV4::Value &data, QV4::ExecutionEngine *eng)
802 roleIndex = e->setJsProperty(*r, data, eng);
813 if (e->next ==
nullptr) {
829 return ModelNodeMetaObject::get(m_objectCache);
834 char *mem = getPropertyMemory(role);
841 char *mem = getPropertyMemory(role);
842 QV4::PersistentValue *g =
reinterpret_cast<QV4::PersistentValue *>(mem);
843 return g->as<QV4::QObjectWrapper>();
848 QVariantMap *map =
nullptr;
850 char *mem = getPropertyMemory(role);
851 if (isMemoryUsed<QVariantMap>(mem))
852 map =
reinterpret_cast<QVariantMap *>(mem);
859 QDateTime *dt =
nullptr;
861 char *mem = getPropertyMemory(role);
862 if (isMemoryUsed<QDateTime>(mem))
863 dt =
reinterpret_cast<QDateTime *>(mem);
872 char *mem = getPropertyMemory(role);
873 if (isMemoryUsed<QUrl>(mem))
874 url =
reinterpret_cast<QUrl *>(mem);
881 QJSValue *f =
nullptr;
883 char *mem = getPropertyMemory(role);
884 if (isMemoryUsed<QJSValue>(mem))
885 f =
reinterpret_cast<QJSValue *>(mem);
890QV4::PersistentValue *
893 char *mem = getPropertyMemory(role);
895 bool existingGuard =
false;
896 for (size_t i = 0; i <
sizeof(QV4::PersistentValue);
899 existingGuard =
true;
904 QV4::PersistentValue *g =
nullptr;
907 g =
reinterpret_cast<QV4::PersistentValue *>(mem);
914 char *mem = getPropertyMemory(role);
921 char *mem = getPropertyMemory(role);
928 double *value =
reinterpret_cast<
double *>(mem);
936 data = value->toString(owner);
943 bool *value =
reinterpret_cast<
bool *>(mem);
953 if (model->m_modelCache ==
nullptr) {
954 model->m_modelCache =
new QQmlListModel(owner, model, eng);
955 QQmlEngine::setContextForObject(model->m_modelCache, QQmlEngine::contextForObject(owner));
958 QObject *object = model->m_modelCache;
959 data = QVariant::fromValue(object);
965 QV4::PersistentValue *guard =
reinterpret_cast<QV4::PersistentValue *>(mem);
966 data = QVariant::fromValue(guard->as<QV4::QObjectWrapper>()->object());
971 if (isMemoryUsed<QVariantMap>(mem)) {
972 QVariantMap *map =
reinterpret_cast<QVariantMap *>(mem);
979 if (isMemoryUsed<QDateTime>(mem)) {
980 QDateTime *dt =
reinterpret_cast<QDateTime *>(mem);
987 if (isMemoryUsed<QUrl>(mem)) {
988 QUrl *url =
reinterpret_cast<QUrl *>(mem);
995 if (isMemoryUsed<QJSValue>(mem)) {
996 QJSValue *func =
reinterpret_cast<QJSValue *>(mem);
997 data = QVariant::fromValue(*func);
1013 char *mem = getPropertyMemory(role);
1019 changed = c->asString().compare(s) != 0;
1033 char *mem = getPropertyMemory(role);
1034 double *value =
reinterpret_cast<
double *>(mem);
1035 bool changed = *value != d;
1049 char *mem = getPropertyMemory(role);
1050 bool *value =
reinterpret_cast<
bool *>(mem);
1051 bool changed = *value != b;
1065 char *mem = getPropertyMemory(role);
1067 if (*value && *value != m) {
1083 char *mem = getPropertyMemory(role);
1084 if (isMemoryUsed<QVariantMap>(mem))
1085 reinterpret_cast<QV4::PersistentValue *>(mem)->set(o->engine(), *o);
1087 new (mem) QV4::PersistentValue(o->engine(), o);
1099 char *mem = getPropertyMemory(role);
1100 if (isMemoryUsed<QVariantMap>(mem)) {
1101 QVariantMap *map =
reinterpret_cast<QVariantMap *>(mem);
1104 new (mem) QVariantMap(o->engine()->variantMapFromJS(o));
1116 char *mem = getPropertyMemory(role);
1117 if (isMemoryUsed<QVariantMap>(mem)) {
1118 QVariantMap *map =
reinterpret_cast<QVariantMap *>(mem);
1119 if (m && map->isSharedWith(*m))
1126 new (mem) QVariantMap(*m);
1128 new (mem) QVariantMap;
1140 char *mem = getPropertyMemory(role);
1141 if (isMemoryUsed<QDateTime>(mem)) {
1142 QDateTime *dt =
reinterpret_cast<QDateTime *>(mem);
1145 new (mem) QDateTime(dt);
1157 char *mem = getPropertyMemory(role);
1158 if (isMemoryUsed<QUrl>(mem)) {
1159 QUrl *qurl =
reinterpret_cast<QUrl *>(mem);
1162 new (mem) QUrl(url);
1174 char *mem = getPropertyMemory(role);
1175 if (isMemoryUsed<QJSValue>(mem)) {
1176 QJSValue *f =
reinterpret_cast<QJSValue *>(mem);
1179 new (mem) QJSValue(f);
1191 char *mem = getPropertyMemory(role);
1193 s->setTranslation(b);
1203 char *mem = getPropertyMemory(role);
1209 char *mem = getPropertyMemory(role);
1210 double *value =
new (mem)
double;
1216 char *mem = getPropertyMemory(role);
1217 bool *value =
new (mem)
bool;
1223 char *mem = getPropertyMemory(role);
1224 new (mem) QV4::PersistentValue(o->engine(), o);
1229 char *mem = getPropertyMemory(role);
1236 char *mem = getPropertyMemory(role);
1237 QVariantMap *map =
new (mem) QVariantMap;
1238 *map = o->engine()->variantMapFromJS(o);
1243 char *mem = getPropertyMemory(role);
1244 new (mem) QDateTime(dt);
1249 char *mem = getPropertyMemory(role);
1250 new (mem) QUrl(url);
1255 char *mem = getPropertyMemory(role);
1256 new (mem) QJSValue(f);
1261 switch (role
.type) {
1262 case ListLayout::Role::String:
1263 setStringProperty(role, QString());
1266 setDoubleProperty(role, 0.0);
1269 setBoolProperty(role,
false);
1272 setListProperty(role,
nullptr);
1274 case ListLayout::Role::QObject:
1275 setQObjectProperty(role,
nullptr);
1277 case ListLayout::Role::DateTime:
1278 setDateTimeProperty(role, QDateTime());
1280 case ListLayout::Role::Url:
1281 setUrlProperty(role, QUrl());
1283 case ListLayout::Role::VariantMap:
1284 setVariantMapProperty(role, (QVariantMap *)
nullptr);
1287 setFunctionProperty(role, QJSValue());
1296 m_objectCache =
nullptr;
1297 uid = uidCounter.fetchAndAddOrdered(1);
1299 memset(data, 0,
sizeof(data));
1304 m_objectCache =
nullptr;
1307 memset(data, 0,
sizeof(data));
1317 QList<
int> changedRoles;
1323 switch (srcRole
.type) {
1326 ListModel *srcSubModel = src->getListProperty(srcRole);
1327 ListModel *targetSubModel = target->getListProperty(targetRole);
1330 if (targetSubModel ==
nullptr) {
1331 targetSubModel =
new ListModel(targetRole.subLayout,
nullptr);
1332 target->setListPropertyFast(targetRole, targetSubModel);
1335 roleIndex = targetRole
.index;
1341 QV4::QObjectWrapper *object = src->getQObjectProperty(srcRole);
1342 roleIndex = target->setQObjectProperty(targetRole, object);
1351 QVariant v = src->getProperty(srcRole,
nullptr,
nullptr);
1352 roleIndex = target->setVariantProperty(targetRole, v);
1357 QVariantMap *map = src->getVariantMapProperty(srcRole);
1358 roleIndex = target->setVariantMapProperty(targetRole, map);
1365 changedRoles << roleIndex;
1368 return changedRoles;
1396 if (QV4::PersistentValue *guard = getGuardProperty(r))
1397 guard->~PersistentValue();
1402 QVariantMap *map = getVariantMapProperty(r);
1409 QDateTime *dt = getDateTimeProperty(r);
1416 QUrl *url = getUrlProperty(r);
1423 QJSValue *f = getFunctionProperty(r);
1434 if (m_objectCache) {
1435 m_objectCache->~QObject();
1436 operator
delete(m_objectCache);
1441 next->destroy(
nullptr);
1449 switch (role
.type) {
1451 roleIndex = setDoubleProperty(role, d.toDouble());
1453 case ListLayout::Role::String:
1454 if (d.userType() == qMetaTypeId<
const QV4::CompiledData::Binding *>())
1455 roleIndex = setTranslationProperty(role, d.value<
const QV4::CompiledData::Binding*>());
1457 roleIndex = setStringProperty(role, d.toString());
1460 roleIndex = setBoolProperty(role, d.toBool());
1463 roleIndex = setListProperty(role, d.value<
ListModel *>());
1466 QVariantMap map = d.toMap();
1467 roleIndex = setVariantMapProperty(role, &map);
1470 case ListLayout::Role::DateTime:
1471 roleIndex = setDateTimeProperty(role, d.toDateTime());
1473 case ListLayout::Role::Url:
1474 roleIndex = setUrlProperty(role, d.toUrl());
1477 roleIndex = setFunctionProperty(role, d.value<QJSValue>());
1491 QV4::Scope scope(eng);
1495 QString qstr = d.toQString();
1496 roleIndex = setStringProperty(role, qstr);
1497 }
else if (d.isNumber()) {
1498 roleIndex = setDoubleProperty(role, d.asDouble());
1499 }
else if (d.as<QV4::ArrayObject>()) {
1500 QV4::ScopedArrayObject a(scope, d);
1502 QV4::Scope scope(a->engine());
1503 QV4::ScopedObject o(scope);
1505 ListModel *subModel =
new ListModel(role.subLayout,
nullptr);
1506 int arrayLength = a->getLength();
1507 for (
int j=0 ; j < arrayLength ; ++j) {
1509 subModel->append(o);
1511 roleIndex = setListProperty(role, subModel);
1513 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));
1515 }
else if (d.isBoolean()) {
1516 roleIndex = setBoolProperty(role, d.booleanValue());
1517 }
else if (d.as<QV4::DateObject>()) {
1518 QV4::Scoped<QV4::DateObject> dd(scope, d);
1519 QDateTime dt = dd->toQDateTime();
1520 roleIndex = setDateTimeProperty(role, dt);
1521 }
else if (d.as<QV4::UrlObject>()) {
1522 QV4::Scoped<QV4::UrlObject> url(scope, d);
1523 QUrl qurl = QUrl(url->href());
1524 roleIndex = setUrlProperty(role, qurl);
1525 }
else if (d.as<QV4::FunctionObject>()) {
1526 QV4::ScopedFunctionObject f(scope, d);
1528 QJSValuePrivate::setValue(&jsv, f);
1529 roleIndex = setFunctionProperty(role, jsv);
1530 }
else if (d.isObject()) {
1531 QV4::ScopedObject o(scope, d);
1532 QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>();
1534 roleIndex = setQObjectProperty(role, wrapper);
1536 roleIndex = setVariantMapProperty(role, o);
1538 QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
1540 QV4::Value::fromReturnedValue(o.asReturnedValue()),
1541 QMetaType::fromType<QUrl>(),
true);
1542 if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
1543 roleIndex = setUrlProperty(role, maybeUrl.toUrl());
1546 }
else if (d.isNullOrUndefined()) {
1547 clearProperty(role);
1559 const int roleCount = m_model->m_listModel->roleCount();
1560 QList<QByteArray> properties;
1561 properties.reserve(roleCount);
1562 for (
int i = 0 ; i < roleCount ; ++i) {
1563 const ListLayout::
Role &role = m_model->m_listModel->getExistingRole(i);
1564 QByteArray name = role.name.toUtf8();
1567 type()->createProperties(properties);
1576#if QT_VERSION >= QT_VERSION_CHECK(7
, 0
, 0
)
1577const QMetaObject *ModelNodeMetaObject::toDynamicMetaObject(QObject *object)
const
1582 if (!m_initialized) {
1583 m_initialized =
true;
1584 const_cast<ModelNodeMetaObject *>(
this)->initialize();
1586 return QQmlOpenMetaObject::toDynamicMetaObject(object);
1591 QObjectPrivate *op = QObjectPrivate::get(obj);
1592 return static_cast<ModelNodeMetaObject*>(op->metaObject);
1598 const int roleCount = m_model->m_listModel->roleCount();
1599 if (!m_initialized) {
1601 Q_ALLOCA_VAR(
int, changedRoles, roleCount *
sizeof(
int));
1602 for (
int i = 0; i < roleCount; ++i)
1603 changedRoles[i] = i;
1604 emitDirectNotifies(changedRoles, roleCount);
1608 for (
int i=0 ; i < roleCount ; ++i) {
1609 const ListLayout::
Role &role = m_model->m_listModel->getExistingRole(i);
1610 QByteArray name = role.name.toUtf8();
1611 const QVariant &data = m_model->data(m_elementIndex, i);
1618 if (!m_initialized) {
1619 emitDirectNotifies(roles.constData(), roles.size());
1622 int roleCount = roles.size();
1623 for (
int i=0 ; i < roleCount ; ++i) {
1624 int roleIndex = roles.at(i);
1625 const ListLayout::
Role &role = m_model->m_listModel->getExistingRole(roleIndex);
1626 QByteArray name = role.name.toUtf8();
1627 const QVariant &data = m_model->data(m_elementIndex, roleIndex);
1637 QString propName = QString::fromUtf8(name(index));
1638 const QVariant value =
this->value(index);
1640 QV4::Scope scope(m_model->engine());
1641 QV4::ScopedValue v(scope, scope.engine->fromVariant(value));
1643 int roleIndex = m_model->m_listModel->setExistingProperty(m_elementIndex, propName, v, scope.engine);
1644 if (roleIndex != -1)
1645 m_model->emitItemsChanged(m_elementIndex, 1, QList<
int>(1, roleIndex));
1651 Q_ASSERT(!m_initialized);
1652 QQmlData *ddata = QQmlData::get(object(),
false);
1656 if (!qmlEngine(m_model))
1658 for (
int i = 0; i < roleCount; ++i) {
1659 const int changedRole = changedRoles[i];
1660 QQmlNotifier::notify(ddata, changedRole);
1666bool ModelObject::virtualPut(Managed *m, PropertyKey id,
const Value &value, Value *receiver)
1669 return Object::virtualPut(m, id, value, receiver);
1670 QString propName = id.toQString();
1674 ExecutionEngine *eng = that->engine();
1675 const int elementIndex = that->d()->elementIndex();
1676 if (QQmlListModel *model = that->d()->m_model) {
1678 = model->listModel()->setExistingProperty(elementIndex, propName, value, eng);
1679 if (roleIndex != -1)
1680 model->emitItemsChanged(elementIndex, 1, QList<
int>(1, roleIndex));
1685 mo->emitPropertyNotification(propName.toUtf8());
1689ReturnedValue
ModelObject::virtualGet(
const Managed *m, PropertyKey id,
const Value *receiver,
bool *hasProperty)
1692 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1696 ScopedString name(scope, id.asStringOrSymbol());
1697 QQmlListModel *model = that->d()->m_model;
1699 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1701 const ListLayout::
Role *role = model->listModel()->getExistingRole(name);
1703 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1705 *hasProperty =
true;
1707 if (QQmlEngine *qmlEngine = that->engine()->qmlEngine()) {
1708 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine);
1709 if (ep && ep->propertyCapture)
1710 ep->propertyCapture->captureProperty(that->object(), -1, role
->index,
false);
1713 const int elementIndex = that->d()->elementIndex();
1714 QVariant value = model->data(elementIndex, role
->index);
1715 return that->engine()->fromVariant(value);
1718ReturnedValue
ModelObject::virtualResolveLookupGetter(
const Object *object, ExecutionEngine *engine, Lookup *lookup)
1720 lookup->call = Lookup::Call::GetterQObjectPropertyFallback;
1721 return lookup->getter(engine, *object);
1728 PropertyKey next(
const Object *o, Property *pd =
nullptr, PropertyAttributes *attrs =
nullptr)
override;
1736 ExecutionEngine *v4 = that->engine();
1738 QQmlListModel *model = that->d()->m_model;
1739 ListModel *listModel = model ? model->listModel() :
nullptr;
1741 Scope scope(that->engine());
1744 ScopedString roleName(scope, v4->newString(role.name));
1746 *attrs = QV4::Attr_Data;
1749 QVariant value = model->data(that->d()->elementIndex(), role
.index);
1750 if (
auto recursiveListModel = qvariant_cast<QQmlListModel*>(value)) {
1751 auto size = recursiveListModel->count();
1752 auto array = ScopedArrayObject{scope, v4->newArrayObject(size)};
1753 QV4::ScopedValue val(scope);
1754 for (
auto i = 0; i < size; i++) {
1755 val = QJSValuePrivate::convertToReturnedValue(
1756 v4, recursiveListModel->get(i));
1757 array->arrayPut(i, val);
1761 pd->value = v4->fromVariant(value);
1764 return roleName->toPropertyKey();
1770 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
1773OwnPropertyKeyIterator *
ModelObject::virtualOwnPropertyKeys(
const Object *m, Value *target)
1792 object->updateValues(obj, roles);
1798 QList<
int> changedRoles;
1799 for (
int i = 0; i < src->m_meta->count(); ++i) {
1800 const QByteArray &name = src->m_meta->name(i);
1801 QVariant value = src->m_meta->value(i);
1803 QQmlListModel *srcModel = qobject_cast<QQmlListModel *>(value.value<QObject *>());
1804 QQmlListModel *targetModel = qobject_cast<QQmlListModel *>(target->m_meta->value(i).value<QObject *>());
1806 bool modelHasChanges =
false;
1808 if (targetModel ==
nullptr)
1809 targetModel = QQmlListModel::createWithOwner(target->m_owner);
1811 modelHasChanges = QQmlListModel::sync(srcModel, targetModel);
1813 QObject *targetModelObject = targetModel;
1814 value = QVariant::fromValue(targetModelObject);
1815 }
else if (targetModel) {
1819 if (target->setValue(name, value) || modelHasChanges)
1820 changedRoles << target->m_owner->m_roles.indexOf(QString::fromUtf8(name));
1822 return changedRoles;
1827 for (
auto it = object.cbegin(), end = object.cend(); it != end; ++it) {
1828 const QString &key = it.key();
1830 int roleIndex = m_owner->m_roles.indexOf(key);
1831 if (roleIndex == -1) {
1832 roleIndex = m_owner->m_roles.size();
1833 m_owner->m_roles.append(key);
1836 QVariant value = it.value();
1840 if (value.userType() == qMetaTypeId<QJSValue>())
1841 value = value.value<QJSValue>().toVariant();
1843 if (value.userType() == QMetaType::QVariantList) {
1844 QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner);
1846 QVariantList subArray = value.toList();
1847 QVariantList::const_iterator subIt = subArray.cbegin();
1848 QVariantList::const_iterator subEnd = subArray.cend();
1849 while (subIt != subEnd) {
1850 const QVariantMap &subObject = subIt->toMap();
1851 subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1855 QObject *subModelObject = subModel;
1856 value = QVariant::fromValue(subModelObject);
1859 const QByteArray &keyUtf8 = key.toUtf8();
1861 QQmlListModel *existingModel = qobject_cast<QQmlListModel *>(m_meta->value(keyUtf8).value<QObject *>());
1862 delete existingModel;
1864 if (m_meta->setValue(keyUtf8, value))
1876 for (
int i=0 ; i < count() ; ++i) {
1877 QQmlListModel *subModel = qobject_cast<QQmlListModel *>(value(i).value<QObject *>());
1887 QVariant v = value(index);
1888 QQmlListModel *model = qobject_cast<QQmlListModel *>(v.value<QObject *>());
1897 QQmlListModel *parentModel = m_owner->m_owner;
1899 QVariant v = value(index);
1903 if (v.userType() == qMetaTypeId<QJSValue>())
1904 v= v.value<QJSValue>().toVariant();
1906 if (v.userType() == QMetaType::QVariantList) {
1907 QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel);
1909 QVariantList subArray = v.toList();
1910 QVariantList::const_iterator subIt = subArray.cbegin();
1911 QVariantList::const_iterator subEnd = subArray.cend();
1912 while (subIt != subEnd) {
1913 const QVariantMap &subObject = subIt->toMap();
1914 subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1918 QObject *subModelObject = subModel;
1919 v = QVariant::fromValue(subModelObject);
1924 int elementIndex = parentModel->m_modelObjects.indexOf(m_owner);
1925 if (elementIndex != -1) {
1926 int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData()));
1927 if (roleIndex != -1)
1928 parentModel->emitItemsChanged(elementIndex, 1, QList<
int>(1, roleIndex));
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2033QQmlListModel::QQmlListModel(QObject *parent)
2034: QAbstractListModel(parent)
2036 m_mainThread =
true;
2039 m_dynamicRoles =
false;
2041 m_layout =
new ListLayout;
2042 m_listModel =
new ListModel(m_layout,
this);
2047QQmlListModel::QQmlListModel(
const QQmlListModel *owner, ListModel *data, QV4::ExecutionEngine *engine, QObject *parent)
2048: QAbstractListModel(parent)
2050 m_mainThread = owner->m_mainThread;
2052 m_agent = owner->m_agent;
2054 Q_ASSERT(owner->m_dynamicRoles ==
false);
2055 m_dynamicRoles =
false;
2060 m_compilationUnit = owner->m_compilationUnit;
2063QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent)
2064: QAbstractListModel(agent)
2066 m_mainThread =
false;
2069 m_dynamicRoles = orig->m_dynamicRoles;
2071 if (ListLayout *layout = orig->m_layout)
2072 m_layout =
new ListLayout(layout);
2074 m_layout =
new ListLayout;
2076 m_listModel =
new ListModel(m_layout,
this);
2081 ListModel::sync(orig->m_listModel, m_listModel);
2084 m_compilationUnit = orig->m_compilationUnit;
2087QQmlListModel::~QQmlListModel()
2089 qDeleteAll(m_modelObjects);
2092 m_listModel->destroy();
2095 if (m_mainThread && m_agent)
2096 m_agent->modelDestroyed();
2099 if (m_agent && m_ownAgent)
2102 m_listModel =
nullptr;
2108QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner)
2110 QQmlListModel *model =
new QQmlListModel;
2112 model->m_mainThread = newOwner->m_mainThread;
2113 model->m_engine = newOwner->m_engine;
2114 model->m_agent = newOwner->m_agent;
2115 model->m_dynamicRoles = newOwner->m_dynamicRoles;
2117 QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner));
2122QV4::ExecutionEngine *QQmlListModel::engine()
const
2124 if (m_engine ==
nullptr) {
2125 m_engine = qmlEngine(
this)->handle();
2131bool QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target)
2133 Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles);
2135 bool hasChanges =
false;
2137 target->m_roles = src->m_roles;
2140 QHash<
int, ElementSync> elementHash;
2141 for (
int i = 0 ; i < target->m_modelObjects.size(); ++i) {
2142 DynamicRoleModelNode *e = target->m_modelObjects.at(i);
2143 int uid = e->getUid();
2146 sync.targetIndex = i;
2147 elementHash.insert(uid, sync);
2149 for (
int i = 0 ; i < src->m_modelObjects.size(); ++i) {
2150 DynamicRoleModelNode *e = src->m_modelObjects.at(i);
2151 int uid = e->getUid();
2153 QHash<
int, ElementSync>::iterator it = elementHash.find(uid);
2154 if (it == elementHash.end()) {
2158 elementHash.insert(uid, sync);
2160 ElementSync &sync = it.value();
2167 int rowsRemoved = 0;
2168 for (
int i = 0 ; i < target->m_modelObjects.size() ; ++i) {
2169 DynamicRoleModelNode *element = target->m_modelObjects.at(i);
2170 ElementSync &s = elementHash.find(element->getUid()).value();
2171 Q_ASSERT(s.targetIndex >= 0);
2173 s.targetIndex -= rowsRemoved;
2174 if (s.src ==
nullptr) {
2175 Q_ASSERT(s.targetIndex == i);
2177 target->beginRemoveRows(QModelIndex(), i, i);
2178 target->m_modelObjects.remove(i, 1);
2179 target->endRemoveRows();
2188 target->m_modelObjects.clear();
2189 for (
int i = 0 ; i < src->m_modelObjects.size() ; ++i) {
2190 DynamicRoleModelNode *element = src->m_modelObjects.at(i);
2191 ElementSync &s = elementHash.find(element->getUid()).value();
2192 Q_ASSERT(s.srcIndex >= 0);
2193 DynamicRoleModelNode *targetElement = s.target;
2194 if (targetElement ==
nullptr) {
2195 targetElement =
new DynamicRoleModelNode(target, element->getUid());
2197 s.changedRoles = DynamicRoleModelNode::sync(element, targetElement);
2198 target->m_modelObjects.append(targetElement);
2206 int rowsInserted = 0;
2207 for (
int i = 0 ; i < target->m_modelObjects.size() ; ++i) {
2208 DynamicRoleModelNode *element = target->m_modelObjects.at(i);
2209 ElementSync &s = elementHash.find(element->getUid()).value();
2210 Q_ASSERT(s.srcIndex >= 0);
2211 s.srcIndex += rowsInserted;
2212 if (s.srcIndex != s.targetIndex) {
2213 if (s.targetIndex == -1) {
2214 target->beginInsertRows(QModelIndex(), i, i);
2215 target->endInsertRows();
2217 target->beginMoveRows(QModelIndex(), i, i, QModelIndex(), s.srcIndex);
2218 target->endMoveRows();
2223 if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) {
2224 QModelIndex idx = target->createIndex(i, 0);
2225 emit target->dataChanged(idx, idx, s.changedRoles);
2232void QQmlListModel::emitItemsChanged(
int index,
int count,
const QList<
int> &roles)
2238 emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);;
2241void QQmlListModel::emitItemsAboutToBeInserted(
int index,
int count)
2243 Q_ASSERT(index >= 0 && count >= 0);
2245 beginInsertRows(QModelIndex(), index, index + count - 1);
2248void QQmlListModel::emitItemsInserted()
2252 emit countChanged();
2256QQmlListModelWorkerAgent *QQmlListModel::agent()
2261 m_agent =
new QQmlListModelWorkerAgent(
this);
2266QModelIndex QQmlListModel::index(
int row,
int column,
const QModelIndex &parent)
const
2268 return row >= 0 && row < count() && column == 0 && !parent.isValid()
2269 ? createIndex(row, column)
2273int QQmlListModel::rowCount(
const QModelIndex &parent)
const
2275 return !parent.isValid() ? count() : 0;
2278QVariant QQmlListModel::data(
const QModelIndex &index,
int role)
const
2280 return data(index.row(), role);
2283bool QQmlListModel::setData(
const QModelIndex &index,
const QVariant &value,
int role)
2285 const int row = index.row();
2286 if (row >= count() || row < 0)
2289 if (m_dynamicRoles) {
2290 const QByteArray property = m_roles.at(role).toUtf8();
2291 if (m_modelObjects[row]->setValue(property, value)) {
2292 emitItemsChanged(row, 1, QList<
int>(1, role));
2296 const ListLayout::Role &r = m_listModel->getExistingRole(role);
2297 const int roleIndex = m_listModel->setOrCreateProperty(row, r.name, value);
2298 if (roleIndex != -1) {
2299 emitItemsChanged(row, 1, QList<
int>(1, role));
2307QVariant QQmlListModel::data(
int index,
int role)
const
2311 if (index >= count() || index < 0)
2315 v = m_modelObjects[index]->getValue(m_roles[role]);
2317 v = m_listModel->getProperty(index, role,
this, engine());
2322QHash<
int, QByteArray> QQmlListModel::roleNames()
const
2324 QHash<
int, QByteArray> roleNames;
2326 if (m_dynamicRoles) {
2327 const auto size = m_roles.size();
2328 roleNames.reserve(size);
2329 for (
int i = 0 ; i < size ; ++i)
2330 roleNames.insert(i, m_roles.at(i).toUtf8());
2332 const auto size = m_listModel->roleCount();
2333 roleNames.reserve(size);
2334 for (
int i = 0 ; i < size; ++i) {
2335 const ListLayout::Role &r = m_listModel->getExistingRole(i);
2336 roleNames.insert(i, r.name.toUtf8());
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369void QQmlListModel::setDynamicRoles(
bool enableDynamicRoles)
2371 if (m_mainThread && m_agent ==
nullptr) {
2372 if (enableDynamicRoles) {
2373 if (m_layout && m_layout->roleCount())
2374 qmlWarning(
this) << tr(
"unable to enable dynamic roles as this model is not empty");
2376 m_dynamicRoles =
true;
2378 if (m_roles.size()) {
2379 qmlWarning(
this) << tr(
"unable to enable static roles as this model is not empty");
2381 m_dynamicRoles =
false;
2385 qmlWarning(
this) << tr(
"dynamic role setting must be made from the main thread, before any worker scripts are created");
2390
2391
2392
2393int QQmlListModel::count()
const
2395 return m_dynamicRoles ? m_modelObjects.size() : m_listModel->elementCount();
2399
2400
2401
2402
2403
2404
2405
2406void QQmlListModel::clear()
2408 removeElements(0, count());
2412
2413
2414
2415
2416
2417
2418void QQmlListModel::remove(QQmlV4FunctionPtr args)
2420 int argLength = args->length();
2422 if (argLength == 1 || argLength == 2) {
2423 QV4::Scope scope(args->v4engine());
2424 int index = QV4::ScopedValue(scope, (*args)[0])->toInt32();
2425 int removeCount = (argLength == 2 ? QV4::ScopedValue(scope, (*args)[1])->toInt32() : 1);
2427 if (index < 0 || index+removeCount > count() || removeCount <= 0) {
2428 qmlWarning(
this) << tr(
"remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count());
2432 removeElements(index, removeCount);
2434 qmlWarning(
this) << tr(
"remove: incorrect number of arguments");
2438void QQmlListModel::removeElements(
int index,
int removeCount)
2440 Q_ASSERT(index >= 0 && removeCount >= 0);
2446 beginRemoveRows(QModelIndex(), index, index + removeCount - 1);
2448 QList<std::function<
void()>> toDestroy;
2449 if (m_dynamicRoles) {
2450 for (
int i=0 ; i < removeCount ; ++i) {
2451 auto modelObject = m_modelObjects[index+i];
2452 toDestroy.append([modelObject](){
2456 m_modelObjects.remove(index, removeCount);
2458 toDestroy = m_listModel->remove(index, removeCount);
2463 emit countChanged();
2465 for (
const auto &destroyer : std::as_const(toDestroy))
2469void QQmlListModel::updateTranslations()
2476 Q_ASSERT(m_listModel);
2479 for (
int i = 0, end = m_listModel->roleCount(); i != end; ++i) {
2480 if (m_listModel->getExistingRole(i).type == ListLayout::Role::String)
2484 if (!roles.isEmpty())
2485 emitItemsChanged(0, rowCount(QModelIndex()), roles);
2487 m_listModel->updateTranslations();
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2506void QQmlListModel::insert(QQmlV4FunctionPtr args)
2508 if (args->length() == 2) {
2509 QV4::Scope scope(args->v4engine());
2510 QV4::ScopedValue arg0(scope, (*args)[0]);
2511 int index = arg0->toInt32();
2513 if (index < 0 || index > count()) {
2514 qmlWarning(
this) << tr(
"insert: index %1 out of range").arg(index);
2518 QV4::ScopedObject argObject(scope, (*args)[1]);
2519 QV4::ScopedArrayObject objectArray(scope, (*args)[1]);
2521 QV4::ScopedObject argObject(scope);
2523 int objectArrayLength = objectArray->getLength();
2524 emitItemsAboutToBeInserted(index, objectArrayLength);
2525 for (
int i=0 ; i < objectArrayLength ; ++i) {
2526 argObject = objectArray->get(i);
2528 if (m_dynamicRoles) {
2529 m_modelObjects.insert(index+i, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2531 m_listModel->insert(index+i, argObject);
2534 emitItemsInserted();
2535 }
else if (argObject) {
2536 emitItemsAboutToBeInserted(index, 1);
2538 if (m_dynamicRoles) {
2539 m_modelObjects.insert(index, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2541 m_listModel->insert(index, argObject);
2544 emitItemsInserted();
2546 qmlWarning(
this) << tr(
"insert: value is not an object");
2549 qmlWarning(
this) << tr(
"insert: value is not an object");
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567void QQmlListModel::move(
int from,
int to,
int n)
2569 if (n == 0 || from == to)
2571 if (!canMove(from, to, n)) {
2572 qmlWarning(
this) << tr(
"move: out of range");
2577 beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to);
2579 if (m_dynamicRoles) {
2581 int realFrom = from;
2594 std::rotate(m_modelObjects.begin() + realFrom,
2595 m_modelObjects.begin() + realFrom + realN,
2596 m_modelObjects.begin() + realTo + realN);
2599 m_listModel->move(from, to, n);
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618void QQmlListModel::append(QQmlV4FunctionPtr args)
2620 if (args->length() == 1) {
2621 QV4::Scope scope(args->v4engine());
2622 QV4::ScopedObject argObject(scope, (*args)[0]);
2623 QV4::ScopedArrayObject objectArray(scope, (*args)[0]);
2626 QV4::ScopedObject argObject(scope);
2628 int objectArrayLength = objectArray->getLength();
2629 if (objectArrayLength > 0) {
2630 int index = count();
2631 emitItemsAboutToBeInserted(index, objectArrayLength);
2633 for (
int i=0 ; i < objectArrayLength ; ++i) {
2634 argObject = objectArray->get(i);
2636 if (m_dynamicRoles) {
2637 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2639 m_listModel->append(argObject);
2643 emitItemsInserted();
2645 }
else if (argObject) {
2648 if (m_dynamicRoles) {
2649 index = m_modelObjects.size();
2650 emitItemsAboutToBeInserted(index, 1);
2651 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2653 index = m_listModel->elementCount();
2654 emitItemsAboutToBeInserted(index, 1);
2655 m_listModel->append(argObject);
2658 emitItemsInserted();
2660 qmlWarning(
this) << tr(
"append: value is not an object");
2663 qmlWarning(
this) << tr(
"append: value is not an object");
2668
2669
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
2699QJSValue QQmlListModel::get(
int index)
const
2701 QV4::Scope scope(engine());
2702 QV4::ScopedValue result(scope, QV4::Value::undefinedValue());
2704 if (index >= 0 && index < count()) {
2706 if (m_dynamicRoles) {
2707 DynamicRoleModelNode *object = m_modelObjects[index];
2708 result = QV4::QObjectWrapper::wrap(scope.engine, object);
2710 QObject *object = m_listModel->getOrCreateModelObject(
const_cast<QQmlListModel *>(
this), index);
2711 QQmlData *ddata = QQmlData::get(object);
2712 if (ddata->jsWrapper.isNullOrUndefined()) {
2713 result = scope.engine->memoryManager->allocate<QV4::ModelObject>(object,
const_cast<QQmlListModel *>(
this));
2715 ddata->jsWrapper.set(scope.engine, result);
2717 result = ddata->jsWrapper.value();
2722 return QJSValuePrivate::fromReturnedValue(result->asReturnedValue());
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741void QQmlListModel::set(
int index,
const QJSValue &value)
2743 QV4::Scope scope(engine());
2744 QV4::ScopedObject object(scope, QJSValuePrivate::asReturnedValue(&value));
2747 qmlWarning(
this) << tr(
"set: value is not an object");
2750 if (index > count() || index < 0) {
2751 qmlWarning(
this) << tr(
"set: index %1 out of range").arg(index);
2756 if (index == count()) {
2757 emitItemsAboutToBeInserted(index, 1);
2759 if (m_dynamicRoles) {
2760 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(object),
this));
2762 m_listModel->insert(index, object);
2765 emitItemsInserted();
2770 if (m_dynamicRoles) {
2771 m_modelObjects[index]->updateValues(scope.engine->variantMapFromJS(object), roles);
2773 m_listModel->set(index, object, &roles);
2777 emitItemsChanged(index, 1, roles);
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794void QQmlListModel::setProperty(
int index,
const QString& property,
const QVariant& value)
2796 if (count() == 0 || index >= count() || index < 0) {
2797 qmlWarning(
this) << tr(
"set: index %1 out of range").arg(index);
2801 if (m_dynamicRoles) {
2802 int roleIndex = m_roles.indexOf(property);
2803 if (roleIndex == -1) {
2804 roleIndex = m_roles.size();
2805 m_roles.append(property);
2807 if (m_modelObjects[index]->setValue(property.toUtf8(), value))
2808 emitItemsChanged(index, 1, QList<
int>(1, roleIndex));
2810 int roleIndex = m_listModel->setOrCreateProperty(index, property, value);
2811 if (roleIndex != -1)
2812 emitItemsChanged(index, 1, QList<
int>(1, roleIndex));
2817
2818
2819
2820
2821
2822void QQmlListModel::sync()
2827 qmlWarning(
this) <<
"List sync() can only be called from a WorkerScript";
2831 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
2832 const QV4::CompiledData::Binding *binding)
2834 if (binding->type() >= QV4::CompiledData::Binding::Type_Object) {
2835 const quint32 targetObjectIndex = binding->value.objectIndex;
2836 const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
2837 QString objName = compilationUnit->stringAt(target->inheritedTypeNameIndex);
2838 if (objName != listElementTypeName) {
2839 const QMetaObject *mo = resolveType(objName);
2840 if (mo != &QQmlListElement::staticMetaObject) {
2841 error(target, QQmlListModel::tr(
"ListElement: cannot contain nested elements"));
2844 listElementTypeName = objName;
2847 if (!compilationUnit->stringAt(target->idNameIndex).isEmpty()) {
2848 error(target->locationOfIdProperty, QQmlListModel::tr(
"ListElement: cannot use reserved \"id\" property"));
2852 const QV4::CompiledData::Binding *binding = target->bindingTable();
2853 for (quint32 i = 0; i < target->nBindings; ++i, ++binding) {
2854 QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
2855 if (propName.isEmpty()) {
2856 error(binding, QQmlListModel::tr(
"ListElement: cannot contain nested elements"));
2859 if (!verifyProperty(compilationUnit, binding))
2862 }
else if (binding->type() == QV4::CompiledData::Binding::Type_Script) {
2863 QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
2864 if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) {
2866 evaluateEnum(scriptStr, &ok);
2868 error(binding, QQmlListModel::tr(
"ListElement: cannot use script for property value"));
2878 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
2879 const QV4::CompiledData::Binding *binding,
ListModel *model, QQmlListModel *owner,
2880 int outterElementIndex)
2882 const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex);
2884 bool roleSet =
false;
2885 const QV4::CompiledData::Binding::Type bindingType = binding->type();
2886 if (bindingType >= QV4::CompiledData::Binding::Type_Object) {
2887 const quint32 targetObjectIndex = binding->value.objectIndex;
2888 const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
2891 if (outterElementIndex == -1) {
2894 const ListLayout::
Role &role = model->getOrCreateListRole(elementName);
2897 if (subModel ==
nullptr) {
2898 subModel =
new ListModel(role.subLayout,
nullptr);
2899 model->setOrCreateProperty(
2900 outterElementIndex, elementName,
2901 QVariant::fromValue(subModel));
2908 const QV4::CompiledData::Binding *subBinding = target->bindingTable();
2909 for (quint32 i = 0; i < target->nBindings; ++i, ++subBinding) {
2910 roleSet |= applyProperty(compilationUnit, subBinding, subModel, owner, elementIndex);
2912 }
else if (!model) {
2917 const bool isTranslationBinding = binding->isTranslationBinding();
2918 if (isTranslationBinding) {
2919 value = QVariant::fromValue<
const QV4::CompiledData::Binding*>(binding);
2920 }
else if (binding->evaluatesToString()) {
2921 value = compilationUnit->bindingValueAsString(binding);
2922 }
else if (bindingType == QV4::CompiledData::Binding::Type_Number) {
2923 value = compilationUnit->bindingValueAsNumber(binding);
2924 }
else if (bindingType == QV4::CompiledData::Binding::Type_Boolean) {
2925 value = binding->valueAsBoolean();
2926 }
else if (bindingType == QV4::CompiledData::Binding::Type_Null) {
2927 value = QVariant::fromValue(
nullptr);
2928 }
else if (bindingType == QV4::CompiledData::Binding::Type_Script) {
2929 QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
2930 if (definesEmptyList(scriptStr)) {
2931 value = QVariant::fromValue(
2932 new ListModel(model->getOrCreateListRole(elementName).subLayout,
nullptr));
2933 }
else if (binding->isFunctionExpression()) {
2934 QQmlBinding::Identifier id = binding->value.compiledScriptIndex;
2935 Q_ASSERT(id != QQmlBinding::Invalid);
2937 auto v4 = compilationUnit->engine;
2938 QV4::Scope scope(v4);
2940 if (model->m_modelCache ==
nullptr) {
2941 model->m_modelCache =
new QQmlListModel(owner, model, v4);
2942 QQmlEngine::setContextForObject(
2943 model->m_modelCache, QQmlEngine::contextForObject(owner));
2947 QV4::ScopedContext context(scope, QV4::QmlContext::create(v4->rootContext(), QQmlContextData::get(qmlContext(model->m_modelCache)),
nullptr));
2948 QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(context, compilationUnit->runtimeFunctions[id]));
2951 QV4::ScopedValue result(scope, function->call(v4->globalObject,
nullptr, 0));
2952 if (v4->hasException)
2953 v4->catchException();
2955 QJSValuePrivate::setValue(&v, result);
2959 value = evaluateEnum(scriptStr, &ok);
2965 model->setOrCreateProperty(outterElementIndex, elementName, value);
2966 auto listModel = model->m_modelCache;
2967 if (isTranslationBinding && listModel) {
2968 if (!listModel->translationChangeHandler) {
2969 auto ep = QQmlEnginePrivate::get(compilationUnit->engine);
2970 model->m_modelCache->translationChangeHandler = std::make_unique<QPropertyNotifier>(
2971 ep->translationLanguage.addNotifier([listModel](){ listModel->updateTranslations(); }));
2980 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
2981 const QList<
const QV4::CompiledData::Binding *> &bindings)
2983 listElementTypeName = QString();
2985 for (
const QV4::CompiledData::Binding *binding : bindings) {
2986 QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
2987 if (!propName.isEmpty()) {
2988 error(binding, QQmlListModel::tr(
"ListModel: undefined property '%1'").arg(propName));
2991 if (!verifyProperty(compilationUnit, binding))
2997 QObject *obj,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
2998 const QList<
const QV4::CompiledData::Binding *> &bindings)
3000 QQmlListModel *rv =
static_cast<QQmlListModel *>(obj);
3002 rv->m_engine = qmlEngine(rv)->handle();
3003 rv->m_compilationUnit = compilationUnit;
3005 bool setRoles =
false;
3007 for (
const QV4::CompiledData::Binding *binding : bindings) {
3008 if (binding->type() != QV4::CompiledData::Binding::Type_Object)
3010 setRoles |= applyProperty(
3011 compilationUnit, binding, rv->m_listModel, rv, -1);
3014 if (setRoles ==
false)
3015 qmlWarning(obj) <<
"All ListElement declarations are empty, no roles can be created unless dynamicRoles is set.";
3020 if (s.startsWith(QLatin1Char(
'[')) && s.endsWith(QLatin1Char(
']'))) {
3021 for (
auto c : s.sliced(1).chopped(1)) {
3032
3033
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
3080#include "moc_qqmllistmodel_p_p.cpp"
3082#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)