8#include <private/qjsvalue_p.h>
10#include <private/qqmlcustomparser_p.h>
11#include <private/qqmlengine_p.h>
12#include <private/qqmljsast_p.h>
13#include <private/qqmljsengine_p.h>
14#include <private/qqmllistwrapper_p.h>
15#include <private/qqmlnotifier_p.h>
16#include <private/qqmlopenmetaobject_p.h>
18#include <private/qv4alloca_p.h>
19#include <private/qv4dateobject_p.h>
20#include <private/qv4lookup_p.h>
21#include <private/qv4object_p.h>
22#include <private/qv4objectiterator_p.h>
23#include <private/qv4qmlcontext_p.h>
24#include <private/qv4sequenceobject_p.h>
25#include <private/qv4urlobject_p.h>
27#include <qqmlcontext.h>
30#include <QtCore/qdebug.h>
31#include <QtCore/qstack.h>
32#include <QXmlStreamReader>
33#include <QtCore/qdatetime.h>
34#include <QScopedValueRollback>
41enum { MIN_LISTMODEL_UID = 1024 };
48 for (size_t i=0 ; i <
sizeof(T) ; ++i) {
58 static const QString roleTypeNames[] = {
59 QStringLiteral(
"String"), QStringLiteral(
"Number"), QStringLiteral(
"Bool"),
60 QStringLiteral(
"List"), QStringLiteral(
"QObject"), QStringLiteral(
"VariantMap"),
61 QStringLiteral(
"DateTime"), QStringLiteral(
"Url"), QStringLiteral(
"Function")
65 return roleTypeNames[t];
72 QStringHash<Role *>::Node *node = roleHash.findNode(key);
74 const Role &r = *node->value;
76 qmlWarning(
nullptr) << QStringLiteral(
"Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type));
80 return createRole(key, type);
85 QStringHash<Role *>::Node *node = roleHash.findNode(key);
87 const Role &r = *node->value;
89 qmlWarning(
nullptr) << QStringLiteral(
"Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type));
93 QString qkey = key->toQString();
95 return createRole(qkey, type);
100 const int dataSizes[] = {
101 sizeof(StringOrTranslation),
105 sizeof(QV4::PersistentValue),
111 const int dataAlignments[] = {
112 alignof(StringOrTranslation),
115 alignof(ListModel *),
116 alignof(QV4::PersistentValue),
117 alignof(QVariantMap),
133 int dataSize = dataSizes[type];
134 int dataAlignment = dataAlignments[type];
136 int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1);
140 currentBlockOffset = dataSize;
144 currentBlockOffset = dataOffset + dataSize;
147 int roleIndex = roles.size();
151 roleHash.insert(key, r);
158 const int otherRolesCount = other->roles.size();
159 roles.reserve(otherRolesCount);
160 for (
int i=0 ; i < otherRolesCount; ++i) {
161 Role *role =
new Role(other->roles[i]);
163 roleHash.insert(role->name, role);
165 currentBlockOffset = other->currentBlockOffset;
166 currentBlock = other->currentBlock;
176 int roleOffset = target->roles.size();
177 int newRoleCount = src->roles.size() - roleOffset;
179 for (
int i=0 ; i < newRoleCount ; ++i) {
180 Role *role =
new Role(src->roles[roleOffset + i]);
181 target->roles.append(role);
182 target->roleHash.insert(role->name, role);
185 target->currentBlockOffset = src->currentBlockOffset;
186 target->currentBlock = src->currentBlock;
211 switch (data.userType()) {
214 case QMetaType::Bool: type =
Role::Bool;
break;
218 case QMetaType::QUrl: type =
Role::Url;
break;
220 if (data.userType() == qMetaTypeId<QJSValue>() &&
221 data.value<QJSValue>().isCallable()) {
224 }
else if (data.userType() == qMetaTypeId<
const QV4::CompiledData::Binding*>()
225 && data.value<
const QV4::CompiledData::Binding*>()->isTranslationBinding()) {
228 }
else if (data.userType() >= QMetaType::User) {
239 qmlWarning(
nullptr) <<
"Can't create role for unsupported data type";
243 return &getRoleOrCreate(key, type);
249 QStringHash<Role *>::Node *node = roleHash.findNode(key);
258 QStringHash<Role *>::Node *node = roleHash.findNode(key);
274 QString mutableString(s);
275 QString::DataPointer dataPointer = mutableString.data_ptr();
276 arrayData = dataPointer->d_ptr();
277 stringData = dataPointer->data();
278 stringSize = mutableString.size();
286 this->binding = binding;
294 return QString(QStringPrivate(arrayData, stringData, stringSize));
298 return owner->m_compilationUnit->bindingValueAsString(binding);
306 return QString(QStringPrivate(arrayData, stringData, stringSize));
311 if (arrayData && !arrayData->deref())
312 QTypedArrayData<ushort>::deallocate(arrayData);
314 stringData =
nullptr;
322 if (e->m_objectCache ==
nullptr) {
323 void *memory = operator
new(
sizeof(QObject) +
sizeof(QQmlData));
324 void *ddataMemory = ((
char *)memory) +
sizeof(
QObject);
325 e->m_objectCache =
new (memory) QObject;
327 const QAbstractDeclarativeData *old = std::exchange(
328 QObjectPrivate::get(e->m_objectCache)->declarativeData,
329 new (ddataMemory) QQmlData(QQmlData::DoesNotOwnMemory));
334 return e->m_objectCache;
341 bool hasChanges =
false;
344 QHash<
int, ElementSync> elementHash;
345 for (
int i = 0; i < target->elements.count(); ++i) {
347 int uid = e->getUid();
350 sync.targetIndex = i;
351 elementHash.insert(uid, sync);
353 for (
int i = 0; i < src->elements.count(); ++i) {
355 int uid = e->getUid();
357 QHash<
int, ElementSync>::iterator it = elementHash.find(uid);
358 if (it == elementHash.end()) {
362 elementHash.insert(uid, sync);
364 ElementSync &sync = it.value();
370 QQmlListModel *targetModel = target->m_modelCache;
374 for (
int i = 0 ; i < target->elements.count() ; ++i) {
376 ElementSync &s = elementHash.find(element->getUid()).value();
377 Q_ASSERT(s.targetIndex >= 0);
379 s.targetIndex -= rowsRemoved;
380 if (s.src ==
nullptr) {
381 Q_ASSERT(s.targetIndex == i);
384 targetModel->beginRemoveRows(QModelIndex(), i, i);
385 s.target->destroy(target->m_layout);
386 target->elements.removeOne(s.target);
389 targetModel->endRemoveRows();
400 target->elements.clear();
401 for (
int i = 0; i < src->elements.count(); ++i) {
403 ElementSync &s = elementHash.find(srcElement->getUid()).value();
404 Q_ASSERT(s.srcIndex >= 0);
406 if (targetElement ==
nullptr) {
409 s.changedRoles = ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout);
410 target->elements.append(targetElement);
413 target->updateCacheIndices();
416 for (
int i=0 ; i < target->elements.count() ; ++i) {
427 int rowsInserted = 0;
428 const int targetElementCount = target->elements.count();
429 for (
int i = 0 ; i < targetElementCount ; ++i) {
431 ElementSync &s = elementHash.find(element->getUid()).value();
432 Q_ASSERT(s.srcIndex >= 0);
433 s.srcIndex += rowsInserted;
434 if (s.srcIndex != s.targetIndex) {
436 if (s.targetIndex == -1) {
437 targetModel->beginInsertRows(QModelIndex(), i, i);
438 targetModel->endInsertRows();
441 bool validMove = targetModel->beginMoveRows(QModelIndex(), s.targetIndex, s.targetIndex, QModelIndex(), i);
443 targetModel->endMoveRows();
445 for (
int j=i+1; j < targetElementCount; ++j) {
447 ElementSync &sToFix = elementHash.find(eToFix->getUid()).value();
448 if (i < s.targetIndex) {
450 if (sToFix.targetIndex > s.targetIndex || sToFix.targetIndex < i)
453 sToFix.targetIndex += 1;
456 if (sToFix.targetIndex < s.targetIndex || sToFix.targetIndex > i)
459 sToFix.targetIndex -= 1;
466 if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) {
467 QModelIndex idx = targetModel->createIndex(i, 0);
469 targetModel->dataChanged(idx, idx, s.changedRoles);
482 for (
const auto &destroyer : remove(0, elements.count()))
486 if (m_modelCache && m_modelCache->m_primary ==
false)
488 m_modelCache =
nullptr;
493 int elementIndex = elements.count();
494 newElement(elementIndex);
501 updateCacheIndices(index);
515 QPODVector<ListElement *, 4> store;
516 for (
int i=0 ; i < (to-from) ; ++i)
517 store.append(elements[from+n+i]);
518 for (
int i=0 ; i < n ; ++i)
519 store.append(elements[from+i]);
520 for (
int i=0 ; i < store.count() ; ++i)
521 elements[from+i] = store[i];
523 updateCacheIndices(from, to + n);
529 elements.insert(index, e);
532void ListModel::updateCacheIndices(
int start,
int end)
534 int count = elements.count();
536 if (end < 0 || end > count)
539 for (
int i = start; i < end; ++i) {
552 return e->getProperty(r, owner, eng);
558 return e->getListProperty(role);
563 for (
int index = 0; index != elements.count(); ++index) {
572void ListModel::
set(
int elementIndex, QV4::Object *object, QVector<
int> *roles)
576 QV4::ExecutionEngine *v4 = object->engine();
577 QV4::Scope scope(v4);
578 QV4::ScopedObject o(scope);
580 QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
581 QV4::ScopedString propertyName(scope);
582 QV4::ScopedValue propertyValue(scope);
584 propertyName = it.nextPropertyNameAsString(propertyValue);
592 if (
const QV4::String *s = propertyValue->as<QV4::String>()) {
593 const ListLayout::
Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String);
594 roleIndex = e->setStringProperty(r, s->toQString());
595 }
else if (propertyValue->isNumber()) {
596 const ListLayout::
Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number);
597 roleIndex = e->setDoubleProperty(r, propertyValue->asDouble());
598 }
else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) {
599 roleIndex = setArrayLike(&o, propertyName, e, a);
600 }
else if (QV4::Sequence *s = propertyValue->as<QV4::Sequence>()) {
601 roleIndex = setArrayLike(&o, propertyName, e, s);
602 }
else if (QV4::QmlListWrapper *l = propertyValue->as<QV4::QmlListWrapper>()) {
603 roleIndex = setArrayLike(&o, propertyName, e, l);
604 }
else if (propertyValue->isBoolean()) {
605 const ListLayout::
Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool);
606 roleIndex = e->setBoolProperty(r, propertyValue->booleanValue());
607 }
else if (QV4::DateObject *dd = propertyValue->as<QV4::DateObject>()) {
608 const ListLayout::
Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime);
609 QDateTime dt = dd->toQDateTime();
610 roleIndex = e->setDateTimeProperty(r, dt);
611 }
else if (QV4::UrlObject *url = propertyValue->as<QV4::UrlObject>()) {
612 const ListLayout::
Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url);
613 QUrl qurl = QUrl(url->href());
614 roleIndex = e->setUrlProperty(r, qurl);
615 }
else if (QV4::FunctionObject *f = propertyValue->as<QV4::FunctionObject>()) {
616 const ListLayout::
Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Function);
617 QV4::ScopedFunctionObject func(scope, f);
619 QJSValuePrivate::setValue(&jsv, func);
620 roleIndex = e->setFunctionProperty(r, jsv);
621 }
else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
622 if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) {
623 const ListLayout::
Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
624 if (role.type == ListLayout::Role::QObject)
625 roleIndex = e->setQObjectProperty(role, wrapper);
626 }
else if (QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
628 QV4::Value::fromReturnedValue(o->asReturnedValue()),
629 QMetaType::fromType<QUrl>(),
true);
630 maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
631 const ListLayout::
Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url);
632 QUrl qurl = maybeUrl.toUrl();
633 roleIndex = e->setUrlProperty(r, qurl);
635 const ListLayout::
Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap);
637 QV4::ScopedObject obj(scope, o);
638 roleIndex = e->setVariantMapProperty(role, obj);
641 }
else if (propertyValue->isNullOrUndefined()) {
642 const ListLayout::
Role *r = m_layout->getExistingRole(propertyName);
644 e->clearProperty(*r);
648 roles->append(roleIndex);
662 QV4::ExecutionEngine *v4 = object->engine();
663 QV4::Scope scope(v4);
665 QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly);
666 QV4::ScopedString propertyName(scope);
667 QV4::ScopedValue propertyValue(scope);
668 QV4::ScopedObject o(scope);
670 propertyName = it.nextPropertyNameAsString(propertyValue);
675 if (QV4::String *s = propertyValue->stringValue()) {
676 const ListLayout::
Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String);
677 if (r.type == ListLayout::Role::String)
678 e->setStringPropertyFast(r, s->toQString());
679 }
else if (propertyValue->isNumber()) {
680 const ListLayout::
Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number);
682 e->setDoublePropertyFast(r, propertyValue->asDouble());
684 }
else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) {
685 setArrayLikeFast(&o, propertyName, e, a);
686 }
else if (QV4::Sequence *s = propertyValue->as<QV4::Sequence>()) {
687 setArrayLikeFast(&o, propertyName, e, s);
688 }
else if (QV4::QmlListWrapper *l = propertyValue->as<QV4::QmlListWrapper>()) {
689 setArrayLikeFast(&o, propertyName, e, l);
690 }
else if (propertyValue->isBoolean()) {
691 const ListLayout::
Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool);
693 e->setBoolPropertyFast(r, propertyValue->booleanValue());
695 }
else if (QV4::DateObject *date = propertyValue->as<QV4::DateObject>()) {
696 const ListLayout::
Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime);
698 QDateTime dt = date->toQDateTime();
699 e->setDateTimePropertyFast(r, dt);
701 }
else if (QV4::UrlObject *url = propertyValue->as<QV4::UrlObject>()){
702 const ListLayout::
Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url);
704 QUrl qurl = QUrl(url->href());
705 e->setUrlPropertyFast(r, qurl);
707 }
else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
708 if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) {
709 const ListLayout::
Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
710 if (r.type == ListLayout::Role::QObject)
711 e->setQObjectPropertyFast(r, wrapper);
713 QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
715 QV4::Value::fromReturnedValue(o->asReturnedValue()),
716 QMetaType::fromType<QUrl>(),
true);
717 if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
718 const QUrl qurl = maybeUrl.toUrl();
719 const ListLayout::
Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url);
721 e->setUrlPropertyFast(r, qurl);
723 const ListLayout::
Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap);
724 if (role.type == ListLayout::Role::VariantMap)
725 e->setVariantMapFast(role, o);
728 }
else if (propertyValue->isNullOrUndefined()) {
731 auto memberName = propertyName->toString(v4)->toQString();
732 err.setDescription(QString::fromLatin1(
"%1 is %2. Adding an object with a %2 member does not create a role for it.").arg(memberName, propertyValue->isNull() ? QLatin1String(
"null") : QLatin1String(
"undefined")));
733 qmlWarning(
nullptr, err);
735 const ListLayout::
Role *r = m_layout->getExistingRole(propertyName);
737 e->clearProperty(*r);
743QVector<std::function<
void()>> ListModel::remove(
int index,
int count)
745 QVector<std::function<
void()>> toDestroy;
746 auto layout = m_layout;
747 for (
int i=0 ; i < count ; ++i) {
748 auto element = elements[index+i];
749 toDestroy.append([element, layout](){
750 element->destroy(layout);
754 elements.remove(index, count);
755 updateCacheIndices(index);
762 set(elementIndex, object, SetElement::WasJustInserted);
768 set(elementIndex, object, SetElement::WasJustInserted);
772int ListModel::setOrCreateProperty(
int elementIndex,
const QString &key,
const QVariant &data)
776 if (elementIndex >= 0 && elementIndex < elements.count()) {
781 roleIndex = e->setVariantProperty(*r, data);
785 if (roleIndex != -1 && cache)
793int ListModel::setExistingProperty(
int elementIndex,
const QString &key,
const QV4::Value &data, QV4::ExecutionEngine *eng)
797 if (elementIndex >= 0 && elementIndex < elements.count()) {
801 roleIndex = e->setJsProperty(*r, data, eng);
812 if (e->next ==
nullptr) {
828 return ModelNodeMetaObject::get(m_objectCache);
833 char *mem = getPropertyMemory(role);
840 char *mem = getPropertyMemory(role);
841 QV4::PersistentValue *g =
reinterpret_cast<QV4::PersistentValue *>(mem);
842 return g->as<QV4::QObjectWrapper>();
847 QVariantMap *map =
nullptr;
849 char *mem = getPropertyMemory(role);
850 if (isMemoryUsed<QVariantMap>(mem))
851 map =
reinterpret_cast<QVariantMap *>(mem);
858 QDateTime *dt =
nullptr;
860 char *mem = getPropertyMemory(role);
861 if (isMemoryUsed<QDateTime>(mem))
862 dt =
reinterpret_cast<QDateTime *>(mem);
871 char *mem = getPropertyMemory(role);
872 if (isMemoryUsed<QUrl>(mem))
873 url =
reinterpret_cast<QUrl *>(mem);
880 QJSValue *f =
nullptr;
882 char *mem = getPropertyMemory(role);
883 if (isMemoryUsed<QJSValue>(mem))
884 f =
reinterpret_cast<QJSValue *>(mem);
889QV4::PersistentValue *
892 char *mem = getPropertyMemory(role);
894 bool existingGuard =
false;
895 for (size_t i = 0; i <
sizeof(QV4::PersistentValue);
898 existingGuard =
true;
903 QV4::PersistentValue *g =
nullptr;
906 g =
reinterpret_cast<QV4::PersistentValue *>(mem);
913 char *mem = getPropertyMemory(role);
920 char *mem = getPropertyMemory(role);
927 double *value =
reinterpret_cast<
double *>(mem);
935 data = value->toString(owner);
942 bool *value =
reinterpret_cast<
bool *>(mem);
952 if (model->m_modelCache ==
nullptr) {
953 model->m_modelCache =
new QQmlListModel(owner, model, eng);
954 QQmlEngine::setContextForObject(model->m_modelCache, QQmlEngine::contextForObject(owner));
957 QObject *object = model->m_modelCache;
958 data = QVariant::fromValue(object);
964 QV4::PersistentValue *guard =
reinterpret_cast<QV4::PersistentValue *>(mem);
965 data = QVariant::fromValue(guard->as<QV4::QObjectWrapper>()->object());
970 if (isMemoryUsed<QVariantMap>(mem)) {
971 QVariantMap *map =
reinterpret_cast<QVariantMap *>(mem);
978 if (isMemoryUsed<QDateTime>(mem)) {
979 QDateTime *dt =
reinterpret_cast<QDateTime *>(mem);
986 if (isMemoryUsed<QUrl>(mem)) {
987 QUrl *url =
reinterpret_cast<QUrl *>(mem);
994 if (isMemoryUsed<QJSValue>(mem)) {
995 QJSValue *func =
reinterpret_cast<QJSValue *>(mem);
996 data = QVariant::fromValue(*func);
1012 char *mem = getPropertyMemory(role);
1018 changed = c->asString().compare(s) != 0;
1032 char *mem = getPropertyMemory(role);
1033 double *value =
reinterpret_cast<
double *>(mem);
1034 bool changed = *value != d;
1048 char *mem = getPropertyMemory(role);
1049 bool *value =
reinterpret_cast<
bool *>(mem);
1050 bool changed = *value != b;
1064 char *mem = getPropertyMemory(role);
1066 if (*value && *value != m) {
1082 char *mem = getPropertyMemory(role);
1083 if (isMemoryUsed<QVariantMap>(mem))
1084 reinterpret_cast<QV4::PersistentValue *>(mem)->set(o->engine(), *o);
1086 new (mem) QV4::PersistentValue(o->engine(), o);
1098 char *mem = getPropertyMemory(role);
1099 if (isMemoryUsed<QVariantMap>(mem)) {
1100 QVariantMap *map =
reinterpret_cast<QVariantMap *>(mem);
1103 new (mem) QVariantMap(o->engine()->variantMapFromJS(o));
1115 char *mem = getPropertyMemory(role);
1116 if (isMemoryUsed<QVariantMap>(mem)) {
1117 QVariantMap *map =
reinterpret_cast<QVariantMap *>(mem);
1118 if (m && map->isSharedWith(*m))
1125 new (mem) QVariantMap(*m);
1127 new (mem) QVariantMap;
1139 char *mem = getPropertyMemory(role);
1140 if (isMemoryUsed<QDateTime>(mem)) {
1141 QDateTime *dt =
reinterpret_cast<QDateTime *>(mem);
1144 new (mem) QDateTime(dt);
1156 char *mem = getPropertyMemory(role);
1157 if (isMemoryUsed<QUrl>(mem)) {
1158 QUrl *qurl =
reinterpret_cast<QUrl *>(mem);
1161 new (mem) QUrl(url);
1173 char *mem = getPropertyMemory(role);
1174 if (isMemoryUsed<QJSValue>(mem)) {
1175 QJSValue *f =
reinterpret_cast<QJSValue *>(mem);
1178 new (mem) QJSValue(f);
1190 char *mem = getPropertyMemory(role);
1192 s->setTranslation(b);
1202 char *mem = getPropertyMemory(role);
1208 char *mem = getPropertyMemory(role);
1209 double *value =
new (mem)
double;
1215 char *mem = getPropertyMemory(role);
1216 bool *value =
new (mem)
bool;
1222 char *mem = getPropertyMemory(role);
1223 new (mem) QV4::PersistentValue(o->engine(), o);
1228 char *mem = getPropertyMemory(role);
1229 ListModel **value =
new (mem) ListModel *;
1235 char *mem = getPropertyMemory(role);
1236 QVariantMap *map =
new (mem) QVariantMap;
1237 *map = o->engine()->variantMapFromJS(o);
1242 char *mem = getPropertyMemory(role);
1243 new (mem) QDateTime(dt);
1248 char *mem = getPropertyMemory(role);
1249 new (mem) QUrl(url);
1254 char *mem = getPropertyMemory(role);
1255 new (mem) QJSValue(f);
1260 switch (role
.type) {
1261 case ListLayout::Role::String:
1262 setStringProperty(role, QString());
1265 setDoubleProperty(role, 0.0);
1268 setBoolProperty(role,
false);
1271 setListProperty(role,
nullptr);
1273 case ListLayout::Role::QObject:
1274 setQObjectProperty(role,
nullptr);
1276 case ListLayout::Role::DateTime:
1277 setDateTimeProperty(role, QDateTime());
1279 case ListLayout::Role::Url:
1280 setUrlProperty(role, QUrl());
1282 case ListLayout::Role::VariantMap:
1283 setVariantMapProperty(role, (QVariantMap *)
nullptr);
1286 setFunctionProperty(role, QJSValue());
1295 m_objectCache =
nullptr;
1296 uid = uidCounter.fetchAndAddOrdered(1);
1298 memset(data, 0,
sizeof(data));
1303 m_objectCache =
nullptr;
1306 memset(data, 0,
sizeof(data));
1316 QVector<
int> changedRoles;
1322 switch (srcRole
.type) {
1325 ListModel *srcSubModel = src->getListProperty(srcRole);
1326 ListModel *targetSubModel = target->getListProperty(targetRole);
1329 if (targetSubModel ==
nullptr) {
1330 targetSubModel =
new ListModel(targetRole.subLayout,
nullptr);
1331 target->setListPropertyFast(targetRole, targetSubModel);
1334 roleIndex = targetRole
.index;
1340 QV4::QObjectWrapper *object = src->getQObjectProperty(srcRole);
1341 roleIndex = target->setQObjectProperty(targetRole, object);
1350 QVariant v = src->getProperty(srcRole,
nullptr,
nullptr);
1351 roleIndex = target->setVariantProperty(targetRole, v);
1356 QVariantMap *map = src->getVariantMapProperty(srcRole);
1357 roleIndex = target->setVariantMapProperty(targetRole, map);
1364 changedRoles << roleIndex;
1367 return changedRoles;
1395 if (QV4::PersistentValue *guard = getGuardProperty(r))
1396 guard->~PersistentValue();
1401 QVariantMap *map = getVariantMapProperty(r);
1408 QDateTime *dt = getDateTimeProperty(r);
1415 QUrl *url = getUrlProperty(r);
1422 QJSValue *f = getFunctionProperty(r);
1433 if (m_objectCache) {
1434 m_objectCache->~QObject();
1435 operator
delete(m_objectCache);
1440 next->destroy(
nullptr);
1448 switch (role
.type) {
1450 roleIndex = setDoubleProperty(role, d.toDouble());
1452 case ListLayout::Role::String:
1453 if (d.userType() == qMetaTypeId<
const QV4::CompiledData::Binding *>())
1454 roleIndex = setTranslationProperty(role, d.value<
const QV4::CompiledData::Binding*>());
1456 roleIndex = setStringProperty(role, d.toString());
1459 roleIndex = setBoolProperty(role, d.toBool());
1462 roleIndex = setListProperty(role, d.value<
ListModel *>());
1465 QVariantMap map = d.toMap();
1466 roleIndex = setVariantMapProperty(role, &map);
1469 case ListLayout::Role::DateTime:
1470 roleIndex = setDateTimeProperty(role, d.toDateTime());
1472 case ListLayout::Role::Url:
1473 roleIndex = setUrlProperty(role, d.toUrl());
1476 roleIndex = setFunctionProperty(role, d.value<QJSValue>());
1490 QV4::Scope scope(eng);
1494 QString qstr = d.toQString();
1495 roleIndex = setStringProperty(role, qstr);
1496 }
else if (d.isNumber()) {
1497 roleIndex = setDoubleProperty(role, d.asDouble());
1498 }
else if (d.as<QV4::ArrayObject>()) {
1499 QV4::ScopedArrayObject a(scope, d);
1501 QV4::Scope scope(a->engine());
1502 QV4::ScopedObject o(scope);
1504 ListModel *subModel =
new ListModel(role.subLayout,
nullptr);
1505 int arrayLength = a->getLength();
1506 for (
int j=0 ; j < arrayLength ; ++j) {
1508 subModel->append(o);
1510 roleIndex = setListProperty(role, subModel);
1512 qmlWarning(
nullptr) << QStringLiteral(
"Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List));
1514 }
else if (d.isBoolean()) {
1515 roleIndex = setBoolProperty(role, d.booleanValue());
1516 }
else if (d.as<QV4::DateObject>()) {
1517 QV4::Scoped<QV4::DateObject> dd(scope, d);
1518 QDateTime dt = dd->toQDateTime();
1519 roleIndex = setDateTimeProperty(role, dt);
1520 }
else if (d.as<QV4::UrlObject>()) {
1521 QV4::Scoped<QV4::UrlObject> url(scope, d);
1522 QUrl qurl = QUrl(url->href());
1523 roleIndex = setUrlProperty(role, qurl);
1524 }
else if (d.as<QV4::FunctionObject>()) {
1525 QV4::ScopedFunctionObject f(scope, d);
1527 QJSValuePrivate::setValue(&jsv, f);
1528 roleIndex = setFunctionProperty(role, jsv);
1529 }
else if (d.isObject()) {
1530 QV4::ScopedObject o(scope, d);
1531 QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>();
1533 roleIndex = setQObjectProperty(role, wrapper);
1535 roleIndex = setVariantMapProperty(role, o);
1537 QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
1539 QV4::Value::fromReturnedValue(o.asReturnedValue()),
1540 QMetaType::fromType<QUrl>(),
true);
1541 if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
1542 roleIndex = setUrlProperty(role, maybeUrl.toUrl());
1545 }
else if (d.isNullOrUndefined()) {
1546 clearProperty(role);
1558 const int roleCount = m_model->m_listModel->roleCount();
1559 QVector<QByteArray> properties;
1560 properties.reserve(roleCount);
1561 for (
int i = 0 ; i < roleCount ; ++i) {
1562 const ListLayout::
Role &role = m_model->m_listModel->getExistingRole(i);
1563 QByteArray name = role.name.toUtf8();
1566 type()->createProperties(properties);
1575#if QT_VERSION >= QT_VERSION_CHECK(7
, 0
, 0
)
1576const QMetaObject *ModelNodeMetaObject::toDynamicMetaObject(QObject *object)
const
1581 if (!m_initialized) {
1582 m_initialized =
true;
1583 const_cast<ModelNodeMetaObject *>(
this)->initialize();
1585 return QQmlOpenMetaObject::toDynamicMetaObject(object);
1590 QObjectPrivate *op = QObjectPrivate::get(obj);
1591 return static_cast<ModelNodeMetaObject*>(op->metaObject);
1596 const int roleCount = m_model->m_listModel->roleCount();
1597 if (!m_initialized) {
1599 Q_ALLOCA_VAR(
int, changedRoles, roleCount *
sizeof(
int));
1600 for (
int i = 0; i < roleCount; ++i)
1601 changedRoles[i] = i;
1602 emitDirectNotifies(changedRoles, roleCount);
1606 for (
int i=0 ; i < roleCount ; ++i) {
1607 const ListLayout::
Role &role = m_model->m_listModel->getExistingRole(i);
1608 QByteArray name = role.name.toUtf8();
1609 const QVariant &data = m_model->data(m_elementIndex, i);
1616 if (!m_initialized) {
1617 emitDirectNotifies(roles.constData(), roles.size());
1620 int roleCount = roles.size();
1621 for (
int i=0 ; i < roleCount ; ++i) {
1622 int roleIndex = roles.at(i);
1623 const ListLayout::
Role &role = m_model->m_listModel->getExistingRole(roleIndex);
1624 QByteArray name = role.name.toUtf8();
1625 const QVariant &data = m_model->data(m_elementIndex, roleIndex);
1635 QString propName = QString::fromUtf8(name(index));
1636 const QVariant value =
this->value(index);
1638 QV4::Scope scope(m_model->engine());
1639 QV4::ScopedValue v(scope, scope.engine->fromVariant(value));
1641 int roleIndex = m_model->m_listModel->setExistingProperty(m_elementIndex, propName, v, scope.engine);
1642 if (roleIndex != -1)
1643 m_model->emitItemsChanged(m_elementIndex, 1, QVector<
int>(1, roleIndex));
1649 Q_ASSERT(!m_initialized);
1650 QQmlData *ddata = QQmlData::get(object(),
false);
1654 if (!qmlEngine(m_model))
1656 for (
int i = 0; i < roleCount; ++i) {
1657 const int changedRole = changedRoles[i];
1658 QQmlNotifier::notify(ddata, changedRole);
1664bool ModelObject::virtualPut(Managed *m, PropertyKey id,
const Value &value, Value *receiver)
1667 return Object::virtualPut(m, id, value, receiver);
1668 QString propName = id.toQString();
1672 ExecutionEngine *eng = that->engine();
1673 const int elementIndex = that->d()->elementIndex();
1674 if (QQmlListModel *model = that->d()->m_model) {
1676 = model->listModel()->setExistingProperty(elementIndex, propName, value, eng);
1677 if (roleIndex != -1)
1678 model->emitItemsChanged(elementIndex, 1, QVector<
int>(1, roleIndex));
1683 mo->emitPropertyNotification(propName.toUtf8());
1687ReturnedValue
ModelObject::virtualGet(
const Managed *m, PropertyKey id,
const Value *receiver,
bool *hasProperty)
1690 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1694 ScopedString name(scope, id.asStringOrSymbol());
1695 QQmlListModel *model = that->d()->m_model;
1697 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1699 const ListLayout::
Role *role = model->listModel()->getExistingRole(name);
1701 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1703 *hasProperty =
true;
1705 if (QQmlEngine *qmlEngine = that->engine()->qmlEngine()) {
1706 QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine);
1707 if (ep && ep->propertyCapture)
1708 ep->propertyCapture->captureProperty(that->object(), -1, role
->index,
false);
1711 const int elementIndex = that->d()->elementIndex();
1712 QVariant value = model->data(elementIndex, role
->index);
1713 return that->engine()->fromVariant(value);
1716ReturnedValue
ModelObject::virtualResolveLookupGetter(
const Object *object, ExecutionEngine *engine, Lookup *lookup)
1718 lookup->call = Lookup::Call::GetterQObjectPropertyFallback;
1719 return lookup->getter(engine, *object);
1726 PropertyKey next(
const Object *o, Property *pd =
nullptr, PropertyAttributes *attrs =
nullptr)
override;
1734 ExecutionEngine *v4 = that->engine();
1736 QQmlListModel *model = that->d()->m_model;
1737 ListModel *listModel = model ? model->listModel() :
nullptr;
1739 Scope scope(that->engine());
1742 ScopedString roleName(scope, v4->newString(role.name));
1744 *attrs = QV4::Attr_Data;
1747 QVariant value = model->data(that->d()->elementIndex(), role
.index);
1748 if (
auto recursiveListModel = qvariant_cast<QQmlListModel*>(value)) {
1749 auto size = recursiveListModel->count();
1750 auto array = ScopedArrayObject{scope, v4->newArrayObject(size)};
1751 QV4::ScopedValue val(scope);
1752 for (
auto i = 0; i < size; i++) {
1753 val = QJSValuePrivate::convertToReturnedValue(
1754 v4, recursiveListModel->get(i));
1755 array->arrayPut(i, val);
1759 pd->value = v4->fromVariant(value);
1762 return roleName->toPropertyKey();
1768 return ObjectOwnPropertyKeyIterator::next(o, pd, attrs);
1771OwnPropertyKeyIterator *
ModelObject::virtualOwnPropertyKeys(
const Object *m, Value *target)
1790 object->updateValues(obj, roles);
1796 QVector<
int> changedRoles;
1797 for (
int i = 0; i < src->m_meta->count(); ++i) {
1798 const QByteArray &name = src->m_meta->name(i);
1799 QVariant value = src->m_meta->value(i);
1801 QQmlListModel *srcModel = qobject_cast<QQmlListModel *>(value.value<QObject *>());
1802 QQmlListModel *targetModel = qobject_cast<QQmlListModel *>(target->m_meta->value(i).value<QObject *>());
1804 bool modelHasChanges =
false;
1806 if (targetModel ==
nullptr)
1807 targetModel = QQmlListModel::createWithOwner(target->m_owner);
1809 modelHasChanges = QQmlListModel::sync(srcModel, targetModel);
1811 QObject *targetModelObject = targetModel;
1812 value = QVariant::fromValue(targetModelObject);
1813 }
else if (targetModel) {
1817 if (target->setValue(name, value) || modelHasChanges)
1818 changedRoles << target->m_owner->m_roles.indexOf(QString::fromUtf8(name));
1820 return changedRoles;
1825 for (
auto it = object.cbegin(), end = object.cend(); it != end; ++it) {
1826 const QString &key = it.key();
1828 int roleIndex = m_owner->m_roles.indexOf(key);
1829 if (roleIndex == -1) {
1830 roleIndex = m_owner->m_roles.size();
1831 m_owner->m_roles.append(key);
1834 QVariant value = it.value();
1838 if (value.userType() == qMetaTypeId<QJSValue>())
1839 value = value.value<QJSValue>().toVariant();
1841 if (value.userType() == QMetaType::QVariantList) {
1842 QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner);
1844 QVariantList subArray = value.toList();
1845 QVariantList::const_iterator subIt = subArray.cbegin();
1846 QVariantList::const_iterator subEnd = subArray.cend();
1847 while (subIt != subEnd) {
1848 const QVariantMap &subObject = subIt->toMap();
1849 subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1853 QObject *subModelObject = subModel;
1854 value = QVariant::fromValue(subModelObject);
1857 const QByteArray &keyUtf8 = key.toUtf8();
1859 QQmlListModel *existingModel = qobject_cast<QQmlListModel *>(m_meta->value(keyUtf8).value<QObject *>());
1860 delete existingModel;
1862 if (m_meta->setValue(keyUtf8, value))
1874 for (
int i=0 ; i < count() ; ++i) {
1875 QQmlListModel *subModel = qobject_cast<QQmlListModel *>(value(i).value<QObject *>());
1885 QVariant v = value(index);
1886 QQmlListModel *model = qobject_cast<QQmlListModel *>(v.value<QObject *>());
1895 QQmlListModel *parentModel = m_owner->m_owner;
1897 QVariant v = value(index);
1901 if (v.userType() == qMetaTypeId<QJSValue>())
1902 v= v.value<QJSValue>().toVariant();
1904 if (v.userType() == QMetaType::QVariantList) {
1905 QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel);
1907 QVariantList subArray = v.toList();
1908 QVariantList::const_iterator subIt = subArray.cbegin();
1909 QVariantList::const_iterator subEnd = subArray.cend();
1910 while (subIt != subEnd) {
1911 const QVariantMap &subObject = subIt->toMap();
1912 subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1916 QObject *subModelObject = subModel;
1917 v = QVariant::fromValue(subModelObject);
1922 int elementIndex = parentModel->m_modelObjects.indexOf(m_owner);
1923 if (elementIndex != -1) {
1924 int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData()));
1925 if (roleIndex != -1)
1926 parentModel->emitItemsChanged(elementIndex, 1, QVector<
int>(1, roleIndex));
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2029QQmlListModel::QQmlListModel(QObject *parent)
2030: QAbstractListModel(parent)
2032 m_mainThread =
true;
2035 m_dynamicRoles =
false;
2037 m_layout =
new ListLayout;
2038 m_listModel =
new ListModel(m_layout,
this);
2043QQmlListModel::QQmlListModel(
const QQmlListModel *owner, ListModel *data, QV4::ExecutionEngine *engine, QObject *parent)
2044: QAbstractListModel(parent)
2046 m_mainThread = owner->m_mainThread;
2048 m_agent = owner->m_agent;
2050 Q_ASSERT(owner->m_dynamicRoles ==
false);
2051 m_dynamicRoles =
false;
2056 m_compilationUnit = owner->m_compilationUnit;
2059QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent)
2060: QAbstractListModel(agent)
2062 m_mainThread =
false;
2065 m_dynamicRoles = orig->m_dynamicRoles;
2067 if (ListLayout *layout = orig->m_layout)
2068 m_layout =
new ListLayout(layout);
2070 m_layout =
new ListLayout;
2072 m_listModel =
new ListModel(m_layout,
this);
2077 ListModel::sync(orig->m_listModel, m_listModel);
2080 m_compilationUnit = orig->m_compilationUnit;
2083QQmlListModel::~QQmlListModel()
2085 qDeleteAll(m_modelObjects);
2088 m_listModel->destroy();
2091 if (m_mainThread && m_agent)
2092 m_agent->modelDestroyed();
2095 if (m_mainThread && m_agent)
2098 m_listModel =
nullptr;
2104QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner)
2106 QQmlListModel *model =
new QQmlListModel;
2108 model->m_mainThread = newOwner->m_mainThread;
2109 model->m_engine = newOwner->m_engine;
2110 model->m_agent = newOwner->m_agent;
2111 model->m_dynamicRoles = newOwner->m_dynamicRoles;
2113 if (model->m_mainThread && model->m_agent)
2114 model->m_agent->addref();
2116 QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner));
2121QV4::ExecutionEngine *QQmlListModel::engine()
const
2123 if (m_engine ==
nullptr) {
2124 m_engine = qmlEngine(
this)->handle();
2130bool QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target)
2132 Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles);
2134 bool hasChanges =
false;
2136 target->m_roles = src->m_roles;
2139 QHash<
int, ElementSync> elementHash;
2140 for (
int i = 0 ; i < target->m_modelObjects.size(); ++i) {
2141 DynamicRoleModelNode *e = target->m_modelObjects.at(i);
2142 int uid = e->getUid();
2145 sync.targetIndex = i;
2146 elementHash.insert(uid, sync);
2148 for (
int i = 0 ; i < src->m_modelObjects.size(); ++i) {
2149 DynamicRoleModelNode *e = src->m_modelObjects.at(i);
2150 int uid = e->getUid();
2152 QHash<
int, ElementSync>::iterator it = elementHash.find(uid);
2153 if (it == elementHash.end()) {
2157 elementHash.insert(uid, sync);
2159 ElementSync &sync = it.value();
2166 int rowsRemoved = 0;
2167 for (
int i = 0 ; i < target->m_modelObjects.size() ; ++i) {
2168 DynamicRoleModelNode *element = target->m_modelObjects.at(i);
2169 ElementSync &s = elementHash.find(element->getUid()).value();
2170 Q_ASSERT(s.targetIndex >= 0);
2172 s.targetIndex -= rowsRemoved;
2173 if (s.src ==
nullptr) {
2174 Q_ASSERT(s.targetIndex == i);
2176 target->beginRemoveRows(QModelIndex(), i, i);
2177 target->m_modelObjects.remove(i, 1);
2178 target->endRemoveRows();
2187 target->m_modelObjects.clear();
2188 for (
int i = 0 ; i < src->m_modelObjects.size() ; ++i) {
2189 DynamicRoleModelNode *element = src->m_modelObjects.at(i);
2190 ElementSync &s = elementHash.find(element->getUid()).value();
2191 Q_ASSERT(s.srcIndex >= 0);
2192 DynamicRoleModelNode *targetElement = s.target;
2193 if (targetElement ==
nullptr) {
2194 targetElement =
new DynamicRoleModelNode(target, element->getUid());
2196 s.changedRoles = DynamicRoleModelNode::sync(element, targetElement);
2197 target->m_modelObjects.append(targetElement);
2205 int rowsInserted = 0;
2206 for (
int i = 0 ; i < target->m_modelObjects.size() ; ++i) {
2207 DynamicRoleModelNode *element = target->m_modelObjects.at(i);
2208 ElementSync &s = elementHash.find(element->getUid()).value();
2209 Q_ASSERT(s.srcIndex >= 0);
2210 s.srcIndex += rowsInserted;
2211 if (s.srcIndex != s.targetIndex) {
2212 if (s.targetIndex == -1) {
2213 target->beginInsertRows(QModelIndex(), i, i);
2214 target->endInsertRows();
2216 target->beginMoveRows(QModelIndex(), i, i, QModelIndex(), s.srcIndex);
2217 target->endMoveRows();
2222 if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) {
2223 QModelIndex idx = target->createIndex(i, 0);
2224 emit target->dataChanged(idx, idx, s.changedRoles);
2231void QQmlListModel::emitItemsChanged(
int index,
int count,
const QVector<
int> &roles)
2237 emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);;
2240void QQmlListModel::emitItemsAboutToBeInserted(
int index,
int count)
2242 Q_ASSERT(index >= 0 && count >= 0);
2244 beginInsertRows(QModelIndex(), index, index + count - 1);
2247void QQmlListModel::emitItemsInserted()
2251 emit countChanged();
2255QQmlListModelWorkerAgent *QQmlListModel::agent()
2260 m_agent =
new QQmlListModelWorkerAgent(
this);
2264QModelIndex QQmlListModel::index(
int row,
int column,
const QModelIndex &parent)
const
2266 return row >= 0 && row < count() && column == 0 && !parent.isValid()
2267 ? createIndex(row, column)
2271int QQmlListModel::rowCount(
const QModelIndex &parent)
const
2273 return !parent.isValid() ? count() : 0;
2276QVariant QQmlListModel::data(
const QModelIndex &index,
int role)
const
2278 return data(index.row(), role);
2281bool QQmlListModel::setData(
const QModelIndex &index,
const QVariant &value,
int role)
2283 const int row = index.row();
2284 if (row >= count() || row < 0)
2287 if (m_dynamicRoles) {
2288 const QByteArray property = m_roles.at(role).toUtf8();
2289 if (m_modelObjects[row]->setValue(property, value)) {
2290 emitItemsChanged(row, 1, QVector<
int>(1, role));
2294 const ListLayout::Role &r = m_listModel->getExistingRole(role);
2295 const int roleIndex = m_listModel->setOrCreateProperty(row, r.name, value);
2296 if (roleIndex != -1) {
2297 emitItemsChanged(row, 1, QVector<
int>(1, role));
2305QVariant QQmlListModel::data(
int index,
int role)
const
2309 if (index >= count() || index < 0)
2313 v = m_modelObjects[index]->getValue(m_roles[role]);
2315 v = m_listModel->getProperty(index, role,
this, engine());
2320QHash<
int, QByteArray> QQmlListModel::roleNames()
const
2322 QHash<
int, QByteArray> roleNames;
2324 if (m_dynamicRoles) {
2325 for (
int i = 0 ; i < m_roles.size() ; ++i)
2326 roleNames.insert(i, m_roles.at(i).toUtf8());
2328 for (
int i = 0 ; i < m_listModel->roleCount() ; ++i) {
2329 const ListLayout::Role &r = m_listModel->getExistingRole(i);
2330 roleNames.insert(i, r.name.toUtf8());
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363void QQmlListModel::setDynamicRoles(
bool enableDynamicRoles)
2365 if (m_mainThread && m_agent ==
nullptr) {
2366 if (enableDynamicRoles) {
2367 if (m_layout && m_layout->roleCount())
2368 qmlWarning(
this) << tr(
"unable to enable dynamic roles as this model is not empty");
2370 m_dynamicRoles =
true;
2372 if (m_roles.size()) {
2373 qmlWarning(
this) << tr(
"unable to enable static roles as this model is not empty");
2375 m_dynamicRoles =
false;
2379 qmlWarning(
this) << tr(
"dynamic role setting must be made from the main thread, before any worker scripts are created");
2384
2385
2386
2387int QQmlListModel::count()
const
2389 return m_dynamicRoles ? m_modelObjects.size() : m_listModel->elementCount();
2393
2394
2395
2396
2397
2398
2399
2400void QQmlListModel::clear()
2402 removeElements(0, count());
2406
2407
2408
2409
2410
2411
2412void QQmlListModel::remove(QQmlV4FunctionPtr args)
2414 int argLength = args->length();
2416 if (argLength == 1 || argLength == 2) {
2417 QV4::Scope scope(args->v4engine());
2418 int index = QV4::ScopedValue(scope, (*args)[0])->toInt32();
2419 int removeCount = (argLength == 2 ? QV4::ScopedValue(scope, (*args)[1])->toInt32() : 1);
2421 if (index < 0 || index+removeCount > count() || removeCount <= 0) {
2422 qmlWarning(
this) << tr(
"remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count());
2426 removeElements(index, removeCount);
2428 qmlWarning(
this) << tr(
"remove: incorrect number of arguments");
2432void QQmlListModel::removeElements(
int index,
int removeCount)
2434 Q_ASSERT(index >= 0 && removeCount >= 0);
2440 beginRemoveRows(QModelIndex(), index, index + removeCount - 1);
2442 QVector<std::function<
void()>> toDestroy;
2443 if (m_dynamicRoles) {
2444 for (
int i=0 ; i < removeCount ; ++i) {
2445 auto modelObject = m_modelObjects[index+i];
2446 toDestroy.append([modelObject](){
2450 m_modelObjects.remove(index, removeCount);
2452 toDestroy = m_listModel->remove(index, removeCount);
2457 emit countChanged();
2459 for (
const auto &destroyer : toDestroy)
2463void QQmlListModel::updateTranslations()
2470 Q_ASSERT(m_listModel);
2473 for (
int i = 0, end = m_listModel->roleCount(); i != end; ++i) {
2474 if (m_listModel->getExistingRole(i).type == ListLayout::Role::String)
2478 if (!roles.isEmpty())
2479 emitItemsChanged(0, rowCount(QModelIndex()), roles);
2481 m_listModel->updateTranslations();
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2500void QQmlListModel::insert(QQmlV4FunctionPtr args)
2502 if (args->length() == 2) {
2503 QV4::Scope scope(args->v4engine());
2504 QV4::ScopedValue arg0(scope, (*args)[0]);
2505 int index = arg0->toInt32();
2507 if (index < 0 || index > count()) {
2508 qmlWarning(
this) << tr(
"insert: index %1 out of range").arg(index);
2512 QV4::ScopedObject argObject(scope, (*args)[1]);
2513 QV4::ScopedArrayObject objectArray(scope, (*args)[1]);
2515 QV4::ScopedObject argObject(scope);
2517 int objectArrayLength = objectArray->getLength();
2518 emitItemsAboutToBeInserted(index, objectArrayLength);
2519 for (
int i=0 ; i < objectArrayLength ; ++i) {
2520 argObject = objectArray->get(i);
2522 if (m_dynamicRoles) {
2523 m_modelObjects.insert(index+i, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2525 m_listModel->insert(index+i, argObject);
2528 emitItemsInserted();
2529 }
else if (argObject) {
2530 emitItemsAboutToBeInserted(index, 1);
2532 if (m_dynamicRoles) {
2533 m_modelObjects.insert(index, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2535 m_listModel->insert(index, argObject);
2538 emitItemsInserted();
2540 qmlWarning(
this) << tr(
"insert: value is not an object");
2543 qmlWarning(
this) << tr(
"insert: value is not an object");
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561void QQmlListModel::move(
int from,
int to,
int n)
2563 if (n == 0 || from == to)
2565 if (!canMove(from, to, n)) {
2566 qmlWarning(
this) << tr(
"move: out of range");
2571 beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to);
2573 if (m_dynamicRoles) {
2575 int realFrom = from;
2588 QPODVector<DynamicRoleModelNode *, 4> store;
2589 for (
int i=0 ; i < (realTo-realFrom) ; ++i)
2590 store.append(m_modelObjects[realFrom+realN+i]);
2591 for (
int i=0 ; i < realN ; ++i)
2592 store.append(m_modelObjects[realFrom+i]);
2593 for (
int i=0 ; i < store.count() ; ++i)
2594 m_modelObjects[realFrom+i] = store[i];
2597 m_listModel->move(from, to, n);
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616void QQmlListModel::append(QQmlV4FunctionPtr args)
2618 if (args->length() == 1) {
2619 QV4::Scope scope(args->v4engine());
2620 QV4::ScopedObject argObject(scope, (*args)[0]);
2621 QV4::ScopedArrayObject objectArray(scope, (*args)[0]);
2624 QV4::ScopedObject argObject(scope);
2626 int objectArrayLength = objectArray->getLength();
2627 if (objectArrayLength > 0) {
2628 int index = count();
2629 emitItemsAboutToBeInserted(index, objectArrayLength);
2631 for (
int i=0 ; i < objectArrayLength ; ++i) {
2632 argObject = objectArray->get(i);
2634 if (m_dynamicRoles) {
2635 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2637 m_listModel->append(argObject);
2641 emitItemsInserted();
2643 }
else if (argObject) {
2646 if (m_dynamicRoles) {
2647 index = m_modelObjects.size();
2648 emitItemsAboutToBeInserted(index, 1);
2649 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject),
this));
2651 index = m_listModel->elementCount();
2652 emitItemsAboutToBeInserted(index, 1);
2653 m_listModel->append(argObject);
2656 emitItemsInserted();
2658 qmlWarning(
this) << tr(
"append: value is not an object");
2661 qmlWarning(
this) << tr(
"append: value is not an object");
2666
2667
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
2697QJSValue QQmlListModel::get(
int index)
const
2699 QV4::Scope scope(engine());
2700 QV4::ScopedValue result(scope, QV4::Value::undefinedValue());
2702 if (index >= 0 && index < count()) {
2704 if (m_dynamicRoles) {
2705 DynamicRoleModelNode *object = m_modelObjects[index];
2706 result = QV4::QObjectWrapper::wrap(scope.engine, object);
2708 QObject *object = m_listModel->getOrCreateModelObject(
const_cast<QQmlListModel *>(
this), index);
2709 QQmlData *ddata = QQmlData::get(object);
2710 if (ddata->jsWrapper.isNullOrUndefined()) {
2711 result = scope.engine->memoryManager->allocate<QV4::ModelObject>(object,
const_cast<QQmlListModel *>(
this));
2713 ddata->jsWrapper.set(scope.engine, result);
2715 result = ddata->jsWrapper.value();
2720 return QJSValuePrivate::fromReturnedValue(result->asReturnedValue());
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739void QQmlListModel::set(
int index,
const QJSValue &value)
2741 QV4::Scope scope(engine());
2742 QV4::ScopedObject object(scope, QJSValuePrivate::asReturnedValue(&value));
2745 qmlWarning(
this) << tr(
"set: value is not an object");
2748 if (index > count() || index < 0) {
2749 qmlWarning(
this) << tr(
"set: index %1 out of range").arg(index);
2754 if (index == count()) {
2755 emitItemsAboutToBeInserted(index, 1);
2757 if (m_dynamicRoles) {
2758 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(object),
this));
2760 m_listModel->insert(index, object);
2763 emitItemsInserted();
2768 if (m_dynamicRoles) {
2769 m_modelObjects[index]->updateValues(scope.engine->variantMapFromJS(object), roles);
2771 m_listModel->set(index, object, &roles);
2775 emitItemsChanged(index, 1, roles);
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792void QQmlListModel::setProperty(
int index,
const QString& property,
const QVariant& value)
2794 if (count() == 0 || index >= count() || index < 0) {
2795 qmlWarning(
this) << tr(
"set: index %1 out of range").arg(index);
2799 if (m_dynamicRoles) {
2800 int roleIndex = m_roles.indexOf(property);
2801 if (roleIndex == -1) {
2802 roleIndex = m_roles.size();
2803 m_roles.append(property);
2805 if (m_modelObjects[index]->setValue(property.toUtf8(), value))
2806 emitItemsChanged(index, 1, QVector<
int>(1, roleIndex));
2808 int roleIndex = m_listModel->setOrCreateProperty(index, property, value);
2809 if (roleIndex != -1)
2810 emitItemsChanged(index, 1, QVector<
int>(1, roleIndex));
2815
2816
2817
2818
2819
2820void QQmlListModel::sync()
2825 qmlWarning(
this) <<
"List sync() can only be called from a WorkerScript";
2829 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
2830 const QV4::CompiledData::Binding *binding)
2832 if (binding->type() >= QV4::CompiledData::Binding::Type_Object) {
2833 const quint32 targetObjectIndex = binding->value.objectIndex;
2834 const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
2835 QString objName = compilationUnit->stringAt(target->inheritedTypeNameIndex);
2836 if (objName != listElementTypeName) {
2837 const QMetaObject *mo = resolveType(objName);
2838 if (mo != &QQmlListElement::staticMetaObject) {
2839 error(target, QQmlListModel::tr(
"ListElement: cannot contain nested elements"));
2842 listElementTypeName = objName;
2845 if (!compilationUnit->stringAt(target->idNameIndex).isEmpty()) {
2846 error(target->locationOfIdProperty, QQmlListModel::tr(
"ListElement: cannot use reserved \"id\" property"));
2850 const QV4::CompiledData::Binding *binding = target->bindingTable();
2851 for (quint32 i = 0; i < target->nBindings; ++i, ++binding) {
2852 QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
2853 if (propName.isEmpty()) {
2854 error(binding, QQmlListModel::tr(
"ListElement: cannot contain nested elements"));
2857 if (!verifyProperty(compilationUnit, binding))
2860 }
else if (binding->type() == QV4::CompiledData::Binding::Type_Script) {
2861 QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
2862 if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) {
2864 evaluateEnum(scriptStr, &ok);
2866 error(binding, QQmlListModel::tr(
"ListElement: cannot use script for property value"));
2876 const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
2877 const QV4::CompiledData::Binding *binding,
ListModel *model,
int outterElementIndex)
2879 const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex);
2881 bool roleSet =
false;
2882 const QV4::CompiledData::Binding::Type bindingType = binding->type();
2883 if (bindingType >= QV4::CompiledData::Binding::Type_Object) {
2884 const quint32 targetObjectIndex = binding->value.objectIndex;
2885 const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
2888 if (outterElementIndex == -1) {
2891 const ListLayout::
Role &role = model->getOrCreateListRole(elementName);
2894 if (subModel ==
nullptr) {
2895 subModel =
new ListModel(role.subLayout,
nullptr);
2896 QVariant vModel = QVariant::fromValue(subModel);
2897 model->setOrCreateProperty(outterElementIndex, elementName, vModel);
2904 const QV4::CompiledData::Binding *subBinding = target->bindingTable();
2905 for (quint32 i = 0; i < target->nBindings; ++i, ++subBinding) {
2906 roleSet |= applyProperty(compilationUnit, subBinding, subModel, elementIndex);
2912 const bool isTranslationBinding = binding->isTranslationBinding();
2913 if (isTranslationBinding) {
2914 value = QVariant::fromValue<
const QV4::CompiledData::Binding*>(binding);
2915 }
else if (binding->evaluatesToString()) {
2916 value = compilationUnit->bindingValueAsString(binding);
2917 }
else if (bindingType == QV4::CompiledData::Binding::Type_Number) {
2918 value = compilationUnit->bindingValueAsNumber(binding);
2919 }
else if (bindingType == QV4::CompiledData::Binding::Type_Boolean) {
2920 value = binding->valueAsBoolean();
2921 }
else if (bindingType == QV4::CompiledData::Binding::Type_Null) {
2922 value = QVariant::fromValue(
nullptr);
2923 }
else if (bindingType == QV4::CompiledData::Binding::Type_Script) {
2924 QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
2925 if (definesEmptyList(scriptStr)) {
2926 const ListLayout::
Role &role = model->getOrCreateListRole(elementName);
2927 ListModel *emptyModel =
new ListModel(role.subLayout,
nullptr);
2928 value = QVariant::fromValue(emptyModel);
2929 }
else if (binding->isFunctionExpression()) {
2930 QQmlBinding::Identifier id = binding->value.compiledScriptIndex;
2931 Q_ASSERT(id != QQmlBinding::Invalid);
2933 auto v4 = compilationUnit->engine;
2934 QV4::Scope scope(v4);
2936 QV4::ScopedContext context(scope, QV4::QmlContext::create(v4->rootContext(), QQmlContextData::get(qmlContext(model->m_modelCache)),
nullptr));
2937 QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(context, compilationUnit->runtimeFunctions[id]));
2940 QV4::ScopedValue result(scope, function->call(v4->globalObject,
nullptr, 0));
2941 if (v4->hasException)
2942 v4->catchException();
2944 QJSValuePrivate::setValue(&v, result);
2948 value = evaluateEnum(scriptStr, &ok);
2956 model->setOrCreateProperty(outterElementIndex, elementName, value);
2957 auto listModel = model->m_modelCache;
2958 if (isTranslationBinding && listModel) {
2959 if (!listModel->translationChangeHandler) {
2960 auto ep = QQmlEnginePrivate::get(compilationUnit->engine);
2961 model->m_modelCache->translationChangeHandler = std::make_unique<QPropertyNotifier>(
2962 ep->translationLanguage.addNotifier([listModel](){ listModel->updateTranslations(); }));
2971 const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit,
2972 const QList<
const QV4::CompiledData::Binding *> &bindings)
2974 listElementTypeName = QString();
2976 for (
const QV4::CompiledData::Binding *binding : bindings) {
2977 QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
2978 if (!propName.isEmpty()) {
2979 error(binding, QQmlListModel::tr(
"ListModel: undefined property '%1'").arg(propName));
2982 if (!verifyProperty(compilationUnit, binding))
2988 QObject *obj,
const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
2989 const QList<
const QV4::CompiledData::Binding *> &bindings)
2991 QQmlListModel *rv =
static_cast<QQmlListModel *>(obj);
2993 rv->m_engine = qmlEngine(rv)->handle();
2994 rv->m_compilationUnit = compilationUnit;
2996 bool setRoles =
false;
2998 for (
const QV4::CompiledData::Binding *binding : bindings) {
2999 if (binding->type() != QV4::CompiledData::Binding::Type_Object)
3001 setRoles |= applyProperty(compilationUnit, binding, rv->m_listModel, -1);
3004 if (setRoles ==
false)
3005 qmlWarning(obj) <<
"All ListElement declarations are empty, no roles can be created unless dynamicRoles is set.";
3010 if (s.startsWith(QLatin1Char(
'[')) && s.endsWith(QLatin1Char(
']'))) {
3011 for (
int i=1; i<s.size()-1; i++) {
3012 if (!s[i].isSpace())
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
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
3070#include "moc_qqmllistmodel_p_p.cpp"
3072#include "moc_qqmllistmodel_p.cpp"
void setNodeUpdatesEnabled(bool enable)
void updateValues(const QVariantMap &object, QVector< int > &roles)
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)
void set(int elementIndex, QV4::Object *object, QVector< int > *roles)
ListModel * getListProperty(int elementIndex, const ListLayout::Role &role)
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)