Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qqmllistmodel.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmllistmodel_p_p.h"
6#include <private/qqmlopenmetaobject_p.h>
7#include <private/qqmljsast_p.h>
8#include <private/qqmljsengine_p.h>
9#include <private/qjsvalue_p.h>
10
11#include <private/qqmlcustomparser_p.h>
12#include <private/qqmlengine_p.h>
13#include <private/qqmlnotifier_p.h>
14
15#include <private/qv4object_p.h>
16#include <private/qv4dateobject_p.h>
17#include <private/qv4urlobject_p.h>
18#include <private/qv4objectiterator_p.h>
19#include <private/qv4alloca_p.h>
20#include <private/qv4lookup_p.h>
21#include <private/qv4qmlcontext_p.h>
22
23#include <qqmlcontext.h>
24#include <qqmlinfo.h>
25
26#include <QtCore/qdebug.h>
27#include <QtCore/qstack.h>
28#include <QXmlStreamReader>
29#include <QtCore/qdatetime.h>
30#include <QScopedValueRollback>
31
33
35
36// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models.
37enum { MIN_LISTMODEL_UID = 1024 };
38
40
41template <typename T>
42static bool isMemoryUsed(const char *mem)
43{
44 for (size_t i=0 ; i < sizeof(T) ; ++i) {
45 if (mem[i] != 0)
46 return true;
47 }
48
49 return false;
50}
51
53{
54 static const QString roleTypeNames[] = {
55 QStringLiteral("String"), QStringLiteral("Number"), QStringLiteral("Bool"),
56 QStringLiteral("List"), QStringLiteral("QObject"), QStringLiteral("VariantMap"),
57 QStringLiteral("DateTime"), QStringLiteral("Url"), QStringLiteral("Function")
58 };
59
61 return roleTypeNames[t];
62
63 return QString();
64}
65
67{
68 QStringHash<Role *>::Node *node = roleHash.findNode(key);
69 if (node) {
70 const Role &r = *node->value;
71 if (type != r.type)
72 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));
73 return r;
74 }
75
76 return createRole(key, type);
77}
78
80{
81 QStringHash<Role *>::Node *node = roleHash.findNode(key);
82 if (node) {
83 const Role &r = *node->value;
84 if (type != r.type)
85 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));
86 return r;
87 }
88
89 QString qkey = key->toQString();
90
91 return createRole(qkey, type);
92}
93
94const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type)
95{
96 const int dataSizes[] = {
97 sizeof(StringOrTranslation),
98 sizeof(double),
99 sizeof(bool),
100 sizeof(ListModel *),
101 sizeof(QV4::PersistentValue),
102 sizeof(QVariantMap),
103 sizeof(QDateTime),
104 sizeof(QUrl),
105 sizeof(QJSValue)
106 };
107 const int dataAlignments[] = {
108 alignof(StringOrTranslation),
109 alignof(double),
110 alignof(bool),
111 alignof(ListModel *),
112 alignof(QV4::PersistentValue),
113 alignof(QVariantMap),
114 alignof(QDateTime),
115 alignof(QUrl),
116 alignof(QJSValue)
117 };
118
119 Role *r = new Role;
120 r->name = key;
121 r->type = type;
122
123 if (type == Role::List) {
124 r->subLayout = new ListLayout;
125 } else {
126 r->subLayout = nullptr;
127 }
128
129 int dataSize = dataSizes[type];
130 int dataAlignment = dataAlignments[type];
131
132 int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1);
133 if (dataOffset + dataSize > ListElement::BLOCK_SIZE) {
134 r->blockIndex = ++currentBlock;
135 r->blockOffset = 0;
136 currentBlockOffset = dataSize;
137 } else {
138 r->blockIndex = currentBlock;
139 r->blockOffset = dataOffset;
140 currentBlockOffset = dataOffset + dataSize;
141 }
142
143 int roleIndex = roles.size();
144 r->index = roleIndex;
145
146 roles.append(r);
147 roleHash.insert(key, r);
148
149 return *r;
150}
151
152ListLayout::ListLayout(const ListLayout *other) : currentBlock(0), currentBlockOffset(0)
153{
154 const int otherRolesCount = other->roles.size();
155 roles.reserve(otherRolesCount);
156 for (int i=0 ; i < otherRolesCount; ++i) {
157 Role *role = new Role(other->roles[i]);
158 roles.append(role);
159 roleHash.insert(role->name, role);
160 }
161 currentBlockOffset = other->currentBlockOffset;
162 currentBlock = other->currentBlock;
163}
164
166{
167 qDeleteAll(roles);
168}
169
171{
172 int roleOffset = target->roles.size();
173 int newRoleCount = src->roles.size() - roleOffset;
174
175 for (int i=0 ; i < newRoleCount ; ++i) {
176 Role *role = new Role(src->roles[roleOffset + i]);
177 target->roles.append(role);
178 target->roleHash.insert(role->name, role);
179 }
180
181 target->currentBlockOffset = src->currentBlockOffset;
182 target->currentBlock = src->currentBlock;
183}
184
186{
187 name = other->name;
188 type = other->type;
189 blockIndex = other->blockIndex;
190 blockOffset = other->blockOffset;
191 index = other->index;
192 if (other->subLayout)
193 subLayout = new ListLayout(other->subLayout);
194 else
195 subLayout = nullptr;
196}
197
199{
200 delete subLayout;
201}
202
204{
206
207 switch (data.userType()) {
208 case QMetaType::Double: type = Role::Number; break;
209 case QMetaType::Int: type = Role::Number; break;
210 case QMetaType::Bool: type = Role::Bool; break;
211 case QMetaType::QString: type = Role::String; break;
212 case QMetaType::QVariantMap: type = Role::VariantMap; break;
213 case QMetaType::QDateTime: type = Role::DateTime; break;
214 case QMetaType::QUrl: type = Role::Url; break;
215 default: {
216 if (data.userType() == qMetaTypeId<QJSValue>() &&
217 data.value<QJSValue>().isCallable()) {
219 break;
220 } else if (data.userType() == qMetaTypeId<const QV4::CompiledData::Binding*>()
223 break;
224 } else if (data.userType() >= QMetaType::User) {
226 break;
227 } else {
229 break;
230 }
231 }
232 }
233
234 if (type == Role::Invalid) {
235 qmlWarning(nullptr) << "Can't create role for unsupported data type";
236 return nullptr;
237 }
238
239 return &getRoleOrCreate(key, type);
240}
241
243{
244 Role *r = nullptr;
245 QStringHash<Role *>::Node *node = roleHash.findNode(key);
246 if (node)
247 r = node->value;
248 return r;
249}
250
252{
253 Role *r = nullptr;
254 QStringHash<Role *>::Node *node = roleHash.findNode(key);
255 if (node)
256 r = node->value;
257 return r;
258}
259
261{
262 clear();
263}
264
266{
267 clear();
268 if (s.isEmpty())
269 return;
270 QString mutableString(s);
271 QString::DataPointer dataPointer = mutableString.data_ptr();
272 arrayData = dataPointer->d_ptr();
273 stringData = dataPointer->data();
274 stringSize = mutableString.size();
275 if (arrayData)
276 arrayData->ref();
277}
278
280{
281 clear();
282 this->binding = binding;
283}
284
286{
287 if (stringSize) {
288 if (arrayData)
289 arrayData->ref();
290 return QString(QStringPrivate(arrayData, stringData, stringSize));
291 }
292 if (!owner)
293 return QString();
294 return owner->m_compilationUnit->bindingValueAsString(binding);
295}
296
298{
299 if (!arrayData)
300 return QString();
301 arrayData->ref();
302 return QString(QStringPrivate(arrayData, stringData, stringSize));
303}
304
305void StringOrTranslation::clear()
306{
307 if (arrayData && !arrayData->deref())
309 arrayData = nullptr;
310 stringData = nullptr;
311 stringSize = 0;
312 binding = nullptr;
313}
314
316{
317 ListElement *e = elements[elementIndex];
318 if (e->m_objectCache == nullptr) {
319 void *memory = operator new(sizeof(QObject) + sizeof(QQmlData));
320 void *ddataMemory = ((char *)memory) + sizeof(QObject);
321 e->m_objectCache = new (memory) QObject;
322
323 const QAbstractDeclarativeData *old = std::exchange(
324 QObjectPrivate::get(e->m_objectCache)->declarativeData,
325 new (ddataMemory) QQmlData(QQmlData::DoesNotOwnMemory));
326 Q_ASSERT(!old); // QObject should really not manipulate QQmlData
327
328 (void)new ModelNodeMetaObject(e->m_objectCache, model, elementIndex);
329 }
330 return e->m_objectCache;
331}
332
334{
335 // Sanity check
336
337 bool hasChanges = false;
338
339 // Build hash of elements <-> uid for each of the lists
340 QHash<int, ElementSync> elementHash;
341 for (int i = 0; i < target->elements.count(); ++i) {
342 ListElement *e = target->elements.at(i);
343 int uid = e->getUid();
344 ElementSync sync;
345 sync.target = e;
346 sync.targetIndex = i;
347 elementHash.insert(uid, sync);
348 }
349 for (int i = 0; i < src->elements.count(); ++i) {
350 ListElement *e = src->elements.at(i);
351 int uid = e->getUid();
352
353 QHash<int, ElementSync>::iterator it = elementHash.find(uid);
354 if (it == elementHash.end()) {
355 ElementSync sync;
356 sync.src = e;
357 sync.srcIndex = i;
358 elementHash.insert(uid, sync);
359 } else {
360 ElementSync &sync = it.value();
361 sync.src = e;
362 sync.srcIndex = i;
363 }
364 }
365
366 QQmlListModel *targetModel = target->m_modelCache;
367
368 // Get list of elements that are in the target but no longer in the source. These get deleted first.
369 int rowsRemoved = 0;
370 for (int i = 0 ; i < target->elements.count() ; ++i) {
371 ListElement *element = target->elements.at(i);
372 ElementSync &s = elementHash.find(element->getUid()).value();
373 Q_ASSERT(s.targetIndex >= 0);
374 // need to update the targetIndex, to keep it correct after removals
375 s.targetIndex -= rowsRemoved;
376 if (s.src == nullptr) {
377 Q_ASSERT(s.targetIndex == i);
378 hasChanges = true;
379 if (targetModel)
380 targetModel->beginRemoveRows(QModelIndex(), i, i);
381 s.target->destroy(target->m_layout);
382 target->elements.removeOne(s.target);
383 delete s.target;
384 if (targetModel)
385 targetModel->endRemoveRows();
386 ++rowsRemoved;
387 --i;
388 continue;
389 }
390 }
391
392 // Sync the layouts
393 ListLayout::sync(src->m_layout, target->m_layout);
394
395 // Clear the target list, and append in correct order from the source
396 target->elements.clear();
397 for (int i = 0; i < src->elements.count(); ++i) {
398 ListElement *srcElement = src->elements.at(i);
399 ElementSync &s = elementHash.find(srcElement->getUid()).value();
400 Q_ASSERT(s.srcIndex >= 0);
401 ListElement *targetElement = s.target;
402 if (targetElement == nullptr) {
403 targetElement = new ListElement(srcElement->getUid());
404 }
405 s.changedRoles = ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout);
406 target->elements.append(targetElement);
407 }
408
409 target->updateCacheIndices();
410
411 // Update values stored in target meta objects
412 for (int i=0 ; i < target->elements.count() ; ++i) {
413 ListElement *e = target->elements[i];
414 if (ModelNodeMetaObject *mo = e->objectCache())
415 mo->updateValues();
416 }
417
418 // now emit the change notifications required. This can be safely done, as we're only emitting changes, moves and inserts,
419 // so the model indices can't be out of bounds
420 //
421 // to ensure things are kept in the correct order, emit inserts and moves first. This shouls ensure all persistent
422 // model indices are updated correctly
423 int rowsInserted = 0;
424 const int targetElementCount = target->elements.count();
425 for (int i = 0 ; i < targetElementCount ; ++i) {
426 ListElement *element = target->elements.at(i);
427 ElementSync &s = elementHash.find(element->getUid()).value();
428 Q_ASSERT(s.srcIndex >= 0);
429 s.srcIndex += rowsInserted;
430 if (s.srcIndex != s.targetIndex) {
431 if (targetModel) {
432 if (s.targetIndex == -1) {
433 targetModel->beginInsertRows(QModelIndex(), i, i);
434 targetModel->endInsertRows();
435 ++rowsInserted;
436 } else {
437 bool validMove = targetModel->beginMoveRows(QModelIndex(), s.targetIndex, s.targetIndex, QModelIndex(), i);
438 Q_ASSERT(validMove);
439 targetModel->endMoveRows();
440 // fixup target indices of elements that still need to move
441 for (int j=i+1; j < targetElementCount; ++j) {
442 ListElement *eToFix = target->elements.at(j);
443 ElementSync &sToFix = elementHash.find(eToFix->getUid()).value();
444 if (i < s.targetIndex) {
445 // element was moved down
446 if (sToFix.targetIndex > s.targetIndex || sToFix.targetIndex < i)
447 continue; // unaffected by reordering
448 else
449 sToFix.targetIndex += 1;
450 } else {
451 // element was moved up
452 if (sToFix.targetIndex < s.targetIndex || sToFix.targetIndex > i)
453 continue; // unaffected by reordering
454 else
455 sToFix.targetIndex -= 1;
456 }
457 }
458 }
459 }
460 hasChanges = true;
461 }
462 if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) {
463 QModelIndex idx = targetModel->createIndex(i, 0);
464 if (targetModel)
465 targetModel->dataChanged(idx, idx, s.changedRoles);
466 hasChanges = true;
467 }
468 }
469 return hasChanges;
470}
471
472ListModel::ListModel(ListLayout *layout, QQmlListModel *modelCache) : m_layout(layout), m_modelCache(modelCache)
473{
474}
475
477{
478 for (const auto &destroyer : remove(0, elements.count()))
479 destroyer();
480
481 m_layout = nullptr;
482 if (m_modelCache && m_modelCache->m_primary == false)
483 delete m_modelCache;
484 m_modelCache = nullptr;
485}
486
488{
489 int elementIndex = elements.count();
490 newElement(elementIndex);
491 return elementIndex;
492}
493
495{
496 newElement(index);
497 updateCacheIndices(index);
498}
499
500void ListModel::move(int from, int to, int n)
501{
502 if (from > to) {
503 // Only move forwards - flip if backwards moving
504 int tfrom = from;
505 int tto = to;
506 from = tto;
507 to = tto+n;
508 n = tfrom-tto;
509 }
510
512 for (int i=0 ; i < (to-from) ; ++i)
513 store.append(elements[from+n+i]);
514 for (int i=0 ; i < n ; ++i)
515 store.append(elements[from+i]);
516 for (int i=0 ; i < store.count() ; ++i)
517 elements[from+i] = store[i];
518
519 updateCacheIndices(from, to + n);
520}
521
522void ListModel::newElement(int index)
523{
525 elements.insert(index, e);
526}
527
528void ListModel::updateCacheIndices(int start, int end)
529{
530 int count = elements.count();
531
532 if (end < 0 || end > count)
533 end = count;
534
535 for (int i = start; i < end; ++i) {
536 ListElement *e = elements.at(i);
537 if (ModelNodeMetaObject *mo = e->objectCache())
538 mo->m_elementIndex = i;
539 }
540}
541
542QVariant ListModel::getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV4::ExecutionEngine *eng)
543{
544 if (roleIndex >= m_layout->roleCount())
545 return QVariant();
546 ListElement *e = elements[elementIndex];
547 const ListLayout::Role &r = m_layout->getExistingRole(roleIndex);
548 return e->getProperty(r, owner, eng);
549}
550
552{
553 ListElement *e = elements[elementIndex];
554 return e->getListProperty(role);
555}
556
558{
559 for (int index = 0; index != elements.count(); ++index) {
560 ListElement *e = elements[index];
561 if (ModelNodeMetaObject *cache = e->objectCache()) {
562 // TODO: more fine grained tracking?
563 cache->updateValues();
564 }
565 }
566}
567
568void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles)
569{
570 ListElement *e = elements[elementIndex];
571
572 QV4::ExecutionEngine *v4 = object->engine();
573 QV4::Scope scope(v4);
574 QV4::ScopedObject o(scope);
575
577 QV4::ScopedString propertyName(scope);
578 QV4::ScopedValue propertyValue(scope);
579 while (1) {
580 propertyName = it.nextPropertyNameAsString(propertyValue);
581 if (!propertyName)
582 break;
583
584 // Check if this key exists yet
585 int roleIndex = -1;
586
587 // Add the value now
588 if (const QV4::String *s = propertyValue->as<QV4::String>()) {
589 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String);
590 roleIndex = e->setStringProperty(r, s->toQString());
591 } else if (propertyValue->isNumber()) {
592 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number);
593 roleIndex = e->setDoubleProperty(r, propertyValue->asDouble());
594 } else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) {
595 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
596 ListModel *subModel = new ListModel(r.subLayout, nullptr);
597
598 int arrayLength = a->getLength();
599 for (int j=0 ; j < arrayLength ; ++j) {
600 o = a->get(j);
601 subModel->append(o);
602 }
603
604 roleIndex = e->setListProperty(r, subModel);
605 } else if (propertyValue->isBoolean()) {
606 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool);
607 roleIndex = e->setBoolProperty(r, propertyValue->booleanValue());
608 } else if (QV4::DateObject *dd = propertyValue->as<QV4::DateObject>()) {
609 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime);
610 QDateTime dt = dd->toQDateTime();
611 roleIndex = e->setDateTimeProperty(r, dt);
612 } else if (QV4::UrlObject *url = propertyValue->as<QV4::UrlObject>()) {
613 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url);
614 QUrl qurl = QUrl(url->href());
615 roleIndex = e->setUrlProperty(r, qurl);
616 } else if (QV4::FunctionObject *f = propertyValue->as<QV4::FunctionObject>()) {
617 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Function);
619 QJSValue jsv;
621 roleIndex = e->setFunctionProperty(r, jsv);
622 } else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
624 const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
626 roleIndex = e->setQObjectProperty(role, wrapper);
627 } else if (QVariant maybeUrl = QV4::ExecutionEngine::toVariant(
628 o->asReturnedValue(), QMetaType::fromType<QUrl>(), true);
629 maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
630 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url);
631 QUrl qurl = maybeUrl.toUrl();
632 roleIndex = e->setUrlProperty(r, qurl);
633 } else {
634 const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap);
636 QV4::ScopedObject obj(scope, o);
637 roleIndex = e->setVariantMapProperty(role, obj);
638 }
639 }
640 } else if (propertyValue->isNullOrUndefined()) {
641 const ListLayout::Role *r = m_layout->getExistingRole(propertyName);
642 if (r)
643 e->clearProperty(*r);
644 }
645
646 if (roleIndex != -1)
647 roles->append(roleIndex);
648 }
649
650 if (ModelNodeMetaObject *mo = e->objectCache())
651 mo->updateValues(*roles);
652}
653
654void ListModel::set(int elementIndex, QV4::Object *object, ListModel::SetElement reason)
655{
656 if (!object)
657 return;
658
659 ListElement *e = elements[elementIndex];
660
661 QV4::ExecutionEngine *v4 = object->engine();
662 QV4::Scope scope(v4);
663
665 QV4::ScopedString propertyName(scope);
666 QV4::ScopedValue propertyValue(scope);
667 QV4::ScopedObject o(scope);
668 while (1) {
669 propertyName = it.nextPropertyNameAsString(propertyValue);
670 if (!propertyName)
671 break;
672
673 // Add the value now
674 if (QV4::String *s = propertyValue->stringValue()) {
675 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String);
676 if (r.type == ListLayout::Role::String)
677 e->setStringPropertyFast(r, s->toQString());
678 } else if (propertyValue->isNumber()) {
679 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number);
680 if (r.type == ListLayout::Role::Number) {
681 e->setDoublePropertyFast(r, propertyValue->asDouble());
682 }
683 } else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) {
684 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List);
685 if (r.type == ListLayout::Role::List) {
686 ListModel *subModel = new ListModel(r.subLayout, nullptr);
687
688 int arrayLength = a->getLength();
689 for (int j=0 ; j < arrayLength ; ++j) {
690 o = a->get(j);
691 subModel->append(o);
692 }
693
694 e->setListPropertyFast(r, subModel);
695 }
696 } else if (propertyValue->isBoolean()) {
697 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool);
698 if (r.type == ListLayout::Role::Bool) {
699 e->setBoolPropertyFast(r, propertyValue->booleanValue());
700 }
701 } else if (QV4::DateObject *date = propertyValue->as<QV4::DateObject>()) {
702 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime);
703 if (r.type == ListLayout::Role::DateTime) {
704 QDateTime dt = date->toQDateTime();
705 e->setDateTimePropertyFast(r, dt);
706 }
707 } else if (QV4::UrlObject *url = propertyValue->as<QV4::UrlObject>()){
708 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url);
709 if (r.type == ListLayout::Role::Url) {
710 QUrl qurl = QUrl(url->href()); // does what the private UrlObject->toQUrl would do
711 e->setUrlPropertyFast(r, qurl);
712 }
713 } else if (QV4::Object *o = propertyValue->as<QV4::Object>()) {
715 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject);
716 if (r.type == ListLayout::Role::QObject)
717 e->setQObjectPropertyFast(r, wrapper);
718 } else {
720 o->asReturnedValue(), QMetaType::fromType<QUrl>(), true);
721 if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
722 const QUrl qurl = maybeUrl.toUrl();
723 const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Url);
724 if (r.type == ListLayout::Role::Url)
725 e->setUrlPropertyFast(r, qurl);
726 } else {
727 const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap);
729 e->setVariantMapFast(role, o);
730 }
731 }
732 } else if (propertyValue->isNullOrUndefined()) {
733 if (reason == SetElement::WasJustInserted) {
734 QQmlError err;
735 auto memberName = propertyName->toString(v4)->toQString();
736 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")));
737 qmlWarning(nullptr, err);
738 } else {
739 const ListLayout::Role *r = m_layout->getExistingRole(propertyName);
740 if (r)
741 e->clearProperty(*r);
742 }
743 }
744 }
745}
746
747QVector<std::function<void()>> ListModel::remove(int index, int count)
748{
749 QVector<std::function<void()>> toDestroy;
750 auto layout = m_layout;
751 for (int i=0 ; i < count ; ++i) {
752 auto element = elements[index+i];
753 toDestroy.append([element, layout](){
754 element->destroy(layout);
755 delete element;
756 });
757 }
758 elements.remove(index, count);
759 updateCacheIndices(index);
760 return toDestroy;
761}
762
763void ListModel::insert(int elementIndex, QV4::Object *object)
764{
765 insertElement(elementIndex);
766 set(elementIndex, object, SetElement::WasJustInserted);
767}
768
770{
771 int elementIndex = appendElement();
772 set(elementIndex, object, SetElement::WasJustInserted);
773 return elementIndex;
774}
775
776int ListModel::setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data)
777{
778 int roleIndex = -1;
779
780 if (elementIndex >= 0 && elementIndex < elements.count()) {
781 ListElement *e = elements[elementIndex];
782
783 const ListLayout::Role *r = m_layout->getRoleOrCreate(key, data);
784 if (r) {
785 roleIndex = e->setVariantProperty(*r, data);
786
787 ModelNodeMetaObject *cache = e->objectCache();
788
789 if (roleIndex != -1 && cache)
790 cache->updateValues(QVector<int>(1, roleIndex));
791 }
792 }
793
794 return roleIndex;
795}
796
798{
799 int roleIndex = -1;
800
801 if (elementIndex >= 0 && elementIndex < elements.count()) {
802 ListElement *e = elements[elementIndex];
803 const ListLayout::Role *r = m_layout->getExistingRole(key);
804 if (r)
805 roleIndex = e->setJsProperty(*r, data, eng);
806 }
807
808 return roleIndex;
809}
810
811inline char *ListElement::getPropertyMemory(const ListLayout::Role &role)
812{
813 ListElement *e = this;
814 int blockIndex = 0;
815 while (blockIndex < role.blockIndex) {
816 if (e->next == nullptr) {
817 e->next = new ListElement;
818 e->next->uid = uid;
819 }
820 e = e->next;
821 ++blockIndex;
822 }
823
824 char *mem = &e->data[role.blockOffset];
825 return mem;
826}
827
828ModelNodeMetaObject *ListElement::objectCache()
829{
830 if (!m_objectCache)
831 return nullptr;
832 return ModelNodeMetaObject::get(m_objectCache);
833}
834
835StringOrTranslation *ListElement::getStringProperty(const ListLayout::Role &role)
836{
837 char *mem = getPropertyMemory(role);
838 StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem);
839 return s;
840}
841
842QV4::QObjectWrapper *ListElement::getQObjectProperty(const ListLayout::Role &role)
843{
844 char *mem = getPropertyMemory(role);
845 QV4::PersistentValue *g = reinterpret_cast<QV4::PersistentValue *>(mem);
846 return g->as<QV4::QObjectWrapper>();
847}
848
849QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role)
850{
851 QVariantMap *map = nullptr;
852
853 char *mem = getPropertyMemory(role);
854 if (isMemoryUsed<QVariantMap>(mem))
855 map = reinterpret_cast<QVariantMap *>(mem);
856
857 return map;
858}
859
860QDateTime *ListElement::getDateTimeProperty(const ListLayout::Role &role)
861{
862 QDateTime *dt = nullptr;
863
864 char *mem = getPropertyMemory(role);
865 if (isMemoryUsed<QDateTime>(mem))
866 dt = reinterpret_cast<QDateTime *>(mem);
867
868 return dt;
869}
870
871QUrl *ListElement::getUrlProperty(const ListLayout::Role &role)
872{
873 QUrl *url = nullptr;
874
875 char *mem = getPropertyMemory(role);
876 if (isMemoryUsed<QUrl>(mem))
877 url = reinterpret_cast<QUrl *>(mem);
878
879 return url;
880}
881
882QJSValue *ListElement::getFunctionProperty(const ListLayout::Role &role)
883{
884 QJSValue *f = nullptr;
885
886 char *mem = getPropertyMemory(role);
887 if (isMemoryUsed<QJSValue>(mem))
888 f = reinterpret_cast<QJSValue *>(mem);
889
890 return f;
891}
892
894ListElement::getGuardProperty(const ListLayout::Role &role)
895{
896 char *mem = getPropertyMemory(role);
897
898 bool existingGuard = false;
899 for (size_t i = 0; i < sizeof(QV4::PersistentValue);
900 ++i) {
901 if (mem[i] != 0) {
902 existingGuard = true;
903 break;
904 }
905 }
906
907 QV4::PersistentValue *g = nullptr;
908
909 if (existingGuard)
910 g = reinterpret_cast<QV4::PersistentValue *>(mem);
911
912 return g;
913}
914
915ListModel *ListElement::getListProperty(const ListLayout::Role &role)
916{
917 char *mem = getPropertyMemory(role);
918 ListModel **value = reinterpret_cast<ListModel **>(mem);
919 return *value;
920}
921
922QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng)
923{
924 char *mem = getPropertyMemory(role);
925
927
928 switch (role.type) {
930 {
931 double *value = reinterpret_cast<double *>(mem);
932 data = *value;
933 }
934 break;
936 {
937 StringOrTranslation *value = reinterpret_cast<StringOrTranslation *>(mem);
938 if (value->isSet())
939 data = value->toString(owner);
940 else
941 data = QString();
942 }
943 break;
945 {
946 bool *value = reinterpret_cast<bool *>(mem);
947 data = *value;
948 }
949 break;
951 {
952 ListModel **value = reinterpret_cast<ListModel **>(mem);
954
955 if (model) {
956 if (model->m_modelCache == nullptr) {
957 model->m_modelCache = new QQmlListModel(owner, model, eng);
959 }
960
961 QObject *object = model->m_modelCache;
962 data = QVariant::fromValue(object);
963 }
964 }
965 break;
967 {
968 QV4::PersistentValue *guard = reinterpret_cast<QV4::PersistentValue *>(mem);
970 }
971 break;
973 {
974 if (isMemoryUsed<QVariantMap>(mem)) {
975 QVariantMap *map = reinterpret_cast<QVariantMap *>(mem);
976 data = *map;
977 }
978 }
979 break;
981 {
982 if (isMemoryUsed<QDateTime>(mem)) {
983 QDateTime *dt = reinterpret_cast<QDateTime *>(mem);
984 data = *dt;
985 }
986 }
987 break;
989 {
990 if (isMemoryUsed<QUrl>(mem)) {
991 QUrl *url = reinterpret_cast<QUrl *>(mem);
992 data = *url;
993 }
994 }
995 break;
997 {
998 if (isMemoryUsed<QJSValue>(mem)) {
999 QJSValue *func = reinterpret_cast<QJSValue *>(mem);
1001 }
1002 }
1003 break;
1004 default:
1005 break;
1006 }
1007
1008 return data;
1009}
1010
1011int ListElement::setStringProperty(const ListLayout::Role &role, const QString &s)
1012{
1013 int roleIndex = -1;
1014
1015 if (role.type == ListLayout::Role::String) {
1016 char *mem = getPropertyMemory(role);
1017 StringOrTranslation *c = reinterpret_cast<StringOrTranslation *>(mem);
1018 bool changed;
1019 if (!c->isSet() || c->isTranslation())
1020 changed = true;
1021 else
1022 changed = c->asString().compare(s) != 0;
1023 c->setString(s);
1024 if (changed)
1025 roleIndex = role.index;
1026 }
1027
1028 return roleIndex;
1029}
1030
1031int ListElement::setDoubleProperty(const ListLayout::Role &role, double d)
1032{
1033 int roleIndex = -1;
1034
1035 if (role.type == ListLayout::Role::Number) {
1036 char *mem = getPropertyMemory(role);
1037 double *value = reinterpret_cast<double *>(mem);
1038 bool changed = *value != d;
1039 *value = d;
1040 if (changed)
1041 roleIndex = role.index;
1042 }
1043
1044 return roleIndex;
1045}
1046
1047int ListElement::setBoolProperty(const ListLayout::Role &role, bool b)
1048{
1049 int roleIndex = -1;
1050
1051 if (role.type == ListLayout::Role::Bool) {
1052 char *mem = getPropertyMemory(role);
1053 bool *value = reinterpret_cast<bool *>(mem);
1054 bool changed = *value != b;
1055 *value = b;
1056 if (changed)
1057 roleIndex = role.index;
1058 }
1059
1060 return roleIndex;
1061}
1062
1063int ListElement::setListProperty(const ListLayout::Role &role, ListModel *m)
1064{
1065 int roleIndex = -1;
1066
1067 if (role.type == ListLayout::Role::List) {
1068 char *mem = getPropertyMemory(role);
1069 ListModel **value = reinterpret_cast<ListModel **>(mem);
1070 if (*value && *value != m) {
1071 (*value)->destroy();
1072 delete *value;
1073 }
1074 *value = m;
1075 roleIndex = role.index;
1076 }
1077
1078 return roleIndex;
1079}
1080
1081int ListElement::setQObjectProperty(const ListLayout::Role &role, QV4::QObjectWrapper *o)
1082{
1083 int roleIndex = -1;
1084
1085 if (role.type == ListLayout::Role::QObject) {
1086 char *mem = getPropertyMemory(role);
1087 if (isMemoryUsed<QVariantMap>(mem))
1088 reinterpret_cast<QV4::PersistentValue *>(mem)->set(o->engine(), *o);
1089 else
1090 new (mem) QV4::PersistentValue(o->engine(), o);
1091 roleIndex = role.index;
1092 }
1093
1094 return roleIndex;
1095}
1096
1097int ListElement::setVariantMapProperty(const ListLayout::Role &role, QV4::Object *o)
1098{
1099 int roleIndex = -1;
1100
1101 if (role.type == ListLayout::Role::VariantMap) {
1102 char *mem = getPropertyMemory(role);
1103 if (isMemoryUsed<QVariantMap>(mem)) {
1104 QVariantMap *map = reinterpret_cast<QVariantMap *>(mem);
1105 map->~QMap();
1106 }
1107 new (mem) QVariantMap(o->engine()->variantMapFromJS(o));
1108 roleIndex = role.index;
1109 }
1110
1111 return roleIndex;
1112}
1113
1114int ListElement::setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m)
1115{
1116 int roleIndex = -1;
1117
1118 if (role.type == ListLayout::Role::VariantMap) {
1119 char *mem = getPropertyMemory(role);
1120 if (isMemoryUsed<QVariantMap>(mem)) {
1121 QVariantMap *map = reinterpret_cast<QVariantMap *>(mem);
1122 if (m && map->isSharedWith(*m))
1123 return roleIndex;
1124 map->~QMap();
1125 } else if (!m) {
1126 return roleIndex;
1127 }
1128 if (m)
1129 new (mem) QVariantMap(*m);
1130 else
1131 new (mem) QVariantMap;
1132 roleIndex = role.index;
1133 }
1134
1135 return roleIndex;
1136}
1137
1138int ListElement::setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt)
1139{
1140 int roleIndex = -1;
1141
1142 if (role.type == ListLayout::Role::DateTime) {
1143 char *mem = getPropertyMemory(role);
1144 if (isMemoryUsed<QDateTime>(mem)) {
1145 QDateTime *dt = reinterpret_cast<QDateTime *>(mem);
1146 dt->~QDateTime();
1147 }
1148 new (mem) QDateTime(dt);
1149 roleIndex = role.index;
1150 }
1151
1152 return roleIndex;
1153}
1154
1155int ListElement::setUrlProperty(const ListLayout::Role &role, const QUrl &url)
1156{
1157 int roleIndex = -1;
1158
1159 if (role.type == ListLayout::Role::Url) {
1160 char *mem = getPropertyMemory(role);
1161 if (isMemoryUsed<QUrl>(mem)) {
1162 QUrl *qurl = reinterpret_cast<QUrl *>(mem);
1163 qurl->~QUrl();
1164 }
1165 new (mem) QUrl(url);
1166 roleIndex = role.index;
1167 }
1168
1169 return roleIndex;
1170}
1171
1172int ListElement::setFunctionProperty(const ListLayout::Role &role, const QJSValue &f)
1173{
1174 int roleIndex = -1;
1175
1176 if (role.type == ListLayout::Role::Function) {
1177 char *mem = getPropertyMemory(role);
1178 if (isMemoryUsed<QJSValue>(mem)) {
1179 QJSValue *f = reinterpret_cast<QJSValue *>(mem);
1180 f->~QJSValue();
1181 }
1182 new (mem) QJSValue(f);
1183 roleIndex = role.index;
1184 }
1185
1186 return roleIndex;
1187}
1188
1189int ListElement::setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b)
1190{
1191 int roleIndex = -1;
1192
1193 if (role.type == ListLayout::Role::String) {
1194 char *mem = getPropertyMemory(role);
1195 StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem);
1196 s->setTranslation(b);
1197 roleIndex = role.index;
1198 }
1199
1200 return roleIndex;
1201}
1202
1203
1204void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s)
1205{
1206 char *mem = getPropertyMemory(role);
1207 reinterpret_cast<StringOrTranslation *>(mem)->setString(s);
1208}
1209
1210void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d)
1211{
1212 char *mem = getPropertyMemory(role);
1213 double *value = new (mem) double;
1214 *value = d;
1215}
1216
1217void ListElement::setBoolPropertyFast(const ListLayout::Role &role, bool b)
1218{
1219 char *mem = getPropertyMemory(role);
1220 bool *value = new (mem) bool;
1221 *value = b;
1222}
1223
1224void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QV4::QObjectWrapper *o)
1225{
1226 char *mem = getPropertyMemory(role);
1227 new (mem) QV4::PersistentValue(o->engine(), o);
1228}
1229
1230void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m)
1231{
1232 char *mem = getPropertyMemory(role);
1233 ListModel **value = new (mem) ListModel *;
1234 *value = m;
1235}
1236
1237void ListElement::setVariantMapFast(const ListLayout::Role &role, QV4::Object *o)
1238{
1239 char *mem = getPropertyMemory(role);
1240 QVariantMap *map = new (mem) QVariantMap;
1241 *map = o->engine()->variantMapFromJS(o);
1242}
1243
1244void ListElement::setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt)
1245{
1246 char *mem = getPropertyMemory(role);
1247 new (mem) QDateTime(dt);
1248}
1249
1250void ListElement::setUrlPropertyFast(const ListLayout::Role &role, const QUrl &url)
1251{
1252 char *mem = getPropertyMemory(role);
1253 new (mem) QUrl(url);
1254}
1255
1256void ListElement::setFunctionPropertyFast(const ListLayout::Role &role, const QJSValue &f)
1257{
1258 char *mem = getPropertyMemory(role);
1259 new (mem) QJSValue(f);
1260}
1261
1262void ListElement::clearProperty(const ListLayout::Role &role)
1263{
1264 switch (role.type) {
1266 setStringProperty(role, QString());
1267 break;
1269 setDoubleProperty(role, 0.0);
1270 break;
1272 setBoolProperty(role, false);
1273 break;
1275 setListProperty(role, nullptr);
1276 break;
1278 setQObjectProperty(role, nullptr);
1279 break;
1281 setDateTimeProperty(role, QDateTime());
1282 break;
1284 setUrlProperty(role, QUrl());
1285 break;
1287 setVariantMapProperty(role, (QVariantMap *)nullptr);
1288 break;
1290 setFunctionProperty(role, QJSValue());
1291 break;
1292 default:
1293 break;
1294 }
1295}
1296
1298{
1299 m_objectCache = nullptr;
1301 next = nullptr;
1302 memset(data, 0, sizeof(data));
1303}
1304
1306{
1307 m_objectCache = nullptr;
1308 uid = existingUid;
1309 next = nullptr;
1310 memset(data, 0, sizeof(data));
1311}
1312
1314{
1315 delete next;
1316}
1317
1319{
1320 QVector<int> changedRoles;
1321 for (int i=0 ; i < srcLayout->roleCount() ; ++i) {
1322 const ListLayout::Role &srcRole = srcLayout->getExistingRole(i);
1323 const ListLayout::Role &targetRole = targetLayout->getExistingRole(i);
1324
1325 int roleIndex = -1;
1326 switch (srcRole.type) {
1328 {
1329 ListModel *srcSubModel = src->getListProperty(srcRole);
1330 ListModel *targetSubModel = target->getListProperty(targetRole);
1331
1332 if (srcSubModel) {
1333 if (targetSubModel == nullptr) {
1334 targetSubModel = new ListModel(targetRole.subLayout, nullptr);
1335 target->setListPropertyFast(targetRole, targetSubModel);
1336 }
1337 if (ListModel::sync(srcSubModel, targetSubModel))
1338 roleIndex = targetRole.index;
1339 }
1340 }
1341 break;
1343 {
1344 QV4::QObjectWrapper *object = src->getQObjectProperty(srcRole);
1345 roleIndex = target->setQObjectProperty(targetRole, object);
1346 }
1347 break;
1353 {
1354 QVariant v = src->getProperty(srcRole, nullptr, nullptr);
1355 roleIndex = target->setVariantProperty(targetRole, v);
1356 }
1357 break;
1359 {
1360 QVariantMap *map = src->getVariantMapProperty(srcRole);
1361 roleIndex = target->setVariantMapProperty(targetRole, map);
1362 }
1363 break;
1364 default:
1365 break;
1366 }
1367 if (roleIndex >= 0)
1368 changedRoles << roleIndex;
1369 }
1370
1371 return changedRoles;
1372}
1373
1374void ListElement::destroy(ListLayout *layout)
1375{
1376 if (layout) {
1377 for (int i=0 ; i < layout->roleCount() ; ++i) {
1378 const ListLayout::Role &r = layout->getExistingRole(i);
1379
1380 switch (r.type) {
1382 {
1383 StringOrTranslation *string = getStringProperty(r);
1384 if (string)
1385 string->~StringOrTranslation();
1386 }
1387 break;
1389 {
1390 ListModel *model = getListProperty(r);
1391 if (model) {
1392 model->destroy();
1393 delete model;
1394 }
1395 }
1396 break;
1398 {
1399 if (QV4::PersistentValue *guard = getGuardProperty(r))
1400 guard->~PersistentValue();
1401 }
1402 break;
1404 {
1405 QVariantMap *map = getVariantMapProperty(r);
1406 if (map)
1407 map->~QMap();
1408 }
1409 break;
1411 {
1412 QDateTime *dt = getDateTimeProperty(r);
1413 if (dt)
1414 dt->~QDateTime();
1415 }
1416 break;
1418 {
1419 QUrl *url = getUrlProperty(r);
1420 if (url)
1421 url->~QUrl();
1422 break;
1423 }
1425 {
1426 QJSValue *f = getFunctionProperty(r);
1427 if (f)
1428 f->~QJSValue();
1429 }
1430 break;
1431 default:
1432 // other types don't need explicit cleanup.
1433 break;
1434 }
1435 }
1436
1437 if (m_objectCache) {
1438 m_objectCache->~QObject();
1439 operator delete(m_objectCache);
1440 }
1441 }
1442
1443 if (next)
1444 next->destroy(nullptr);
1445 uid = -1;
1446}
1447
1448int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant &d)
1449{
1450 int roleIndex = -1;
1451
1452 switch (role.type) {
1454 roleIndex = setDoubleProperty(role, d.toDouble());
1455 break;
1457 if (d.userType() == qMetaTypeId<const QV4::CompiledData::Binding *>())
1458 roleIndex = setTranslationProperty(role, d.value<const QV4::CompiledData::Binding*>());
1459 else
1460 roleIndex = setStringProperty(role, d.toString());
1461 break;
1463 roleIndex = setBoolProperty(role, d.toBool());
1464 break;
1466 roleIndex = setListProperty(role, d.value<ListModel *>());
1467 break;
1469 QVariantMap map = d.toMap();
1470 roleIndex = setVariantMapProperty(role, &map);
1471 }
1472 break;
1474 roleIndex = setDateTimeProperty(role, d.toDateTime());
1475 break;
1477 roleIndex = setUrlProperty(role, d.toUrl());
1478 break;
1480 roleIndex = setFunctionProperty(role, d.value<QJSValue>());
1481 break;
1482 default:
1483 break;
1484 }
1485
1486 return roleIndex;
1487}
1488
1489int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d, QV4::ExecutionEngine *eng)
1490{
1491 // Check if this key exists yet
1492 int roleIndex = -1;
1493
1494 QV4::Scope scope(eng);
1495
1496 // Add the value now
1497 if (d.isString()) {
1498 QString qstr = d.toQString();
1499 roleIndex = setStringProperty(role, qstr);
1500 } else if (d.isNumber()) {
1501 roleIndex = setDoubleProperty(role, d.asDouble());
1502 } else if (d.as<QV4::ArrayObject>()) {
1503 QV4::ScopedArrayObject a(scope, d);
1504 if (role.type == ListLayout::Role::List) {
1505 QV4::Scope scope(a->engine());
1506 QV4::ScopedObject o(scope);
1507
1508 ListModel *subModel = new ListModel(role.subLayout, nullptr);
1509 int arrayLength = a->getLength();
1510 for (int j=0 ; j < arrayLength ; ++j) {
1511 o = a->get(j);
1512 subModel->append(o);
1513 }
1514 roleIndex = setListProperty(role, subModel);
1515 } else {
1516 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));
1517 }
1518 } else if (d.isBoolean()) {
1519 roleIndex = setBoolProperty(role, d.booleanValue());
1520 } else if (d.as<QV4::DateObject>()) {
1522 QDateTime dt = dd->toQDateTime();
1523 roleIndex = setDateTimeProperty(role, dt);
1524 } else if (d.as<QV4::UrlObject>()) {
1526 QUrl qurl = QUrl(url->href());
1527 roleIndex = setUrlProperty(role, qurl);
1528 } else if (d.as<QV4::FunctionObject>()) {
1530 QJSValue jsv;
1532 roleIndex = setFunctionProperty(role, jsv);
1533 } else if (d.isObject()) {
1534 QV4::ScopedObject o(scope, d);
1536 if (role.type == ListLayout::Role::QObject && wrapper) {
1537 roleIndex = setQObjectProperty(role, wrapper);
1538 } else if (role.type == ListLayout::Role::VariantMap) {
1539 roleIndex = setVariantMapProperty(role, o);
1540 } else if (role.type == ListLayout::Role::Url) {
1542 o.asReturnedValue(), QMetaType::fromType<QUrl>(), true);
1543 if (maybeUrl.metaType() == QMetaType::fromType<QUrl>()) {
1544 roleIndex = setUrlProperty(role, maybeUrl.toUrl());
1545 }
1546 }
1547 } else if (d.isNullOrUndefined()) {
1548 clearProperty(role);
1549 }
1550
1551 return roleIndex;
1552}
1553
1555: QQmlOpenMetaObject(object), m_enabled(false), m_model(model), m_elementIndex(elementIndex), m_initialized(false)
1556{}
1557
1558void ModelNodeMetaObject::initialize()
1559{
1560 const int roleCount = m_model->m_listModel->roleCount();
1562 properties.reserve(roleCount);
1563 for (int i = 0 ; i < roleCount ; ++i) {
1564 const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i);
1565 QByteArray name = role.name.toUtf8();
1566 properties << name;
1567 }
1569 updateValues();
1570 m_enabled = true;
1571}
1572
1574{
1575}
1576
1578{
1579 if (!m_initialized) {
1580 m_initialized = true;
1581 initialize();
1582 }
1584}
1585
1587{
1589 return static_cast<ModelNodeMetaObject*>(op->metaObject);
1590}
1591
1593{
1594 const int roleCount = m_model->m_listModel->roleCount();
1595 if (!m_initialized) {
1596 if (roleCount) {
1597 Q_ALLOCA_VAR(int, changedRoles, roleCount * sizeof(int));
1598 for (int i = 0; i < roleCount; ++i)
1599 changedRoles[i] = i;
1600 emitDirectNotifies(changedRoles, roleCount);
1601 }
1602 return;
1603 }
1604 for (int i=0 ; i < roleCount ; ++i) {
1605 const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i);
1606 QByteArray name = role.name.toUtf8();
1608 setValue(name, data, role.type == ListLayout::Role::List);
1609 }
1610}
1611
1613{
1614 if (!m_initialized) {
1615 emitDirectNotifies(roles.constData(), roles.size());
1616 return;
1617 }
1618 int roleCount = roles.size();
1619 for (int i=0 ; i < roleCount ; ++i) {
1620 int roleIndex = roles.at(i);
1621 const ListLayout::Role &role = m_model->m_listModel->getExistingRole(roleIndex);
1622 QByteArray name = role.name.toUtf8();
1623 const QVariant &data = m_model->data(m_elementIndex, roleIndex);
1624 setValue(name, data, role.type == ListLayout::Role::List);
1625 }
1626}
1627
1629{
1630 if (!m_enabled)
1631 return;
1632
1633 QString propName = QString::fromUtf8(name(index));
1634 const QVariant value = this->value(index);
1635
1636 QV4::Scope scope(m_model->engine());
1637 QV4::ScopedValue v(scope, scope.engine->fromVariant(value));
1638
1639 int roleIndex = m_model->m_listModel->setExistingProperty(m_elementIndex, propName, v, scope.engine);
1640 if (roleIndex != -1)
1641 m_model->emitItemsChanged(m_elementIndex, 1, QVector<int>(1, roleIndex));
1642}
1643
1644// Does the emission of the notifiers when we haven't created the meta-object yet
1645void ModelNodeMetaObject::emitDirectNotifies(const int *changedRoles, int roleCount)
1646{
1647 Q_ASSERT(!m_initialized);
1648 QQmlData *ddata = QQmlData::get(object(), /*create*/false);
1649 if (!ddata)
1650 return;
1651 // There's nothing to emit if we're a list model in a worker thread.
1652 if (!qmlEngine(m_model))
1653 return;
1654 for (int i = 0; i < roleCount; ++i) {
1655 const int changedRole = changedRoles[i];
1656 QQmlNotifier::notify(ddata, changedRole);
1657 }
1658}
1659
1660namespace QV4 {
1661
1663{
1664 if (!id.isString())
1665 return Object::virtualPut(m, id, value, receiver);
1666 QString propName = id.toQString();
1667
1668 ModelObject *that = static_cast<ModelObject*>(m);
1669
1670 ExecutionEngine *eng = that->engine();
1671 const int elementIndex = that->d()->elementIndex();
1672 int roleIndex = that->d()->m_model->m_listModel->setExistingProperty(elementIndex, propName, value, eng);
1673 if (roleIndex != -1)
1674 that->d()->m_model->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex));
1675
1677 if (mo->initialized())
1678 mo->emitPropertyNotification(propName.toUtf8());
1679 return true;
1680}
1681
1682ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
1683{
1684 if (!id.isString())
1685 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1686
1687 const ModelObject *that = static_cast<const ModelObject*>(m);
1688 Scope scope(that);
1689 ScopedString name(scope, id.asStringOrSymbol());
1690 const ListLayout::Role *role = that->d()->m_model->m_listModel->getExistingRole(name);
1691 if (!role)
1692 return QObjectWrapper::virtualGet(m, id, receiver, hasProperty);
1693 if (hasProperty)
1694 *hasProperty = true;
1695
1696 if (QQmlEngine *qmlEngine = that->engine()->qmlEngine()) {
1698 if (ep && ep->propertyCapture)
1699 ep->propertyCapture->captureProperty(that->object(), -1, role->index, /*doNotify=*/ false);
1700 }
1701
1702 const int elementIndex = that->d()->elementIndex();
1703 QVariant value = that->d()->m_model->data(elementIndex, role->index);
1704 return that->engine()->fromVariant(value);
1705}
1706
1708{
1710 return lookup->getter(lookup, engine, *object);
1711}
1712
1714{
1717 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
1718
1719};
1720
1722{
1723 const ModelObject *that = static_cast<const ModelObject *>(o);
1724
1725 ExecutionEngine *v4 = that->engine();
1726 if (roleNameIndex < that->listModel()->roleCount()) {
1727 Scope scope(that->engine());
1729 ++roleNameIndex;
1730 ScopedString roleName(scope, v4->newString(role.name));
1731 if (attrs)
1733 if (pd) {
1734
1735 QVariant value = that->d()->m_model->data(that->d()->elementIndex(), role.index);
1736 if (auto recursiveListModel = qvariant_cast<QQmlListModel*>(value)) {
1737 auto size = recursiveListModel->count();
1738 auto array = ScopedArrayObject{scope, v4->newArrayObject(size)};
1739 for (auto i = 0; i < size; i++) {
1741 v4, recursiveListModel->get(i)));
1742 }
1743 pd->value = array;
1744 } else {
1745 pd->value = v4->fromVariant(value);
1746 }
1747 }
1748 return roleName->toPropertyKey();
1749 }
1750
1751 // Fall back to QV4::Object as opposed to QV4::QObjectWrapper otherwise it will add
1752 // unnecessary entries that relate to the roles used. These just create extra work
1753 // later on as they will just be ignored.
1755}
1756
1758{
1759 *target = *m;
1761}
1762
1764
1765} // namespace QV4
1766
1767DynamicRoleModelNode::DynamicRoleModelNode(QQmlListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this))
1768{
1770}
1771
1773{
1775 QVector<int> roles;
1776 object->updateValues(obj, roles);
1777 return object;
1778}
1779
1781{
1782 QVector<int> changedRoles;
1783 for (int i = 0; i < src->m_meta->count(); ++i) {
1784 const QByteArray &name = src->m_meta->name(i);
1785 QVariant value = src->m_meta->value(i);
1786
1787 QQmlListModel *srcModel = qobject_cast<QQmlListModel *>(value.value<QObject *>());
1788 QQmlListModel *targetModel = qobject_cast<QQmlListModel *>(target->m_meta->value(i).value<QObject *>());
1789
1790 bool modelHasChanges = false;
1791 if (srcModel) {
1792 if (targetModel == nullptr)
1793 targetModel = QQmlListModel::createWithOwner(target->m_owner);
1794
1795 modelHasChanges = QQmlListModel::sync(srcModel, targetModel);
1796
1797 QObject *targetModelObject = targetModel;
1798 value = QVariant::fromValue(targetModelObject);
1799 } else if (targetModel) {
1800 delete targetModel;
1801 }
1802
1803 if (target->setValue(name, value) || modelHasChanges)
1804 changedRoles << target->m_owner->m_roles.indexOf(QString::fromUtf8(name));
1805 }
1806 return changedRoles;
1807}
1808
1810{
1811 for (auto it = object.cbegin(), end = object.cend(); it != end; ++it) {
1812 const QString &key = it.key();
1813
1814 int roleIndex = m_owner->m_roles.indexOf(key);
1815 if (roleIndex == -1) {
1816 roleIndex = m_owner->m_roles.size();
1817 m_owner->m_roles.append(key);
1818 }
1819
1820 QVariant value = it.value();
1821
1822 // A JS array/object is translated into a (hierarchical) QQmlListModel,
1823 // so translate to a variant map/list first with toVariant().
1824 if (value.userType() == qMetaTypeId<QJSValue>())
1825 value = value.value<QJSValue>().toVariant();
1826
1827 if (value.userType() == QMetaType::QVariantList) {
1828 QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner);
1829
1830 QVariantList subArray = value.toList();
1831 QVariantList::const_iterator subIt = subArray.cbegin();
1832 QVariantList::const_iterator subEnd = subArray.cend();
1833 while (subIt != subEnd) {
1834 const QVariantMap &subObject = subIt->toMap();
1835 subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1836 ++subIt;
1837 }
1838
1839 QObject *subModelObject = subModel;
1840 value = QVariant::fromValue(subModelObject);
1841 }
1842
1843 const QByteArray &keyUtf8 = key.toUtf8();
1844
1845 QQmlListModel *existingModel = qobject_cast<QQmlListModel *>(m_meta->value(keyUtf8).value<QObject *>());
1846 delete existingModel;
1847
1848 if (m_meta->setValue(keyUtf8, value))
1849 roles << roleIndex;
1850 }
1851}
1852
1854 : QQmlOpenMetaObject(object), m_enabled(false), m_owner(object)
1855{
1856}
1857
1859{
1860 for (int i=0 ; i < count() ; ++i) {
1861 QQmlListModel *subModel = qobject_cast<QQmlListModel *>(value(i).value<QObject *>());
1862 delete subModel;
1863 }
1864}
1865
1867{
1868 if (!m_enabled)
1869 return;
1870
1871 QVariant v = value(index);
1872 QQmlListModel *model = qobject_cast<QQmlListModel *>(v.value<QObject *>());
1873 delete model;
1874}
1875
1877{
1878 if (!m_enabled)
1879 return;
1880
1881 QQmlListModel *parentModel = m_owner->m_owner;
1882
1883 QVariant v = value(index);
1884
1885 // A JS array/object is translated into a (hierarchical) QQmlListModel,
1886 // so translate to a variant map/list first with toVariant().
1887 if (v.userType() == qMetaTypeId<QJSValue>())
1888 v= v.value<QJSValue>().toVariant();
1889
1890 if (v.userType() == QMetaType::QVariantList) {
1891 QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel);
1892
1893 QVariantList subArray = v.toList();
1894 QVariantList::const_iterator subIt = subArray.cbegin();
1895 QVariantList::const_iterator subEnd = subArray.cend();
1896 while (subIt != subEnd) {
1897 const QVariantMap &subObject = subIt->toMap();
1898 subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel));
1899 ++subIt;
1900 }
1901
1902 QObject *subModelObject = subModel;
1903 v = QVariant::fromValue(subModelObject);
1904
1905 setValue(index, v);
1906 }
1907
1908 int elementIndex = parentModel->m_modelObjects.indexOf(m_owner);
1909 if (elementIndex != -1) {
1910 int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData()));
1911 if (roleIndex != -1)
1912 parentModel->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex));
1913 }
1914}
1915
2012{
2013 m_mainThread = true;
2014 m_primary = true;
2015 m_agent = nullptr;
2016 m_dynamicRoles = false;
2017
2018 m_layout = new ListLayout;
2019 m_listModel = new ListModel(m_layout, this);
2020
2021 m_engine = nullptr;
2022}
2023
2026{
2027 m_mainThread = owner->m_mainThread;
2028 m_primary = false;
2029 m_agent = owner->m_agent;
2030
2031 Q_ASSERT(owner->m_dynamicRoles == false);
2032 m_dynamicRoles = false;
2033 m_layout = nullptr;
2034 m_listModel = data;
2035
2036 m_engine = engine;
2037 m_compilationUnit = owner->m_compilationUnit;
2038}
2039
2041: QAbstractListModel(agent)
2042{
2043 m_mainThread = false;
2044 m_primary = true;
2045 m_agent = agent;
2046 m_dynamicRoles = orig->m_dynamicRoles;
2047
2048 m_layout = new ListLayout(orig->m_layout);
2049 m_listModel = new ListModel(m_layout, this);
2050
2051 if (m_dynamicRoles)
2052 sync(orig, this);
2053 else
2054 ListModel::sync(orig->m_listModel, m_listModel);
2055
2056 m_engine = nullptr;
2057 m_compilationUnit = orig->m_compilationUnit;
2058}
2059
2061{
2062 qDeleteAll(m_modelObjects);
2063
2064 if (m_primary) {
2065 m_listModel->destroy();
2066 delete m_listModel;
2067
2068 if (m_mainThread && m_agent) {
2069 m_agent->modelDestroyed();
2070 m_agent->release();
2071 }
2072 }
2073
2074 m_listModel = nullptr;
2075
2076 delete m_layout;
2077 m_layout = nullptr;
2078}
2079
2080QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner)
2081{
2083
2084 model->m_mainThread = newOwner->m_mainThread;
2085 model->m_engine = newOwner->m_engine;
2086 model->m_agent = newOwner->m_agent;
2087 model->m_dynamicRoles = newOwner->m_dynamicRoles;
2088
2089 if (model->m_mainThread && model->m_agent)
2090 model->m_agent->addref();
2091
2093
2094 return model;
2095}
2096
2097QV4::ExecutionEngine *QQmlListModel::engine() const
2098{
2099 if (m_engine == nullptr) {
2100 m_engine = qmlEngine(this)->handle();
2101 }
2102
2103 return m_engine;
2104}
2105
2107{
2108 Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles);
2109
2110 bool hasChanges = false;
2111
2112 target->m_roles = src->m_roles;
2113
2114 // Build hash of elements <-> uid for each of the lists
2115 QHash<int, ElementSync> elementHash;
2116 for (int i = 0 ; i < target->m_modelObjects.size(); ++i) {
2117 DynamicRoleModelNode *e = target->m_modelObjects.at(i);
2118 int uid = e->getUid();
2119 ElementSync sync;
2120 sync.target = e;
2121 sync.targetIndex = i;
2122 elementHash.insert(uid, sync);
2123 }
2124 for (int i = 0 ; i < src->m_modelObjects.size(); ++i) {
2125 DynamicRoleModelNode *e = src->m_modelObjects.at(i);
2126 int uid = e->getUid();
2127
2128 QHash<int, ElementSync>::iterator it = elementHash.find(uid);
2129 if (it == elementHash.end()) {
2130 ElementSync sync;
2131 sync.src = e;
2132 sync.srcIndex = i;
2133 elementHash.insert(uid, sync);
2134 } else {
2135 ElementSync &sync = it.value();
2136 sync.src = e;
2137 sync.srcIndex = i;
2138 }
2139 }
2140
2141 // Get list of elements that are in the target but no longer in the source. These get deleted first.
2142 int rowsRemoved = 0;
2143 for (int i = 0 ; i < target->m_modelObjects.size() ; ++i) {
2144 DynamicRoleModelNode *element = target->m_modelObjects.at(i);
2145 ElementSync &s = elementHash.find(element->getUid()).value();
2146 Q_ASSERT(s.targetIndex >= 0);
2147 // need to update the targetIndex, to keep it correct after removals
2148 s.targetIndex -= rowsRemoved;
2149 if (s.src == nullptr) {
2150 Q_ASSERT(s.targetIndex == i);
2151 hasChanges = true;
2152 target->beginRemoveRows(QModelIndex(), i, i);
2153 target->m_modelObjects.remove(i, 1);
2154 target->endRemoveRows();
2155 delete s.target;
2156 ++rowsRemoved;
2157 --i;
2158 continue;
2159 }
2160 }
2161
2162 // Clear the target list, and append in correct order from the source
2163 target->m_modelObjects.clear();
2164 for (int i = 0 ; i < src->m_modelObjects.size() ; ++i) {
2165 DynamicRoleModelNode *element = src->m_modelObjects.at(i);
2166 ElementSync &s = elementHash.find(element->getUid()).value();
2167 Q_ASSERT(s.srcIndex >= 0);
2168 DynamicRoleModelNode *targetElement = s.target;
2169 if (targetElement == nullptr) {
2170 targetElement = new DynamicRoleModelNode(target, element->getUid());
2171 }
2172 s.changedRoles = DynamicRoleModelNode::sync(element, targetElement);
2173 target->m_modelObjects.append(targetElement);
2174 }
2175
2176 // now emit the change notifications required. This can be safely done, as we're only emitting changes, moves and inserts,
2177 // so the model indices can't be out of bounds
2178 //
2179 // to ensure things are kept in the correct order, emit inserts and moves first. This shouls ensure all persistent
2180 // model indices are updated correctly
2181 int rowsInserted = 0;
2182 for (int i = 0 ; i < target->m_modelObjects.size() ; ++i) {
2183 DynamicRoleModelNode *element = target->m_modelObjects.at(i);
2184 ElementSync &s = elementHash.find(element->getUid()).value();
2185 Q_ASSERT(s.srcIndex >= 0);
2186 s.srcIndex += rowsInserted;
2187 if (s.srcIndex != s.targetIndex) {
2188 if (s.targetIndex == -1) {
2189 target->beginInsertRows(QModelIndex(), i, i);
2190 target->endInsertRows();
2191 } else {
2192 target->beginMoveRows(QModelIndex(), i, i, QModelIndex(), s.srcIndex);
2193 target->endMoveRows();
2194 }
2195 hasChanges = true;
2196 ++rowsInserted;
2197 }
2198 if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) {
2199 QModelIndex idx = target->createIndex(i, 0);
2200 emit target->dataChanged(idx, idx, s.changedRoles);
2201 hasChanges = true;
2202 }
2203 }
2204 return hasChanges;
2205}
2206
2207void QQmlListModel::emitItemsChanged(int index, int count, const QVector<int> &roles)
2208{
2209 if (count <= 0)
2210 return;
2211
2212 if (m_mainThread)
2213 emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);;
2214}
2215
2216void QQmlListModel::emitItemsAboutToBeInserted(int index, int count)
2217{
2218 Q_ASSERT(index >= 0 && count >= 0);
2219 if (m_mainThread)
2221}
2222
2223void QQmlListModel::emitItemsInserted()
2224{
2225 if (m_mainThread) {
2226 endInsertRows();
2228 }
2229}
2230
2232{
2233 if (m_agent)
2234 return m_agent;
2235
2236 m_agent = new QQmlListModelWorkerAgent(this);
2237 return m_agent;
2238}
2239
2241{
2242 return row >= 0 && row < count() && column == 0 && !parent.isValid()
2244 : QModelIndex();
2245}
2246
2248{
2249 return !parent.isValid() ? count() : 0;
2250}
2251
2253{
2254 return data(index.row(), role);
2255}
2256
2258{
2259 const int row = index.row();
2260 if (row >= count() || row < 0)
2261 return false;
2262
2263 if (m_dynamicRoles) {
2264 const QByteArray property = m_roles.at(role).toUtf8();
2265 if (m_modelObjects[row]->setValue(property, value)) {
2266 emitItemsChanged(row, 1, QVector<int>(1, role));
2267 return true;
2268 }
2269 } else {
2270 const ListLayout::Role &r = m_listModel->getExistingRole(role);
2271 const int roleIndex = m_listModel->setOrCreateProperty(row, r.name, value);
2272 if (roleIndex != -1) {
2273 emitItemsChanged(row, 1, QVector<int>(1, role));
2274 return true;
2275 }
2276 }
2277
2278 return false;
2279}
2280
2282{
2283 QVariant v;
2284
2285 if (index >= count() || index < 0)
2286 return v;
2287
2288 if (m_dynamicRoles)
2289 v = m_modelObjects[index]->getValue(m_roles[role]);
2290 else
2291 v = m_listModel->getProperty(index, role, this, engine());
2292
2293 return v;
2294}
2295
2297{
2299
2300 if (m_dynamicRoles) {
2301 for (int i = 0 ; i < m_roles.size() ; ++i)
2302 roleNames.insert(i, m_roles.at(i).toUtf8());
2303 } else {
2304 for (int i = 0 ; i < m_listModel->roleCount() ; ++i) {
2305 const ListLayout::Role &r = m_listModel->getExistingRole(i);
2306 roleNames.insert(i, r.name.toUtf8());
2307 }
2308 }
2309
2310 return roleNames;
2311}
2312
2339void QQmlListModel::setDynamicRoles(bool enableDynamicRoles)
2340{
2341 if (m_mainThread && m_agent == nullptr) {
2342 if (enableDynamicRoles) {
2343 if (m_layout->roleCount())
2344 qmlWarning(this) << tr("unable to enable dynamic roles as this model is not empty");
2345 else
2346 m_dynamicRoles = true;
2347 } else {
2348 if (m_roles.size()) {
2349 qmlWarning(this) << tr("unable to enable static roles as this model is not empty");
2350 } else {
2351 m_dynamicRoles = false;
2352 }
2353 }
2354 } else {
2355 qmlWarning(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created");
2356 }
2357}
2358
2364{
2365 return m_dynamicRoles ? m_modelObjects.size() : m_listModel->elementCount();
2366}
2367
2376{
2377 removeElements(0, count());
2378}
2379
2388{
2389 int argLength = args->length();
2390
2391 if (argLength == 1 || argLength == 2) {
2392 QV4::Scope scope(args->v4engine());
2393 int index = QV4::ScopedValue(scope, (*args)[0])->toInt32();
2394 int removeCount = (argLength == 2 ? QV4::ScopedValue(scope, (*args)[1])->toInt32() : 1);
2395
2396 if (index < 0 || index+removeCount > count() || removeCount <= 0) {
2397 qmlWarning(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count());
2398 return;
2399 }
2400
2401 removeElements(index, removeCount);
2402 } else {
2403 qmlWarning(this) << tr("remove: incorrect number of arguments");
2404 }
2405}
2406
2407void QQmlListModel::removeElements(int index, int removeCount)
2408{
2409 Q_ASSERT(index >= 0 && removeCount >= 0);
2410
2411 if (!removeCount)
2412 return;
2413
2414 if (m_mainThread)
2415 beginRemoveRows(QModelIndex(), index, index + removeCount - 1);
2416
2417 QVector<std::function<void()>> toDestroy;
2418 if (m_dynamicRoles) {
2419 for (int i=0 ; i < removeCount ; ++i) {
2420 auto modelObject = m_modelObjects[index+i];
2421 toDestroy.append([modelObject](){
2422 delete modelObject;
2423 });
2424 }
2425 m_modelObjects.remove(index, removeCount);
2426 } else {
2427 toDestroy = m_listModel->remove(index, removeCount);
2428 }
2429
2430 if (m_mainThread) {
2431 endRemoveRows();
2433 }
2434 for (const auto &destroyer : toDestroy)
2435 destroyer();
2436}
2437
2438void QQmlListModel::updateTranslations()
2439{
2440 // assumption: it is impossible to have retranslatable strings in a
2441 // dynamic list model, as they would already have "decayed" to strings
2442 // when they were inserted
2443 if (m_dynamicRoles)
2444 return;
2445 Q_ASSERT(m_listModel);
2446
2447 QList<int> roles;
2448 for (int i = 0, end = m_listModel->roleCount(); i != end; ++i) {
2449 if (m_listModel->getExistingRole(i).type == ListLayout::Role::String)
2450 roles.append(i);
2451 }
2452
2453 if (!roles.isEmpty())
2454 emitItemsChanged(0, rowCount(QModelIndex()), roles);
2455
2456 m_listModel->updateTranslations();
2457}
2458
2476{
2477 if (args->length() == 2) {
2478 QV4::Scope scope(args->v4engine());
2479 QV4::ScopedValue arg0(scope, (*args)[0]);
2480 int index = arg0->toInt32();
2481
2482 if (index < 0 || index > count()) {
2483 qmlWarning(this) << tr("insert: index %1 out of range").arg(index);
2484 return;
2485 }
2486
2487 QV4::ScopedObject argObject(scope, (*args)[1]);
2488 QV4::ScopedArrayObject objectArray(scope, (*args)[1]);
2489 if (objectArray) {
2490 QV4::ScopedObject argObject(scope);
2491
2492 int objectArrayLength = objectArray->getLength();
2493 emitItemsAboutToBeInserted(index, objectArrayLength);
2494 for (int i=0 ; i < objectArrayLength ; ++i) {
2495 argObject = objectArray->get(i);
2496
2497 if (m_dynamicRoles) {
2498 m_modelObjects.insert(index+i, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this));
2499 } else {
2500 m_listModel->insert(index+i, argObject);
2501 }
2502 }
2503 emitItemsInserted();
2504 } else if (argObject) {
2505 emitItemsAboutToBeInserted(index, 1);
2506
2507 if (m_dynamicRoles) {
2508 m_modelObjects.insert(index, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this));
2509 } else {
2510 m_listModel->insert(index, argObject);
2511 }
2512
2513 emitItemsInserted();
2514 } else {
2515 qmlWarning(this) << tr("insert: value is not an object");
2516 }
2517 } else {
2518 qmlWarning(this) << tr("insert: value is not an object");
2519 }
2520}
2521
2536void QQmlListModel::move(int from, int to, int n)
2537{
2538 if (n == 0 || from == to)
2539 return;
2540 if (!canMove(from, to, n)) {
2541 qmlWarning(this) << tr("move: out of range");
2542 return;
2543 }
2544
2545 if (m_mainThread)
2546 beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to);
2547
2548 if (m_dynamicRoles) {
2549
2550 int realFrom = from;
2551 int realTo = to;
2552 int realN = n;
2553
2554 if (from > to) {
2555 // Only move forwards - flip if backwards moving
2556 int tfrom = from;
2557 int tto = to;
2558 realFrom = tto;
2559 realTo = tto+n;
2560 realN = tfrom-tto;
2561 }
2562
2564 for (int i=0 ; i < (realTo-realFrom) ; ++i)
2565 store.append(m_modelObjects[realFrom+realN+i]);
2566 for (int i=0 ; i < realN ; ++i)
2567 store.append(m_modelObjects[realFrom+i]);
2568 for (int i=0 ; i < store.count() ; ++i)
2569 m_modelObjects[realFrom+i] = store[i];
2570
2571 } else {
2572 m_listModel->move(from, to, n);
2573 }
2574
2575 if (m_mainThread)
2576 endMoveRows();
2577}
2578
2592{
2593 if (args->length() == 1) {
2594 QV4::Scope scope(args->v4engine());
2595 QV4::ScopedObject argObject(scope, (*args)[0]);
2596 QV4::ScopedArrayObject objectArray(scope, (*args)[0]);
2597
2598 if (objectArray) {
2599 QV4::ScopedObject argObject(scope);
2600
2601 int objectArrayLength = objectArray->getLength();
2602 if (objectArrayLength > 0) {
2603 int index = count();
2604 emitItemsAboutToBeInserted(index, objectArrayLength);
2605
2606 for (int i=0 ; i < objectArrayLength ; ++i) {
2607 argObject = objectArray->get(i);
2608
2609 if (m_dynamicRoles) {
2610 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this));
2611 } else {
2612 m_listModel->append(argObject);
2613 }
2614 }
2615
2616 emitItemsInserted();
2617 }
2618 } else if (argObject) {
2619 int index;
2620
2621 if (m_dynamicRoles) {
2622 index = m_modelObjects.size();
2623 emitItemsAboutToBeInserted(index, 1);
2624 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this));
2625 } else {
2626 index = m_listModel->elementCount();
2627 emitItemsAboutToBeInserted(index, 1);
2628 m_listModel->append(argObject);
2629 }
2630
2631 emitItemsInserted();
2632 } else {
2633 qmlWarning(this) << tr("append: value is not an object");
2634 }
2635 } else {
2636 qmlWarning(this) << tr("append: value is not an object");
2637 }
2638}
2639
2672{
2673 QV4::Scope scope(engine());
2675
2676 if (index >= 0 && index < count()) {
2677
2678 if (m_dynamicRoles) {
2679 DynamicRoleModelNode *object = m_modelObjects[index];
2680 result = QV4::QObjectWrapper::wrap(scope.engine, object);
2681 } else {
2682 QObject *object = m_listModel->getOrCreateModelObject(const_cast<QQmlListModel *>(this), index);
2683 QQmlData *ddata = QQmlData::get(object);
2684 if (ddata->jsWrapper.isNullOrUndefined()) {
2685 result = scope.engine->memoryManager->allocate<QV4::ModelObject>(object, const_cast<QQmlListModel *>(this));
2686 // Keep track of the QObjectWrapper in persistent value storage
2687 ddata->jsWrapper.set(scope.engine, result);
2688 } else {
2689 result = ddata->jsWrapper.value();
2690 }
2691 }
2692 }
2693
2694 return QJSValuePrivate::fromReturnedValue(result->asReturnedValue());
2695}
2696
2714{
2715 QV4::Scope scope(engine());
2717
2718 if (!object) {
2719 qmlWarning(this) << tr("set: value is not an object");
2720 return;
2721 }
2722 if (index > count() || index < 0) {
2723 qmlWarning(this) << tr("set: index %1 out of range").arg(index);
2724 return;
2725 }
2726
2727
2728 if (index == count()) {
2729 emitItemsAboutToBeInserted(index, 1);
2730
2731 if (m_dynamicRoles) {
2732 m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(object), this));
2733 } else {
2734 m_listModel->insert(index, object);
2735 }
2736
2737 emitItemsInserted();
2738 } else {
2739
2740 QVector<int> roles;
2741
2742 if (m_dynamicRoles) {
2743 m_modelObjects[index]->updateValues(scope.engine->variantMapFromJS(object), roles);
2744 } else {
2745 m_listModel->set(index, object, &roles);
2746 }
2747
2748 if (roles.size())
2749 emitItemsChanged(index, 1, roles);
2750 }
2751}
2752
2767{
2768 if (count() == 0 || index >= count() || index < 0) {
2769 qmlWarning(this) << tr("set: index %1 out of range").arg(index);
2770 return;
2771 }
2772
2773 if (m_dynamicRoles) {
2774 int roleIndex = m_roles.indexOf(property);
2775 if (roleIndex == -1) {
2776 roleIndex = m_roles.size();
2777 m_roles.append(property);
2778 }
2779 if (m_modelObjects[index]->setValue(property.toUtf8(), value))
2780 emitItemsChanged(index, 1, QVector<int>(1, roleIndex));
2781 } else {
2782 int roleIndex = m_listModel->setOrCreateProperty(index, property, value);
2783 if (roleIndex != -1)
2784 emitItemsChanged(index, 1, QVector<int>(1, roleIndex));
2785 }
2786}
2787
2795{
2796 // This is just a dummy method to make it look like sync() exists in
2797 // ListModel (and not just QQmlListModelWorkerAgent) and to let
2798 // us document sync().
2799 qmlWarning(this) << "List sync() can only be called from a WorkerScript";
2800}
2801
2802bool QQmlListModelParser::verifyProperty(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding)
2803{
2805 const quint32 targetObjectIndex = binding->value.objectIndex;
2806 const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
2807 QString objName = compilationUnit->stringAt(target->inheritedTypeNameIndex);
2808 if (objName != listElementTypeName) {
2809 const QMetaObject *mo = resolveType(objName);
2810 if (mo != &QQmlListElement::staticMetaObject) {
2811 error(target, QQmlListModel::tr("ListElement: cannot contain nested elements"));
2812 return false;
2813 }
2814 listElementTypeName = objName; // cache right name for next time
2815 }
2816
2817 if (!compilationUnit->stringAt(target->idNameIndex).isEmpty()) {
2818 error(target->locationOfIdProperty, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property"));
2819 return false;
2820 }
2821
2822 const QV4::CompiledData::Binding *binding = target->bindingTable();
2823 for (quint32 i = 0; i < target->nBindings; ++i, ++binding) {
2824 QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
2825 if (propName.isEmpty()) {
2826 error(binding, QQmlListModel::tr("ListElement: cannot contain nested elements"));
2827 return false;
2828 }
2829 if (!verifyProperty(compilationUnit, binding))
2830 return false;
2831 }
2832 } else if (binding->type() == QV4::CompiledData::Binding::Type_Script) {
2833 QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
2834 if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) {
2835 bool ok;
2836 evaluateEnum(scriptStr, &ok);
2837 if (!ok) {
2838 error(binding, QQmlListModel::tr("ListElement: cannot use script for property value"));
2839 return false;
2840 }
2841 }
2842 }
2843
2844 return true;
2845}
2846
2847bool QQmlListModelParser::applyProperty(
2849 const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex)
2850{
2851 const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex);
2852
2853 bool roleSet = false;
2854 const QV4::CompiledData::Binding::Type bindingType = binding->type();
2855 if (bindingType >= QV4::CompiledData::Binding::Type_Object) {
2856 const quint32 targetObjectIndex = binding->value.objectIndex;
2857 const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex);
2858
2859 ListModel *subModel = nullptr;
2860 if (outterElementIndex == -1) {
2861 subModel = model;
2862 } else {
2863 const ListLayout::Role &role = model->getOrCreateListRole(elementName);
2864 if (role.type == ListLayout::Role::List) {
2865 subModel = model->getListProperty(outterElementIndex, role);
2866 if (subModel == nullptr) {
2867 subModel = new ListModel(role.subLayout, nullptr);
2868 QVariant vModel = QVariant::fromValue(subModel);
2869 model->setOrCreateProperty(outterElementIndex, elementName, vModel);
2870 }
2871 }
2872 }
2873
2874 int elementIndex = subModel ? subModel->appendElement() : -1;
2875
2876 const QV4::CompiledData::Binding *subBinding = target->bindingTable();
2877 for (quint32 i = 0; i < target->nBindings; ++i, ++subBinding) {
2878 roleSet |= applyProperty(compilationUnit, subBinding, subModel, elementIndex);
2879 }
2880
2881 } else {
2883
2884 const bool isTranslationBinding = binding->isTranslationBinding();
2885 if (isTranslationBinding) {
2886 value = QVariant::fromValue<const QV4::CompiledData::Binding*>(binding);
2887 } else if (binding->evaluatesToString()) {
2888 value = compilationUnit->bindingValueAsString(binding);
2889 } else if (bindingType == QV4::CompiledData::Binding::Type_Number) {
2890 value = compilationUnit->bindingValueAsNumber(binding);
2891 } else if (bindingType == QV4::CompiledData::Binding::Type_Boolean) {
2892 value = binding->valueAsBoolean();
2893 } else if (bindingType == QV4::CompiledData::Binding::Type_Null) {
2894 value = QVariant::fromValue(nullptr);
2895 } else if (bindingType == QV4::CompiledData::Binding::Type_Script) {
2896 QString scriptStr = compilationUnit->bindingValueAsScriptString(binding);
2897 if (definesEmptyList(scriptStr)) {
2898 const ListLayout::Role &role = model->getOrCreateListRole(elementName);
2899 ListModel *emptyModel = new ListModel(role.subLayout, nullptr);
2900 value = QVariant::fromValue(emptyModel);
2901 } else if (binding->isFunctionExpression()) {
2904
2905 auto v4 = compilationUnit->engine;
2906 QV4::Scope scope(v4);
2907 // for now we do not provide a context object; data from the ListElement must be passed to the function
2908 QV4::ScopedContext context(scope, QV4::QmlContext::create(v4->rootContext(), QQmlContextData::get(qmlContext(model->m_modelCache)), nullptr));
2909 QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(context, compilationUnit->runtimeFunctions[id]));
2910
2911 QJSValue v;
2912 QV4::ScopedValue result(scope, function->call(v4->globalObject, nullptr, 0));
2913 if (v4->hasException)
2914 v4->catchException();
2915 else
2916 QJSValuePrivate::setValue(&v, result->asReturnedValue());
2917 value.setValue(v);
2918 } else {
2919 bool ok;
2920 value = evaluateEnum(scriptStr, &ok);
2921 }
2922 } else {
2923 Q_UNREACHABLE();
2924 }
2925
2926 if (!model)
2927 return roleSet;
2928 model->setOrCreateProperty(outterElementIndex, elementName, value);
2929 auto listModel = model->m_modelCache;
2930 if (isTranslationBinding && listModel) {
2931 if (!listModel->translationChangeHandler) {
2932 auto ep = QQmlEnginePrivate::get(compilationUnit->engine);
2933 model->m_modelCache->translationChangeHandler = std::make_unique<QPropertyNotifier>(
2934 ep->translationLanguage.addNotifier([listModel](){ listModel->updateTranslations(); }));
2935 }
2936 }
2937 roleSet = true;
2938 }
2939 return roleSet;
2940}
2941
2943{
2944 listElementTypeName = QString(); // unknown
2945
2946 for (const QV4::CompiledData::Binding *binding : bindings) {
2947 QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
2948 if (!propName.isEmpty()) { // isn't default property
2949 error(binding, QQmlListModel::tr("ListModel: undefined property '%1'").arg(propName));
2950 return;
2951 }
2952 if (!verifyProperty(compilationUnit, binding))
2953 return;
2954 }
2955}
2956
2958{
2959 QQmlListModel *rv = static_cast<QQmlListModel *>(obj);
2960
2961 rv->m_engine = qmlEngine(rv)->handle();
2962 rv->m_compilationUnit = compilationUnit;
2963
2964 bool setRoles = false;
2965
2966 for (const QV4::CompiledData::Binding *binding : bindings) {
2968 continue;
2969 setRoles |= applyProperty(compilationUnit, binding, rv->m_listModel, /*outter element index*/-1);
2970 }
2971
2972 if (setRoles == false)
2973 qmlWarning(obj) << "All ListElement declarations are empty, no roles can be created unless dynamicRoles is set.";
2974}
2975
2976bool QQmlListModelParser::definesEmptyList(const QString &s)
2977{
2978 if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) {
2979 for (int i=1; i<s.size()-1; i++) {
2980 if (!s[i].isSpace())
2981 return false;
2982 }
2983 return true;
2984 }
2985 return false;
2986}
2987
2988
3036
3037#include "moc_qqmllistmodel_p_p.cpp"
3038
3039#include "moc_qqmllistmodel_p.cpp"
DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object)
void propertyWritten(int index) override
void propertyWrite(int index) override
static QVector< int > sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target)
static DynamicRoleModelNode * create(const QVariantMap &obj, QQmlListModel *owner)
DynamicRoleModelNode(QQmlListModel *owner, int uid)
void setNodeUpdatesEnabled(bool enable)
void updateValues(const QVariantMap &object, QVector< int > &roles)
friend class ListModel
static QVector< int > sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout)
int roleCount() const
const Role * getRoleOrCreate(const QString &key, const QVariant &data)
static void sync(ListLayout *src, ListLayout *target)
const Role & getExistingRole(int index) const
Q_REQUIRED_RESULT QVector< std::function< void()> > remove(int index, int count)
static bool sync(ListModel *src, ListModel *target)
int append(QV4::Object *object)
QVariant getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV4::ExecutionEngine *eng)
int elementCount() const
void move(int from, int to, int n)
void insertElement(int index)
ListModel(ListLayout *layout, QQmlListModel *modelCache)
int appendElement()
void insert(int elementIndex, QV4::Object *object)
int setExistingProperty(int uid, const QString &key, const QV4::Value &data, QV4::ExecutionEngine *eng)
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)
int roleCount() const
int setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data)
friend class ListElement
const ListLayout::Role & getExistingRole(int index) const
QQmlListModel * m_model
void propertyWritten(int index) override
static ModelNodeMetaObject * get(QObject *obj)
QMetaObject * toDynamicMetaObject(QObject *object) override
ModelNodeMetaObject(QObject *object, QQmlListModel *model, int elementIndex)
bool beginMoveRows(const QModelIndex &sourceParent, int sourceFirst, int sourceLast, const QModelIndex &destinationParent, int destinationRow)
void endRemoveRows()
Ends a row removal operation.
void endMoveRows()
Ends a row move operation.
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles=QList< int >())
This signal is emitted whenever the data in an existing item changes.
void endInsertRows()
Ends a row insertion operation.
void rowsInserted(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted after rows have been inserted into the model.
QModelIndex createIndex(int row, int column, const void *data=nullptr) const
Creates a model index for the given row and column with the internal pointer ptr.
void beginRemoveRows(const QModelIndex &parent, int first, int last)
Begins a row removal operation.
void beginInsertRows(const QModelIndex &parent, int first, int last)
Begins a row insertion operation.
void rowsRemoved(const QModelIndex &parent, int first, int last, QPrivateSignal)
This signal is emitted after rows have been removed from the model.
QObject * parent() const
Returns a pointer to the parent object.
Definition qobject.h:311
\inmodule QtCore
Definition qatomic.h:112
T fetchAndAddOrdered(T valueToAdd) noexcept
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore\reentrant
Definition qdatetime.h:257
~QDateTime()
Destroys the datetime.
\inmodule QtCore
Definition qhash.h:1093
T & value() const noexcept
Returns a modifiable reference to the current item's value.
Definition qhash.h:1111
\inmodule QtCore
Definition qhash.h:818
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
Definition qhash.h:1258
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1206
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1283
QV4::ExecutionEngine * handle() const
Definition qjsengine.h:292
static QJSValue fromReturnedValue(QV4::ReturnedValue d)
Definition qjsvalue_p.h:189
static QV4::ReturnedValue asReturnedValue(const QJSValue *jsval)
Definition qjsvalue_p.h:249
static QV4::ReturnedValue convertToReturnedValue(QV4::ExecutionEngine *e, const QJSValue &jsval)
Definition qjsvalue_p.h:298
static void setValue(QJSValue *jsval, const QV4::Value &v)
Definition qjsvalue_p.h:282
The QJSValue class acts as a container for Qt/JavaScript data types.
Definition qjsvalue.h:31
bool isCallable() const
Returns true if this QJSValue is a function, otherwise returns false.
Definition qjsvalue.cpp:445
~QJSValue()
Destroys this QJSValue.
Definition qjsvalue.cpp:280
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
const_pointer constData() const noexcept
Definition qlist.h:416
bool isEmpty() const noexcept
Definition qlist.h:390
QList< T > toList() const noexcept
Definition qlist.h:716
qsizetype length() const noexcept
Definition qlist.h:388
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
const_iterator cend() const noexcept
Definition qlist.h:614
void append(parameter_type t)
Definition qlist.h:441
const_iterator cbegin() const noexcept
Definition qlist.h:613
bool isSharedWith(const QMap< Key, T > &other) const noexcept
Definition qmap.h:283
\inmodule QtCore
QDynamicMetaObjectData * metaObject
Definition qobject.h:77
static QObjectPrivate * get(QObject *o)
Definition qobject_p.h:153
QAbstractDeclarativeData * declarativeData
Definition qobject_p.h:209
\inmodule QtCore
Definition qobject.h:90
virtual ~QObject()
Destroys the object, deleting all its child objects.
Definition qobject.cpp:980
void remove(int idx, int count=1)
void insert(int idx, const T &v)
void append(const T &v)
const T & at(int idx) const
int count() const
static QQmlRefPointer< QQmlContextData > get(QQmlContext *context)
const QMetaObject * resolveType(const QString &) const
Resolves name to a type, or 0 if it is not a type.
int evaluateEnum(const QString &, bool *ok) const
If script is a simple enumeration expression (eg.
QV4::WeakValue jsWrapper
Definition qqmldata_p.h:193
@ DoesNotOwnMemory
Definition qqmldata_p.h:58
static QQmlData * get(QObjectPrivate *priv, bool create)
Definition qqmldata_p.h:199
QQmlPropertyCapture * propertyCapture
static QQmlEnginePrivate * get(QQmlEngine *e)
The QQmlEngine class provides an environment for instantiating QML components.
Definition qqmlengine.h:57
static QQmlContext * contextForObject(const QObject *)
Returns the QQmlContext for the object, or nullptr if no context has been set.
static void setContextForObject(QObject *, QQmlContext *)
Sets the QQmlContext for the object to context.
The QQmlError class encapsulates a QML error.
Definition qqmlerror.h:18
void setDescription(const QString &)
Sets the error description.
void verifyBindings(const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, const QList< const QV4::CompiledData::Binding * > &bindings) override
void applyBindings(QObject *obj, const QQmlRefPointer< QV4::ExecutableCompilationUnit > &compilationUnit, const QList< const QV4::CompiledData::Binding * > &bindings) override
friend class ListModel
Q_INVOKABLE void insert(QQmlV4Function *args)
\qmlmethod ListModel::insert(int index, jsobject dict)
Q_INVOKABLE void sync()
\qmlmethod ListModel::sync()
Q_INVOKABLE void clear()
\qmlmethod ListModel::clear()
friend class QQmlListModelWorkerAgent
Q_INVOKABLE void append(QQmlV4Function *args)
\qmlmethod ListModel::append(jsobject dict)
QVariant data(const QModelIndex &index, int role) const override
Returns the data stored under the given role for the item referred to by the index.
void setDynamicRoles(bool enableDynamicRoles)
\qmlproperty bool ListModel::dynamicRoles
QModelIndex index(int row, int column, const QModelIndex &parent) const override
Returns the index of the data in row and column with parent.
Q_INVOKABLE void setProperty(int index, const QString &property, const QVariant &value)
\qmlmethod ListModel::setProperty(int index, string property, variant value)
Q_INVOKABLE QJSValue get(int index) const
\qmlmethod object ListModel::get(int index)
Q_INVOKABLE void move(int from, int to, int count)
\qmlmethod ListModel::move(int from, int to, int n)
int rowCount(const QModelIndex &parent) const override
Returns the number of rows under the given parent.
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole) override
Sets the role data for the item at index to value.
Q_INVOKABLE void remove(QQmlV4Function *args)
\qmlmethod ListModel::remove(int index, int count = 1)
QHash< int, QByteArray > roleNames() const override
void countChanged()
friend class DynamicRoleModelNode
QQmlListModel(QObject *parent=nullptr)
\qmltype ListModel \instantiates QQmlListModel \inqmlmodule QtQml.Models
Q_INVOKABLE void set(int index, const QJSValue &value)
\qmlmethod ListModel::set(int index, jsobject dict)
void createProperties(const QVector< QByteArray > &names)
QQmlOpenMetaObjectType * type() const
bool setValue(const QByteArray &, const QVariant &, bool force=false)
QVariant value(const QByteArray &) const
void captureProperty(QQmlNotifier *)
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
int compare(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.cpp:6498
DataPointer & data_ptr()
Definition qstring.h:986
QByteArray toUtf8() const &
Definition qstring.h:563
\inmodule QtCore
Definition qurl.h:94
~QUrl()
Destructor; called immediately before the object is deleted.
Definition qurl.cpp:1859
QString bindingValueAsString(const CompiledData::Binding *binding) const
ObjectType::Data * allocate(Args &&... args)
Definition qv4mm_p.h:199
ReturnedValue value() const
bool isNullOrUndefined() const
void set(ExecutionEngine *engine, const Value &value)
\inmodule QtCore
Definition qvariant.h:64
T value() const &
Definition qvariant.h:511
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:531
QMetaType metaType() const
QUrl toUrl() const
Returns the variant as a QUrl if the variant has userType() \l QMetaType::QUrl; otherwise returns an ...
#define this
Definition dialogs.cpp:9
QMap< QString, QString > map
[6]
b clear()
QDate date
[1]
qDeleteAll(list.begin(), list.end())
double e
QCache< int, Employee > cache
[0]
QSet< QString >::iterator it
auto mo
[7]
short next
Definition keywords.cpp:445
Combined button and popup list for selecting options.
\qmltype Particle \inqmlmodule QtQuick.Particles
quint64 ReturnedValue
@ Attr_Data
static int arrayLength(const QString &rawType)
Definition provider.cpp:52
static void * context
QMap< QString, QVariant > QVariantMap
static const QCssKnownValue properties[NumProperties - 1]
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
DBusConnection const char DBusError * error
static struct AttrInfo attrs[]
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
static QByteArray stringData(const QMetaObject *mo, int index)
#define Q_DECLARE_METATYPE(TYPE)
Definition qmetatype.h:1504
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
const GLfloat * m
GLuint64 key
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLenum GLsizei dataSize
GLenum GLenum GLsizei count
GLuint object
[3]
GLfloat GLfloat f
GLenum src
GLenum type
GLenum target
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint start
GLboolean GLboolean g
GLuint name
GLfloat n
GLsizei GLenum GLsizei GLsizei GLuint memory
GLenum GLenum GLsizei void GLsizei void * column
GLhandleARB obj
[2]
GLenum func
Definition qopenglext.h:663
const GLubyte * c
GLenum array
GLdouble GLdouble t
Definition qopenglext.h:243
GLenum GLenum GLsizei void * row
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
QQmlEngine * qmlEngine(const QObject *obj)
Definition qqml.cpp:76
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:71
Q_QML_EXPORT QQmlInfo qmlWarning(const QObject *me)
@ MIN_LISTMODEL_UID
static QAtomicInt uidCounter(MIN_LISTMODEL_UID)
static bool isMemoryUsed(const char *mem)
static QString roleTypeName(ListLayout::Role::DataType t)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int(*) void arg)
QLatin1StringView QLatin1String
Definition qstringfwd.h:31
#define QStringLiteral(str)
QArrayDataPointer< char16_t > QStringPrivate
#define tr(X)
static const QTextHtmlElement elements[Html_NumElements]
#define emit
unsigned int quint32
Definition qtypes.h:45
#define Q_ALLOCA_VAR(type, name, size)
Definition qv4alloca_p.h:36
static QVariant toVariant(const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols, V4ObjectSet *visitedObjects)
#define DEFINE_OBJECT_VTABLE(classname)
const char property[13]
Definition qwizard.cpp:101
QSqlQueryModel * model
[16]
QFuture< QSet< QChar > > set
[10]
settings setValue("DataPump/bgcolor", color)
QUrl url("example.com")
[constructor-url-reference]
QVBoxLayout * layout
QSharedPointer< T > other(t)
[5]
QJSValueList args
QJSEngine engine
[0]
Data * d_ptr() noexcept
virtual QMetaObject * toDynamicMetaObject(QObject *)=0
\inmodule QtCore \reentrant
Definition qchar.h:17
qsizetype indexOf(const AT &t, qsizetype from=0) const noexcept
Definition qlist.h:955
\inmodule QtCore
static void deallocate(QArrayData *data) noexcept
Definition qarraydata.h:121
union QV4::CompiledData::Binding::@543 value
MemoryManager * memoryManager
Heap::String * newString(const QString &s=QString())
QV4::ReturnedValue fromVariant(const QVariant &)
static QVariantMap variantMapFromJS(const QV4::Object *o)
static QVariant toVariant(const QV4::Value &value, QMetaType typeHint, bool createJSValueForObjectsAndSymbols=true)
QQmlEngine * qmlEngine() const
Heap::ArrayObject * newArrayObject(int count=0)
static Heap::FunctionObject * createScriptFunction(ExecutionContext *scope, Function *function)
ReturnedValue(* getter)(Lookup *l, ExecutionEngine *engine, const Value &object)
Definition qv4lookup_p.h:36
static ReturnedValue getterFallback(Lookup *l, ExecutionEngine *engine, const Value &object)
ExecutionEngine * engine() const
PropertyKey next(const Object *o, Property *pd=nullptr, PropertyAttributes *attrs=nullptr) override
~ModelObjectOwnPropertyKeyIterator() override=default
V4_NEEDS_DESTROY ListModel * listModel() const
PropertyKey next(const Object *o, Property *pd=nullptr, PropertyAttributes *attrs=nullptr) override
bool hasProperty(PropertyKey id) const
bool arrayPut(uint index, const Value &value)
static ReturnedValue wrap(ExecutionEngine *engine, QObject *object)
QObject * object() const
static Heap::QmlContext * create(QV4::ExecutionContext *parent, QQmlRefPointer< QQmlContextData > context, QObject *scopeObject)
ExecutionEngine * engine
bool isNumber() const
bool isNullOrUndefined() const
bool isBoolean() const
bool booleanValue() const
double asDouble() const
static constexpr VTable::OwnPropertyKeys virtualOwnPropertyKeys
static constexpr VTable::Get virtualGet
static constexpr VTable::Put virtualPut
static constexpr VTable::ResolveLookupGetter virtualResolveLookupGetter
bool isString() const
Definition qv4value_p.h:284
int toInt32() const
Definition qv4value_p.h:350
QML_NEARLY_ALWAYS_INLINE String * stringValue() const
Definition qv4value_p.h:55
static constexpr Value undefinedValue()
Definition qv4value_p.h:191
const T * as() const
Definition qv4value_p.h:132
void setString(const QString &s)
QString toString(const QQmlListModel *owner) const
QString asString() const
void setTranslation(const QV4::CompiledData::Binding *binding)
void wrapper()
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent