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