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