1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qqmladaptormodel_p.h"
41
42#include <private/qqmldelegatemodel_p_p.h>
43#include <private/qmetaobjectbuilder_p.h>
44#include <private/qqmlproperty_p.h>
45
46#include <private/qv4value_p.h>
47#include <private/qv4functionobject_p.h>
48
49QT_BEGIN_NAMESPACE
50
51class QQmlAdaptorModelEngineData : public QV4::ExecutionEngine::Deletable
52{
53public:
54 QQmlAdaptorModelEngineData(QV4::ExecutionEngine *v4);
55 ~QQmlAdaptorModelEngineData();
56
57 QV4::ExecutionEngine *v4;
58 QV4::PersistentValue listItemProto;
59};
60
61V4_DEFINE_EXTENSION(QQmlAdaptorModelEngineData, engineData)
62
63static QV4::ReturnedValue get_index(const QV4::FunctionObject *f, const QV4::Value *thisObject, const QV4::Value *, int)
64{
65 QV4::Scope scope(f);
66 QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
67 if (!o)
68 RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")));
69
70 RETURN_RESULT(QV4::Encode(o->d()->item->index));
71}
72
73template <typename T, typename M> static void setModelDataType(QMetaObjectBuilder *builder, M *metaType)
74{
75 builder->setFlags(QMetaObjectBuilder::DynamicMetaObject);
76 builder->setClassName(T::staticMetaObject.className());
77 builder->setSuperClass(&T::staticMetaObject);
78 metaType->propertyOffset = T::staticMetaObject.propertyCount();
79 metaType->signalOffset = T::staticMetaObject.methodCount();
80}
81
82static void addProperty(QMetaObjectBuilder *builder, int propertyId, const QByteArray &propertyName, const QByteArray &propertyType)
83{
84 builder->addSignal(signature: "__" + QByteArray::number(propertyId) + "()");
85 QMetaPropertyBuilder property = builder->addProperty(
86 name: propertyName, type: propertyType, notifierId: propertyId);
87 property.setWritable(true);
88}
89
90class VDMModelDelegateDataType;
91
92class QQmlDMCachedModelData : public QQmlDelegateModelItem
93{
94public:
95 QQmlDMCachedModelData(
96 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
97 VDMModelDelegateDataType *dataType,
98 int index, int row, int column);
99
100 int metaCall(QMetaObject::Call call, int id, void **arguments);
101
102 virtual QVariant value(int role) const = 0;
103 virtual void setValue(int role, const QVariant &value) = 0;
104
105 void setValue(const QString &role, const QVariant &value) override;
106 bool resolveIndex(const QQmlAdaptorModel &model, int idx) override;
107
108 static QV4::ReturnedValue get_property(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
109 static QV4::ReturnedValue set_property(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc);
110
111 VDMModelDelegateDataType *type;
112 QVector<QVariant> cachedData;
113};
114
115class VDMModelDelegateDataType
116 : public QQmlRefCount
117 , public QQmlAdaptorModel::Accessors
118 , public QAbstractDynamicMetaObject
119{
120public:
121 VDMModelDelegateDataType(QQmlAdaptorModel *model)
122 : model(model)
123 , propertyOffset(0)
124 , signalOffset(0)
125 , hasModelData(false)
126 {
127 }
128
129 bool notify(
130 const QQmlAdaptorModel &,
131 const QList<QQmlDelegateModelItem *> &items,
132 int index,
133 int count,
134 const QVector<int> &roles) const override
135 {
136 bool changed = roles.isEmpty() && !watchedRoles.isEmpty();
137 if (!changed && !watchedRoles.isEmpty() && watchedRoleIds.isEmpty()) {
138 QList<int> roleIds;
139 for (const QByteArray &r : watchedRoles) {
140 QHash<QByteArray, int>::const_iterator it = roleNames.find(key: r);
141 if (it != roleNames.end())
142 roleIds << it.value();
143 }
144 const_cast<VDMModelDelegateDataType *>(this)->watchedRoleIds = roleIds;
145 }
146
147 QVector<int> signalIndexes;
148 for (int i = 0; i < roles.count(); ++i) {
149 const int role = roles.at(i);
150 if (!changed && watchedRoleIds.contains(t: role))
151 changed = true;
152
153 int propertyId = propertyRoles.indexOf(t: role);
154 if (propertyId != -1)
155 signalIndexes.append(t: propertyId + signalOffset);
156 }
157 if (roles.isEmpty()) {
158 const int propertyRolesCount = propertyRoles.count();
159 signalIndexes.reserve(size: propertyRolesCount);
160 for (int propertyId = 0; propertyId < propertyRolesCount; ++propertyId)
161 signalIndexes.append(t: propertyId + signalOffset);
162 }
163
164 QVarLengthArray<QQmlGuard<QQmlDelegateModelItem>> guardedItems;
165 for (const auto item : items)
166 guardedItems.append(t: item);
167
168 for (const auto &item : qAsConst(t&: guardedItems)) {
169 if (item.isNull())
170 continue;
171
172 const int idx = item->modelIndex();
173 if (idx >= index && idx < index + count) {
174 for (int i = 0; i < signalIndexes.count(); ++i)
175 QMetaObject::activate(sender: item, signal_index: signalIndexes.at(i), argv: nullptr);
176 }
177 }
178 return changed;
179 }
180
181 void replaceWatchedRoles(
182 QQmlAdaptorModel &,
183 const QList<QByteArray> &oldRoles,
184 const QList<QByteArray> &newRoles) const override
185 {
186 VDMModelDelegateDataType *dataType = const_cast<VDMModelDelegateDataType *>(this);
187
188 dataType->watchedRoleIds.clear();
189 for (const QByteArray &oldRole : oldRoles)
190 dataType->watchedRoles.removeOne(t: oldRole);
191 dataType->watchedRoles += newRoles;
192 }
193
194 static QV4::ReturnedValue get_hasModelChildren(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
195 {
196 QV4::Scope scope(b);
197 QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
198 if (!o)
199 RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")));
200
201 const QQmlAdaptorModel *const model = static_cast<QQmlDMCachedModelData *>(o->d()->item)->type->model;
202 if (o->d()->item->index >= 0) {
203 if (const QAbstractItemModel *const aim = model->aim())
204 RETURN_RESULT(QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex))));
205 }
206 RETURN_RESULT(QV4::Encode(false));
207 }
208
209
210 void initializeConstructor(QQmlAdaptorModelEngineData *const data)
211 {
212 QV4::ExecutionEngine *v4 = data->v4;
213 QV4::Scope scope(v4);
214 QV4::ScopedObject proto(scope, v4->newObject());
215 proto->defineAccessorProperty(QStringLiteral("index"), getter: get_index, setter: nullptr);
216 proto->defineAccessorProperty(QStringLiteral("hasModelChildren"), getter: get_hasModelChildren, setter: nullptr);
217 QV4::ScopedProperty p(scope);
218
219 typedef QHash<QByteArray, int>::const_iterator iterator;
220 for (iterator it = roleNames.constBegin(), end = roleNames.constEnd(); it != end; ++it) {
221 const int propertyId = propertyRoles.indexOf(t: it.value());
222 const QByteArray &propertyName = it.key();
223
224 QV4::ScopedString name(scope, v4->newString(s: QString::fromUtf8(str: propertyName)));
225 QV4::ExecutionContext *global = v4->rootContext();
226 QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(args: global, args: propertyId, args: QQmlDMCachedModelData::get_property));
227 QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(args: global, args: propertyId, args: QQmlDMCachedModelData::set_property));
228 p->setGetter(g);
229 p->setSetter(s);
230 proto->insertMember(s: name, p, attributes: QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable);
231 }
232 prototype.set(engine: v4, value: proto);
233 }
234
235 // QAbstractDynamicMetaObject
236
237 void objectDestroyed(QObject *) override
238 {
239 release();
240 }
241
242 int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) override
243 {
244 return static_cast<QQmlDMCachedModelData *>(object)->metaCall(call, id, arguments);
245 }
246
247 QV4::PersistentValue prototype;
248 QList<int> propertyRoles;
249 QList<int> watchedRoleIds;
250 QList<QByteArray> watchedRoles;
251 QHash<QByteArray, int> roleNames;
252 QQmlAdaptorModel *model;
253 int propertyOffset;
254 int signalOffset;
255 bool hasModelData;
256};
257
258QQmlDMCachedModelData::QQmlDMCachedModelData(
259 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
260 VDMModelDelegateDataType *dataType, int index, int row, int column)
261 : QQmlDelegateModelItem(metaType, dataType, index, row, column)
262 , type(dataType)
263{
264 if (index == -1)
265 cachedData.resize(size: type->hasModelData ? 1 : type->propertyRoles.count());
266
267 QObjectPrivate::get(o: this)->metaObject = type;
268
269 type->addref();
270}
271
272int QQmlDMCachedModelData::metaCall(QMetaObject::Call call, int id, void **arguments)
273{
274 if (call == QMetaObject::ReadProperty && id >= type->propertyOffset) {
275 const int propertyIndex = id - type->propertyOffset;
276 if (index == -1) {
277 if (!cachedData.isEmpty()) {
278 *static_cast<QVariant *>(arguments[0]) = cachedData.at(
279 i: type->hasModelData ? 0 : propertyIndex);
280 }
281 } else if (*type->model) {
282 *static_cast<QVariant *>(arguments[0]) = value(role: type->propertyRoles.at(i: propertyIndex));
283 }
284 return -1;
285 } else if (call == QMetaObject::WriteProperty && id >= type->propertyOffset) {
286 const int propertyIndex = id - type->propertyOffset;
287 if (index == -1) {
288 const QMetaObject *meta = metaObject();
289 if (cachedData.count() > 1) {
290 cachedData[propertyIndex] = *static_cast<QVariant *>(arguments[0]);
291 QMetaObject::activate(sender: this, meta, local_signal_index: propertyIndex, argv: nullptr);
292 } else if (cachedData.count() == 1) {
293 cachedData[0] = *static_cast<QVariant *>(arguments[0]);
294 QMetaObject::activate(sender: this, meta, local_signal_index: 0, argv: nullptr);
295 QMetaObject::activate(sender: this, meta, local_signal_index: 1, argv: nullptr);
296 }
297 } else if (*type->model) {
298 setValue(role: type->propertyRoles.at(i: propertyIndex), value: *static_cast<QVariant *>(arguments[0]));
299 }
300 return -1;
301 } else {
302 return qt_metacall(call, id, arguments);
303 }
304}
305
306void QQmlDMCachedModelData::setValue(const QString &role, const QVariant &value)
307{
308 QHash<QByteArray, int>::iterator it = type->roleNames.find(key: role.toUtf8());
309 if (it != type->roleNames.end()) {
310 for (int i = 0; i < type->propertyRoles.count(); ++i) {
311 if (type->propertyRoles.at(i) == *it) {
312 cachedData[i] = value;
313 return;
314 }
315 }
316 }
317}
318
319bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &adaptorModel, int idx)
320{
321 if (index == -1) {
322 Q_ASSERT(idx >= 0);
323 cachedData.clear();
324 setModelIndex(idx, newRow: adaptorModel.rowAt(index: idx), newColumn: adaptorModel.columnAt(index: idx));
325 const QMetaObject *meta = metaObject();
326 const int propertyCount = type->propertyRoles.count();
327 for (int i = 0; i < propertyCount; ++i)
328 QMetaObject::activate(sender: this, meta, local_signal_index: i, argv: nullptr);
329 return true;
330 } else {
331 return false;
332 }
333}
334
335QV4::ReturnedValue QQmlDMCachedModelData::get_property(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
336{
337 QV4::Scope scope(b);
338 QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
339 if (!o)
340 return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
341
342 uint propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index;
343
344 QQmlDMCachedModelData *modelData = static_cast<QQmlDMCachedModelData *>(o->d()->item);
345 if (o->d()->item->index == -1) {
346 if (!modelData->cachedData.isEmpty()) {
347 return scope.engine->fromVariant(
348 modelData->cachedData.at(i: modelData->type->hasModelData ? 0 : propertyId));
349 }
350 } else if (*modelData->type->model) {
351 return scope.engine->fromVariant(
352 modelData->value(role: modelData->type->propertyRoles.at(i: propertyId)));
353 }
354 return QV4::Encode::undefined();
355}
356
357QV4::ReturnedValue QQmlDMCachedModelData::set_property(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
358{
359 QV4::Scope scope(b);
360 QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
361 if (!o)
362 return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
363 if (!argc)
364 return scope.engine->throwTypeError();
365
366 uint propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index;
367
368 if (o->d()->item->index == -1) {
369 QQmlDMCachedModelData *modelData = static_cast<QQmlDMCachedModelData *>(o->d()->item);
370 if (!modelData->cachedData.isEmpty()) {
371 if (modelData->cachedData.count() > 1) {
372 modelData->cachedData[propertyId] = scope.engine->toVariant(value: argv[0], typeHint: QMetaType::UnknownType);
373 QMetaObject::activate(sender: o->d()->item, o->d()->item->metaObject(), local_signal_index: propertyId, argv: nullptr);
374 } else if (modelData->cachedData.count() == 1) {
375 modelData->cachedData[0] = scope.engine->toVariant(value: argv[0], typeHint: QMetaType::UnknownType);
376 QMetaObject::activate(sender: o->d()->item, o->d()->item->metaObject(), local_signal_index: 0, argv: nullptr);
377 QMetaObject::activate(sender: o->d()->item, o->d()->item->metaObject(), local_signal_index: 1, argv: nullptr);
378 }
379 }
380 }
381 return QV4::Encode::undefined();
382}
383
384//-----------------------------------------------------------------
385// QAbstractItemModel
386//-----------------------------------------------------------------
387
388class QQmlDMAbstractItemModelData : public QQmlDMCachedModelData
389{
390 Q_OBJECT
391 Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT)
392
393public:
394 QQmlDMAbstractItemModelData(
395 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
396 VDMModelDelegateDataType *dataType,
397 int index, int row, int column)
398 : QQmlDMCachedModelData(metaType, dataType, index, row, column)
399 {
400 }
401
402 bool hasModelChildren() const
403 {
404 if (index >= 0) {
405 if (const QAbstractItemModel *const model = type->model->aim())
406 return model->hasChildren(parent: model->index(row, column, parent: type->model->rootIndex));
407 }
408 return false;
409 }
410
411 QVariant value(int role) const override
412 {
413 if (const QAbstractItemModel *aim = type->model->aim())
414 return aim->index(row, column, parent: type->model->rootIndex).data(arole: role);
415 return QVariant();
416 }
417
418 void setValue(int role, const QVariant &value) override
419 {
420 if (QAbstractItemModel *aim = type->model->aim())
421 aim->setData(index: aim->index(row, column, parent: type->model->rootIndex), value, role);
422 }
423
424 QV4::ReturnedValue get() override
425 {
426 if (type->prototype.isUndefined()) {
427 QQmlAdaptorModelEngineData * const data = engineData(engine: v4);
428 type->initializeConstructor(data);
429 }
430 QV4::Scope scope(v4);
431 QV4::ScopedObject proto(scope, type->prototype.value());
432 QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocate<QQmlDelegateModelItemObject>(args: this));
433 o->setPrototypeOf(proto);
434 ++scriptRef;
435 return o.asReturnedValue();
436 }
437};
438
439class VDMAbstractItemModelDataType : public VDMModelDelegateDataType
440{
441public:
442 VDMAbstractItemModelDataType(QQmlAdaptorModel *model)
443 : VDMModelDelegateDataType(model)
444 {
445 }
446
447 int rowCount(const QQmlAdaptorModel &model) const override
448 {
449 if (const QAbstractItemModel *aim = model.aim())
450 return aim->rowCount(parent: model.rootIndex);
451 return 0;
452 }
453
454 int columnCount(const QQmlAdaptorModel &model) const override
455 {
456 if (const QAbstractItemModel *aim = model.aim())
457 return aim->columnCount(parent: model.rootIndex);
458 return 0;
459 }
460
461 void cleanup(QQmlAdaptorModel &) const override
462 {
463 release();
464 }
465
466 QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
467 {
468 if (!metaObject) {
469 VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this);
470 dataType->initializeMetaType(model);
471 }
472
473 if (const QAbstractItemModel *aim = model.aim()) {
474 QHash<QByteArray, int>::const_iterator it = roleNames.find(key: role.toUtf8());
475 if (it != roleNames.end()) {
476 return aim->index(row: model.rowAt(index), column: model.columnAt(index),
477 parent: model.rootIndex).data(arole: *it);
478 } else if (role == QLatin1String("hasModelChildren")) {
479 return QVariant(aim->hasChildren(parent: aim->index(row: model.rowAt(index),
480 column: model.columnAt(index),
481 parent: model.rootIndex)));
482 }
483 }
484 return QVariant();
485 }
486
487 QVariant parentModelIndex(const QQmlAdaptorModel &model) const override
488 {
489 if (const QAbstractItemModel *aim = model.aim())
490 return QVariant::fromValue(value: aim->parent(child: model.rootIndex));
491 return QVariant();
492 }
493
494 QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override
495 {
496 if (const QAbstractItemModel *aim = model.aim())
497 return QVariant::fromValue(value: aim->index(row: model.rowAt(index), column: model.columnAt(index),
498 parent: model.rootIndex));
499 return QVariant();
500 }
501
502 bool canFetchMore(const QQmlAdaptorModel &model) const override
503 {
504 if (const QAbstractItemModel *aim = model.aim())
505 return aim->canFetchMore(parent: model.rootIndex);
506 return false;
507 }
508
509 void fetchMore(QQmlAdaptorModel &model) const override
510 {
511 if (QAbstractItemModel *aim = model.aim())
512 aim->fetchMore(parent: model.rootIndex);
513 }
514
515 QQmlDelegateModelItem *createItem(
516 QQmlAdaptorModel &model,
517 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
518 int index, int row, int column) const override
519 {
520 VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this);
521 if (!metaObject)
522 dataType->initializeMetaType(model);
523 return new QQmlDMAbstractItemModelData(metaType, dataType, index, row, column);
524 }
525
526 void initializeMetaType(const QQmlAdaptorModel &model)
527 {
528 QMetaObjectBuilder builder;
529 setModelDataType<QQmlDMAbstractItemModelData>(builder: &builder, metaType: this);
530
531 const QByteArray propertyType = QByteArrayLiteral("QVariant");
532 const QAbstractItemModel *aim = model.aim();
533 const QHash<int, QByteArray> names = aim ? aim->roleNames() : QHash<int, QByteArray>();
534 for (QHash<int, QByteArray>::const_iterator it = names.begin(), cend = names.end(); it != cend; ++it) {
535 const int propertyId = propertyRoles.count();
536 propertyRoles.append(t: it.key());
537 roleNames.insert(key: it.value(), value: it.key());
538 addProperty(builder: &builder, propertyId, propertyName: it.value(), propertyType);
539 }
540 if (propertyRoles.count() == 1) {
541 hasModelData = true;
542 const int role = names.begin().key();
543 const QByteArray propertyName = QByteArrayLiteral("modelData");
544
545 propertyRoles.append(t: role);
546 roleNames.insert(key: propertyName, value: role);
547 addProperty(builder: &builder, propertyId: 1, propertyName, propertyType);
548 }
549
550 metaObject.reset(other: builder.toMetaObject());
551 *static_cast<QMetaObject *>(this) = *metaObject;
552 propertyCache.adopt(new QQmlPropertyCache(metaObject.data(), model.modelItemRevision));
553 }
554};
555
556//-----------------------------------------------------------------
557// QQmlListAccessor
558//-----------------------------------------------------------------
559
560class QQmlDMListAccessorData : public QQmlDelegateModelItem
561{
562 Q_OBJECT
563 Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
564public:
565 QQmlDMListAccessorData(const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
566 QQmlAdaptorModel::Accessors *accessor,
567 int index, int row, int column, const QVariant &value)
568 : QQmlDelegateModelItem(metaType, accessor, index, row, column)
569 , cachedData(value)
570 {
571 }
572
573 QVariant modelData() const
574 {
575 return cachedData;
576 }
577
578 void setModelData(const QVariant &data)
579 {
580 if (data == cachedData)
581 return;
582
583 cachedData = data;
584 emit modelDataChanged();
585 }
586
587 static QV4::ReturnedValue get_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
588 {
589 QV4::ExecutionEngine *v4 = b->engine();
590 const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>();
591 if (!o)
592 return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
593
594 return v4->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData);
595 }
596
597 static QV4::ReturnedValue set_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
598 {
599 QV4::ExecutionEngine *v4 = b->engine();
600 const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>();
601 if (!o)
602 return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
603 if (!argc)
604 return v4->throwTypeError();
605
606 static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData(v4->toVariant(value: argv[0], typeHint: QMetaType::UnknownType));
607 return QV4::Encode::undefined();
608 }
609
610 QV4::ReturnedValue get() override
611 {
612 QQmlAdaptorModelEngineData *data = engineData(engine: v4);
613 QV4::Scope scope(v4);
614 QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(args: this));
615 QV4::ScopedObject p(scope, data->listItemProto.value());
616 o->setPrototypeOf(p);
617 ++scriptRef;
618 return o.asReturnedValue();
619 }
620
621 void setValue(const QString &role, const QVariant &value) override
622 {
623 if (role == QLatin1String("modelData"))
624 cachedData = value;
625 }
626
627 bool resolveIndex(const QQmlAdaptorModel &model, int idx) override
628 {
629 if (index == -1) {
630 index = idx;
631 cachedData = model.list.at(idx);
632 emit modelIndexChanged();
633 emit modelDataChanged();
634 return true;
635 } else {
636 return false;
637 }
638 }
639
640
641Q_SIGNALS:
642 void modelDataChanged();
643
644private:
645 QVariant cachedData;
646};
647
648
649class VDMListDelegateDataType : public QQmlRefCount, public QQmlAdaptorModel::Accessors
650{
651public:
652 VDMListDelegateDataType()
653 : QQmlRefCount()
654 , QQmlAdaptorModel::Accessors()
655 {}
656
657 void cleanup(QQmlAdaptorModel &) const override
658 {
659 const_cast<VDMListDelegateDataType *>(this)->release();
660 }
661
662 int rowCount(const QQmlAdaptorModel &model) const override
663 {
664 return model.list.count();
665 }
666
667 int columnCount(const QQmlAdaptorModel &) const override
668 {
669 return 1;
670 }
671
672 QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
673 {
674 return role == QLatin1String("modelData")
675 ? model.list.at(index)
676 : QVariant();
677 }
678
679 QQmlDelegateModelItem *createItem(
680 QQmlAdaptorModel &model,
681 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
682 int index, int row, int column) const override
683 {
684 VDMListDelegateDataType *dataType = const_cast<VDMListDelegateDataType *>(this);
685 if (!propertyCache) {
686 dataType->propertyCache.adopt(new QQmlPropertyCache(
687 &QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision));
688 }
689
690 return new QQmlDMListAccessorData(
691 metaType,
692 dataType,
693 index, row, column,
694 index >= 0 && index < model.list.count() ? model.list.at(index) : QVariant());
695 }
696
697 bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override
698 {
699 for (auto modelItem : items) {
700 const int modelItemIndex = modelItem->index;
701 if (modelItemIndex < index || modelItemIndex >= index + count)
702 continue;
703
704 auto listModelItem = static_cast<QQmlDMListAccessorData *>(modelItem);
705 QVariant updatedModelData = model.list.at(listModelItem->index);
706 listModelItem->setModelData(updatedModelData);
707 }
708 return true;
709 }
710};
711
712//-----------------------------------------------------------------
713// QObject
714//-----------------------------------------------------------------
715
716class VDMObjectDelegateDataType;
717class QQmlDMObjectData : public QQmlDelegateModelItem, public QQmlAdaptorModelProxyInterface
718{
719 Q_OBJECT
720 Q_PROPERTY(QObject *modelData READ modelData NOTIFY modelDataChanged)
721 Q_INTERFACES(QQmlAdaptorModelProxyInterface)
722public:
723 QQmlDMObjectData(
724 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
725 VDMObjectDelegateDataType *dataType,
726 int index, int row, int column,
727 QObject *object);
728
729 void setModelData(QObject *modelData)
730 {
731 if (modelData == object)
732 return;
733
734 object = modelData;
735 emit modelDataChanged();
736 }
737
738 QObject *modelData() const { return object; }
739 QObject *proxiedObject() override { return object; }
740
741 QPointer<QObject> object;
742
743Q_SIGNALS:
744 void modelDataChanged();
745};
746
747class VDMObjectDelegateDataType : public QQmlRefCount, public QQmlAdaptorModel::Accessors
748{
749public:
750 int propertyOffset;
751 int signalOffset;
752 bool shared;
753 QMetaObjectBuilder builder;
754
755 VDMObjectDelegateDataType()
756 : propertyOffset(0)
757 , signalOffset(0)
758 , shared(true)
759 {
760 }
761
762 VDMObjectDelegateDataType(const VDMObjectDelegateDataType &type)
763 : QQmlRefCount()
764 , QQmlAdaptorModel::Accessors()
765 , propertyOffset(type.propertyOffset)
766 , signalOffset(type.signalOffset)
767 , shared(false)
768 , builder(type.metaObject.data(), QMetaObjectBuilder::Properties
769 | QMetaObjectBuilder::Signals
770 | QMetaObjectBuilder::SuperClass
771 | QMetaObjectBuilder::ClassName)
772 {
773 builder.setFlags(QMetaObjectBuilder::DynamicMetaObject);
774 }
775
776 int rowCount(const QQmlAdaptorModel &model) const override
777 {
778 return model.list.count();
779 }
780
781 int columnCount(const QQmlAdaptorModel &) const override
782 {
783 return 1;
784 }
785
786 QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
787 {
788 if (QObject *object = model.list.at(index).value<QObject *>())
789 return object->property(name: role.toUtf8());
790 return QVariant();
791 }
792
793 QQmlDelegateModelItem *createItem(
794 QQmlAdaptorModel &model,
795 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
796 int index, int row, int column) const override
797 {
798 VDMObjectDelegateDataType *dataType = const_cast<VDMObjectDelegateDataType *>(this);
799 if (!metaObject)
800 dataType->initializeMetaType(model);
801 return index >= 0 && index < model.list.count()
802 ? new QQmlDMObjectData(metaType, dataType, index, row, column, qvariant_cast<QObject *>(v: model.list.at(index)))
803 : nullptr;
804 }
805
806 void initializeMetaType(QQmlAdaptorModel &model)
807 {
808 Q_UNUSED(model);
809 setModelDataType<QQmlDMObjectData>(builder: &builder, metaType: this);
810
811 metaObject.reset(other: builder.toMetaObject());
812 // Note: ATM we cannot create a shared property cache for this class, since each model
813 // object can have different properties. And to make those properties available to the
814 // delegate, QQmlDMObjectData makes use of a QAbstractDynamicMetaObject subclass
815 // (QQmlDMObjectDataMetaObject), which we cannot represent in a QQmlPropertyCache.
816 // By not having a shared property cache, revisioned properties in QQmlDelegateModelItem
817 // will always be available to the delegate, regardless of the import version.
818 }
819
820 void cleanup(QQmlAdaptorModel &) const override
821 {
822 const_cast<VDMObjectDelegateDataType *>(this)->release();
823 }
824
825 bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override
826 {
827 for (auto modelItem : items) {
828 const int modelItemIndex = modelItem->index;
829 if (modelItemIndex < index || modelItemIndex >= index + count)
830 continue;
831
832 auto objectModelItem = static_cast<QQmlDMObjectData *>(modelItem);
833 QObject *updatedModelData = qvariant_cast<QObject *>(v: model.list.at(objectModelItem->index));
834 objectModelItem->setModelData(updatedModelData);
835 }
836 return true;
837 }
838};
839
840class QQmlDMObjectDataMetaObject : public QAbstractDynamicMetaObject
841{
842public:
843 QQmlDMObjectDataMetaObject(QQmlDMObjectData *data, VDMObjectDelegateDataType *type)
844 : m_data(data)
845 , m_type(type)
846 {
847 QObjectPrivate *op = QObjectPrivate::get(o: m_data);
848 *static_cast<QMetaObject *>(this) = *type->metaObject;
849 op->metaObject = this;
850 m_type->addref();
851 }
852
853 ~QQmlDMObjectDataMetaObject()
854 {
855 m_type->release();
856 }
857
858 int metaCall(QObject *o, QMetaObject::Call call, int id, void **arguments) override
859 {
860 Q_ASSERT(o == m_data);
861 Q_UNUSED(o);
862
863 static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount();
864 if (id >= m_type->propertyOffset
865 && (call == QMetaObject::ReadProperty
866 || call == QMetaObject::WriteProperty
867 || call == QMetaObject::ResetProperty)) {
868 if (m_data->object)
869 QMetaObject::metacall(m_data->object, call, id - m_type->propertyOffset + objectPropertyOffset, arguments);
870 return -1;
871 } else if (id >= m_type->signalOffset && call == QMetaObject::InvokeMetaMethod) {
872 QMetaObject::activate(sender: m_data, this, local_signal_index: id - m_type->signalOffset, argv: nullptr);
873 return -1;
874 } else {
875 return m_data->qt_metacall(call, id, arguments);
876 }
877 }
878
879 int createProperty(const char *name, const char *) override
880 {
881 if (!m_data->object)
882 return -1;
883 const QMetaObject *metaObject = m_data->object->metaObject();
884 static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount();
885
886 const int previousPropertyCount = propertyCount() - propertyOffset();
887 int propertyIndex = metaObject->indexOfProperty(name);
888 if (propertyIndex == -1)
889 return -1;
890 if (previousPropertyCount + objectPropertyOffset == metaObject->propertyCount())
891 return propertyIndex + m_type->propertyOffset - objectPropertyOffset;
892
893 if (m_type->shared) {
894 VDMObjectDelegateDataType *type = m_type;
895 m_type = new VDMObjectDelegateDataType(*m_type);
896 type->release();
897 }
898
899 const int previousMethodCount = methodCount();
900 int notifierId = previousMethodCount - methodOffset();
901 for (int propertyId = previousPropertyCount; propertyId < metaObject->propertyCount() - objectPropertyOffset; ++propertyId) {
902 QMetaProperty property = metaObject->property(index: propertyId + objectPropertyOffset);
903 QMetaPropertyBuilder propertyBuilder;
904 if (property.hasNotifySignal()) {
905 m_type->builder.addSignal(signature: "__" + QByteArray::number(propertyId) + "()");
906 propertyBuilder = m_type->builder.addProperty(name: property.name(), type: property.typeName(), notifierId);
907 ++notifierId;
908 } else {
909 propertyBuilder = m_type->builder.addProperty(name: property.name(), type: property.typeName());
910 }
911 propertyBuilder.setWritable(property.isWritable());
912 propertyBuilder.setResettable(property.isResettable());
913 propertyBuilder.setConstant(property.isConstant());
914 }
915
916 m_type->metaObject.reset(other: m_type->builder.toMetaObject());
917 *static_cast<QMetaObject *>(this) = *m_type->metaObject;
918
919 notifierId = previousMethodCount;
920 for (int i = previousPropertyCount; i < metaObject->propertyCount() - objectPropertyOffset; ++i) {
921 QMetaProperty property = metaObject->property(index: i + objectPropertyOffset);
922 if (property.hasNotifySignal()) {
923 QQmlPropertyPrivate::connect(
924 sender: m_data->object, signal_index: property.notifySignalIndex(), receiver: m_data, method_index: notifierId);
925 ++notifierId;
926 }
927 }
928 return propertyIndex + m_type->propertyOffset - objectPropertyOffset;
929 }
930
931 QQmlDMObjectData *m_data;
932 VDMObjectDelegateDataType *m_type;
933};
934
935QQmlDMObjectData::QQmlDMObjectData(const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
936 VDMObjectDelegateDataType *dataType,
937 int index, int row, int column,
938 QObject *object)
939 : QQmlDelegateModelItem(metaType, dataType, index, row, column)
940 , object(object)
941{
942 new QQmlDMObjectDataMetaObject(this, dataType);
943}
944
945//-----------------------------------------------------------------
946// QQmlAdaptorModel
947//-----------------------------------------------------------------
948
949static const QQmlAdaptorModel::Accessors qt_vdm_null_accessors;
950
951QQmlAdaptorModel::Accessors::~Accessors()
952{
953}
954
955QQmlAdaptorModel::QQmlAdaptorModel()
956 : accessors(&qt_vdm_null_accessors)
957{
958}
959
960QQmlAdaptorModel::~QQmlAdaptorModel()
961{
962 accessors->cleanup(*this);
963}
964
965void QQmlAdaptorModel::setModel(const QVariant &variant, QObject *, QQmlEngine *engine)
966{
967 accessors->cleanup(*this);
968
969 list.setList(variant, engine);
970 modelStrongReference.clear();
971
972 if (QObject *object = qvariant_cast<QObject *>(v: list.list())) {
973 if (QQmlData *ddata = QQmlData::get(object))
974 modelStrongReference = ddata->jsWrapper;
975 setObject(object);
976 if (qobject_cast<QAbstractItemModel *>(object))
977 accessors = new VDMAbstractItemModelDataType(this);
978 else
979 accessors = new VDMObjectDelegateDataType;
980 } else if (list.type() == QQmlListAccessor::ListProperty) {
981 auto object = static_cast<const QQmlListReference *>(variant.constData())->object();
982 if (QQmlData *ddata = QQmlData::get(object))
983 modelStrongReference = ddata->jsWrapper;
984 setObject(object);
985 accessors = new VDMObjectDelegateDataType;
986 } else if (list.type() == QQmlListAccessor::ObjectList) {
987 setObject(nullptr);
988 accessors = new VDMObjectDelegateDataType;
989 } else if (list.type() != QQmlListAccessor::Invalid
990 && list.type() != QQmlListAccessor::Instance) { // Null QObject
991 setObject(nullptr);
992 accessors = new VDMListDelegateDataType;
993 } else {
994 setObject(nullptr);
995 accessors = &qt_vdm_null_accessors;
996 }
997}
998
999void QQmlAdaptorModel::invalidateModel()
1000{
1001 accessors->cleanup(*this);
1002 accessors = &qt_vdm_null_accessors;
1003 // Don't clear the model object as we still need the guard to clear the list variant if the
1004 // object is destroyed.
1005}
1006
1007bool QQmlAdaptorModel::isValid() const
1008{
1009 return accessors != &qt_vdm_null_accessors;
1010}
1011
1012int QQmlAdaptorModel::count() const
1013{
1014 return rowCount() * columnCount();
1015}
1016
1017int QQmlAdaptorModel::rowCount() const
1018{
1019 return qMax(a: 0, b: accessors->rowCount(*this));
1020}
1021
1022int QQmlAdaptorModel::columnCount() const
1023{
1024 return qMax(a: 0, b: accessors->columnCount(*this));
1025}
1026
1027int QQmlAdaptorModel::rowAt(int index) const
1028{
1029 int count = rowCount();
1030 return count <= 0 ? -1 : index % count;
1031}
1032
1033int QQmlAdaptorModel::columnAt(int index) const
1034{
1035 int count = rowCount();
1036 return count <= 0 ? -1 : index / count;
1037}
1038
1039int QQmlAdaptorModel::indexAt(int row, int column) const
1040{
1041 return column * rowCount() + row;
1042}
1043
1044void QQmlAdaptorModel::useImportVersion(int minorVersion)
1045{
1046 modelItemRevision = minorVersion;
1047}
1048
1049void QQmlAdaptorModel::objectDestroyed(QObject *)
1050{
1051 setModel(variant: QVariant(), nullptr, engine: nullptr);
1052}
1053
1054QQmlAdaptorModelEngineData::QQmlAdaptorModelEngineData(QV4::ExecutionEngine *v4)
1055 : v4(v4)
1056{
1057 QV4::Scope scope(v4);
1058 QV4::ScopedObject proto(scope, v4->newObject());
1059 proto->defineAccessorProperty(QStringLiteral("index"), getter: get_index, setter: nullptr);
1060 proto->defineAccessorProperty(QStringLiteral("modelData"),
1061 getter: QQmlDMListAccessorData::get_modelData, setter: QQmlDMListAccessorData::set_modelData);
1062 listItemProto.set(engine: v4, value: proto);
1063}
1064
1065QQmlAdaptorModelEngineData::~QQmlAdaptorModelEngineData()
1066{
1067}
1068
1069QT_END_NAMESPACE
1070
1071#include <qqmladaptormodel.moc>
1072

source code of qtdeclarative/src/qmlmodels/qqmladaptormodel.cpp