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