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 
49 QT_BEGIN_NAMESPACE
50 
51 class QQmlAdaptorModelEngineData : public QV4::ExecutionEngine::Deletable
52 {
53 public:
54     QQmlAdaptorModelEngineData(QV4::ExecutionEngine *v4);
55     ~QQmlAdaptorModelEngineData();
56 
57     QV4::ExecutionEngine *v4;
58     QV4::PersistentValue listItemProto;
59 };
60 
V4_DEFINE_EXTENSION(QQmlAdaptorModelEngineData,engineData)61 V4_DEFINE_EXTENSION(QQmlAdaptorModelEngineData, engineData)
62 
63 static 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 
setModelDataType(QMetaObjectBuilder * builder,M * metaType)73 template <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 
addProperty(QMetaObjectBuilder * builder,int propertyId,const QByteArray & propertyName,const QByteArray & propertyType)82 static void addProperty(QMetaObjectBuilder *builder, int propertyId, const QByteArray &propertyName, const QByteArray &propertyType)
83 {
84     builder->addSignal("__" + QByteArray::number(propertyId) + "()");
85     QMetaPropertyBuilder property = builder->addProperty(
86             propertyName, propertyType, propertyId);
87     property.setWritable(true);
88 }
89 
90 class VDMModelDelegateDataType;
91 
92 class QQmlDMCachedModelData : public QQmlDelegateModelItem
93 {
94 public:
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 
115 class VDMModelDelegateDataType
116         : public QQmlRefCount
117         , public QQmlAdaptorModel::Accessors
118         , public QAbstractDynamicMetaObject
119 {
120 public:
VDMModelDelegateDataType(QQmlAdaptorModel * model)121     VDMModelDelegateDataType(QQmlAdaptorModel *model)
122         : model(model)
123         , propertyOffset(0)
124         , signalOffset(0)
125         , hasModelData(false)
126     {
127     }
128 
notify(const QQmlAdaptorModel &,const QList<QQmlDelegateModelItem * > & items,int index,int count,const QVector<int> & roles) const129     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(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(role))
151                 changed = true;
152 
153             int propertyId = propertyRoles.indexOf(role);
154             if (propertyId != -1)
155                 signalIndexes.append(propertyId + signalOffset);
156         }
157         if (roles.isEmpty()) {
158             const int propertyRolesCount = propertyRoles.count();
159             signalIndexes.reserve(propertyRolesCount);
160             for (int propertyId = 0; propertyId < propertyRolesCount; ++propertyId)
161                 signalIndexes.append(propertyId + signalOffset);
162         }
163 
164         QVarLengthArray<QQmlGuard<QQmlDelegateModelItem>> guardedItems;
165         for (const auto item : items)
166             guardedItems.append(item);
167 
168         for (const auto &item : qAsConst(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(item, signalIndexes.at(i), nullptr);
176             }
177         }
178         return changed;
179     }
180 
replaceWatchedRoles(QQmlAdaptorModel &,const QList<QByteArray> & oldRoles,const QList<QByteArray> & newRoles) const181     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(oldRole);
191         dataType->watchedRoles += newRoles;
192     }
193 
get_hasModelChildren(const QV4::FunctionObject * b,const QV4::Value * thisObject,const QV4::Value *,int)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 
initializeConstructor(QQmlAdaptorModelEngineData * const data)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"), get_index, nullptr);
216         proto->defineAccessorProperty(QStringLiteral("hasModelChildren"), get_hasModelChildren, 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(it.value());
222             const QByteArray &propertyName = it.key();
223 
224             QV4::ScopedString name(scope, v4->newString(QString::fromUtf8(propertyName)));
225             QV4::ExecutionContext *global = v4->rootContext();
226             QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::get_property));
227             QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::set_property));
228             p->setGetter(g);
229             p->setSetter(s);
230             proto->insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable);
231         }
232         prototype.set(v4, proto);
233     }
234 
235     // QAbstractDynamicMetaObject
236 
objectDestroyed(QObject *)237     void objectDestroyed(QObject *) override
238     {
239         release();
240     }
241 
metaCall(QObject * object,QMetaObject::Call call,int id,void ** arguments)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 
QQmlDMCachedModelData(const QQmlRefPointer<QQmlDelegateModelItemMetaType> & metaType,VDMModelDelegateDataType * dataType,int index,int row,int column)258 QQmlDMCachedModelData::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(type->hasModelData ? 1 : type->propertyRoles.count());
266 
267     QObjectPrivate::get(this)->metaObject = type;
268 
269     type->addref();
270 }
271 
metaCall(QMetaObject::Call call,int id,void ** arguments)272 int 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                     type->hasModelData ? 0 : propertyIndex);
280             }
281         } else  if (*type->model) {
282             *static_cast<QVariant *>(arguments[0]) = value(type->propertyRoles.at(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(this, meta, propertyIndex, nullptr);
292             } else if (cachedData.count() == 1) {
293                 cachedData[0] = *static_cast<QVariant *>(arguments[0]);
294                 QMetaObject::activate(this, meta, 0, nullptr);
295                 QMetaObject::activate(this, meta, 1, nullptr);
296             }
297         } else if (*type->model) {
298             setValue(type->propertyRoles.at(propertyIndex), *static_cast<QVariant *>(arguments[0]));
299         }
300         return -1;
301     } else {
302         return qt_metacall(call, id, arguments);
303     }
304 }
305 
setValue(const QString & role,const QVariant & value)306 void QQmlDMCachedModelData::setValue(const QString &role, const QVariant &value)
307 {
308     QHash<QByteArray, int>::iterator it = type->roleNames.find(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 
resolveIndex(const QQmlAdaptorModel & adaptorModel,int idx)319 bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &adaptorModel, int idx)
320 {
321     if (index == -1) {
322         Q_ASSERT(idx >= 0);
323         cachedData.clear();
324         setModelIndex(idx, adaptorModel.rowAt(idx), adaptorModel.columnAt(idx));
325         const QMetaObject *meta = metaObject();
326         const int propertyCount = type->propertyRoles.count();
327         for (int i = 0; i < propertyCount; ++i)
328             QMetaObject::activate(this, meta, i, nullptr);
329         return true;
330     } else {
331         return false;
332     }
333 }
334 
get_property(const QV4::FunctionObject * b,const QV4::Value * thisObject,const QV4::Value *,int)335 QV4::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(modelData->type->hasModelData ? 0 : propertyId));
349         }
350     } else if (*modelData->type->model) {
351         return scope.engine->fromVariant(
352                 modelData->value(modelData->type->propertyRoles.at(propertyId)));
353     }
354     return QV4::Encode::undefined();
355 }
356 
set_property(const QV4::FunctionObject * b,const QV4::Value * thisObject,const QV4::Value * argv,int argc)357 QV4::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(argv[0], QMetaType::UnknownType);
373                 QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), propertyId, nullptr);
374             } else if (modelData->cachedData.count() == 1) {
375                 modelData->cachedData[0] = scope.engine->toVariant(argv[0], QMetaType::UnknownType);
376                 QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), 0, nullptr);
377                 QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), 1, nullptr);
378             }
379         }
380     }
381     return QV4::Encode::undefined();
382 }
383 
384 //-----------------------------------------------------------------
385 // QAbstractItemModel
386 //-----------------------------------------------------------------
387 
388 class QQmlDMAbstractItemModelData : public QQmlDMCachedModelData
389 {
390     Q_OBJECT
391     Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT)
392 
393 public:
QQmlDMAbstractItemModelData(const QQmlRefPointer<QQmlDelegateModelItemMetaType> & metaType,VDMModelDelegateDataType * dataType,int index,int row,int column)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 
hasModelChildren() const402     bool hasModelChildren() const
403     {
404         if (index >= 0) {
405             if (const QAbstractItemModel *const model = type->model->aim())
406                 return model->hasChildren(model->index(row, column, type->model->rootIndex));
407         }
408         return false;
409     }
410 
value(int role) const411     QVariant value(int role) const override
412     {
413         if (const QAbstractItemModel *aim = type->model->aim())
414             return aim->index(row, column, type->model->rootIndex).data(role);
415         return QVariant();
416     }
417 
setValue(int role,const QVariant & value)418     void setValue(int role, const QVariant &value) override
419     {
420         if (QAbstractItemModel *aim = type->model->aim())
421             aim->setData(aim->index(row, column, type->model->rootIndex), value, role);
422     }
423 
get()424     QV4::ReturnedValue get() override
425     {
426         if (type->prototype.isUndefined()) {
427             QQmlAdaptorModelEngineData * const data = engineData(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>(this));
433         o->setPrototypeOf(proto);
434         ++scriptRef;
435         return o.asReturnedValue();
436     }
437 };
438 
439 class VDMAbstractItemModelDataType : public VDMModelDelegateDataType
440 {
441 public:
VDMAbstractItemModelDataType(QQmlAdaptorModel * model)442     VDMAbstractItemModelDataType(QQmlAdaptorModel *model)
443         : VDMModelDelegateDataType(model)
444     {
445     }
446 
rowCount(const QQmlAdaptorModel & model) const447     int rowCount(const QQmlAdaptorModel &model) const override
448     {
449         if (const QAbstractItemModel *aim = model.aim())
450             return aim->rowCount(model.rootIndex);
451         return 0;
452     }
453 
columnCount(const QQmlAdaptorModel & model) const454     int columnCount(const QQmlAdaptorModel &model) const override
455     {
456         if (const QAbstractItemModel *aim = model.aim())
457             return aim->columnCount(model.rootIndex);
458         return 0;
459     }
460 
cleanup(QQmlAdaptorModel &) const461     void cleanup(QQmlAdaptorModel &) const override
462     {
463         release();
464     }
465 
value(const QQmlAdaptorModel & model,int index,const QString & role) const466     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(role.toUtf8());
475             if (it != roleNames.end()) {
476                 return aim->index(model.rowAt(index), model.columnAt(index),
477                                   model.rootIndex).data(*it);
478             } else if (role == QLatin1String("hasModelChildren")) {
479                 return QVariant(aim->hasChildren(aim->index(model.rowAt(index),
480                                                             model.columnAt(index),
481                                                             model.rootIndex)));
482             }
483         }
484         return QVariant();
485     }
486 
parentModelIndex(const QQmlAdaptorModel & model) const487     QVariant parentModelIndex(const QQmlAdaptorModel &model) const override
488     {
489         if (const QAbstractItemModel *aim = model.aim())
490             return QVariant::fromValue(aim->parent(model.rootIndex));
491         return QVariant();
492     }
493 
modelIndex(const QQmlAdaptorModel & model,int index) const494     QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override
495     {
496         if (const QAbstractItemModel *aim = model.aim())
497             return QVariant::fromValue(aim->index(model.rowAt(index), model.columnAt(index),
498                                                   model.rootIndex));
499         return QVariant();
500     }
501 
canFetchMore(const QQmlAdaptorModel & model) const502     bool canFetchMore(const QQmlAdaptorModel &model) const override
503     {
504         if (const QAbstractItemModel *aim = model.aim())
505             return aim->canFetchMore(model.rootIndex);
506         return false;
507     }
508 
fetchMore(QQmlAdaptorModel & model) const509     void fetchMore(QQmlAdaptorModel &model) const override
510     {
511         if (QAbstractItemModel *aim = model.aim())
512             aim->fetchMore(model.rootIndex);
513     }
514 
createItem(QQmlAdaptorModel & model,const QQmlRefPointer<QQmlDelegateModelItemMetaType> & metaType,int index,int row,int column) const515     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 
initializeMetaType(const QQmlAdaptorModel & model)526     void initializeMetaType(const QQmlAdaptorModel &model)
527     {
528         QMetaObjectBuilder builder;
529         setModelDataType<QQmlDMAbstractItemModelData>(&builder, 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(it.key());
537             roleNames.insert(it.value(), it.key());
538             addProperty(&builder, propertyId, 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(role);
546             roleNames.insert(propertyName, role);
547             addProperty(&builder, 1, propertyName, propertyType);
548         }
549 
550         metaObject.reset(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 
560 class QQmlDMListAccessorData : public QQmlDelegateModelItem
561 {
562     Q_OBJECT
563     Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
564 public:
QQmlDMListAccessorData(const QQmlRefPointer<QQmlDelegateModelItemMetaType> & metaType,QQmlAdaptorModel::Accessors * accessor,int index,int row,int column,const QVariant & value)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 
modelData() const573     QVariant modelData() const
574     {
575         return cachedData;
576     }
577 
setModelData(const QVariant & data)578     void setModelData(const QVariant &data)
579     {
580         if (data == cachedData)
581             return;
582 
583         cachedData = data;
584         emit modelDataChanged();
585     }
586 
get_modelData(const QV4::FunctionObject * b,const QV4::Value * thisObject,const QV4::Value *,int)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 
set_modelData(const QV4::FunctionObject * b,const QV4::Value * thisObject,const QV4::Value * argv,int argc)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(argv[0], QMetaType::UnknownType));
607         return QV4::Encode::undefined();
608     }
609 
get()610     QV4::ReturnedValue get() override
611     {
612         QQmlAdaptorModelEngineData *data = engineData(v4);
613         QV4::Scope scope(v4);
614         QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(this));
615         QV4::ScopedObject p(scope, data->listItemProto.value());
616         o->setPrototypeOf(p);
617         ++scriptRef;
618         return o.asReturnedValue();
619     }
620 
setValue(const QString & role,const QVariant & value)621     void setValue(const QString &role, const QVariant &value) override
622     {
623         if (role == QLatin1String("modelData"))
624             cachedData = value;
625     }
626 
resolveIndex(const QQmlAdaptorModel & model,int idx)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 
641 Q_SIGNALS:
642     void modelDataChanged();
643 
644 private:
645     QVariant cachedData;
646 };
647 
648 
649 class VDMListDelegateDataType : public QQmlRefCount, public QQmlAdaptorModel::Accessors
650 {
651 public:
VDMListDelegateDataType()652     VDMListDelegateDataType()
653         : QQmlRefCount()
654         , QQmlAdaptorModel::Accessors()
655     {}
656 
cleanup(QQmlAdaptorModel &) const657     void cleanup(QQmlAdaptorModel &) const override
658     {
659         const_cast<VDMListDelegateDataType *>(this)->release();
660     }
661 
rowCount(const QQmlAdaptorModel & model) const662     int rowCount(const QQmlAdaptorModel &model) const override
663     {
664         return model.list.count();
665     }
666 
columnCount(const QQmlAdaptorModel &) const667     int columnCount(const QQmlAdaptorModel &) const override
668     {
669         return 1;
670     }
671 
value(const QQmlAdaptorModel & model,int index,const QString & role) const672     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 
createItem(QQmlAdaptorModel & model,const QQmlRefPointer<QQmlDelegateModelItemMetaType> & metaType,int index,int row,int column) const679     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 
notify(const QQmlAdaptorModel & model,const QList<QQmlDelegateModelItem * > & items,int index,int count,const QVector<int> &) const697     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 
716 class VDMObjectDelegateDataType;
717 class QQmlDMObjectData : public QQmlDelegateModelItem, public QQmlAdaptorModelProxyInterface
718 {
719     Q_OBJECT
720     Q_PROPERTY(QObject *modelData READ modelData NOTIFY modelDataChanged)
721     Q_INTERFACES(QQmlAdaptorModelProxyInterface)
722 public:
723     QQmlDMObjectData(
724             const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
725             VDMObjectDelegateDataType *dataType,
726             int index, int row, int column,
727             QObject *object);
728 
setModelData(QObject * modelData)729     void setModelData(QObject *modelData)
730     {
731         if (modelData == object)
732             return;
733 
734         object = modelData;
735         emit modelDataChanged();
736     }
737 
modelData() const738     QObject *modelData() const { return object; }
proxiedObject()739     QObject *proxiedObject() override { return object; }
740 
741     QPointer<QObject> object;
742 
743 Q_SIGNALS:
744     void modelDataChanged();
745 };
746 
747 class VDMObjectDelegateDataType : public QQmlRefCount, public QQmlAdaptorModel::Accessors
748 {
749 public:
750     int propertyOffset;
751     int signalOffset;
752     bool shared;
753     QMetaObjectBuilder builder;
754 
VDMObjectDelegateDataType()755     VDMObjectDelegateDataType()
756         : propertyOffset(0)
757         , signalOffset(0)
758         , shared(true)
759     {
760     }
761 
VDMObjectDelegateDataType(const VDMObjectDelegateDataType & type)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 
rowCount(const QQmlAdaptorModel & model) const776     int rowCount(const QQmlAdaptorModel &model) const override
777     {
778         return model.list.count();
779     }
780 
columnCount(const QQmlAdaptorModel &) const781     int columnCount(const QQmlAdaptorModel &) const override
782     {
783         return 1;
784     }
785 
value(const QQmlAdaptorModel & model,int index,const QString & role) const786     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(role.toUtf8());
790         return QVariant();
791     }
792 
createItem(QQmlAdaptorModel & model,const QQmlRefPointer<QQmlDelegateModelItemMetaType> & metaType,int index,int row,int column) const793     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 *>(model.list.at(index)))
803                 : nullptr;
804     }
805 
initializeMetaType(QQmlAdaptorModel & model)806     void initializeMetaType(QQmlAdaptorModel &model)
807     {
808         Q_UNUSED(model);
809         setModelDataType<QQmlDMObjectData>(&builder, this);
810 
811         metaObject.reset(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 
cleanup(QQmlAdaptorModel &) const820     void cleanup(QQmlAdaptorModel &) const override
821     {
822         const_cast<VDMObjectDelegateDataType *>(this)->release();
823     }
824 
notify(const QQmlAdaptorModel & model,const QList<QQmlDelegateModelItem * > & items,int index,int count,const QVector<int> &) const825     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 *>(model.list.at(objectModelItem->index));
834             objectModelItem->setModelData(updatedModelData);
835         }
836         return true;
837     }
838 };
839 
840 class QQmlDMObjectDataMetaObject : public QAbstractDynamicMetaObject
841 {
842 public:
QQmlDMObjectDataMetaObject(QQmlDMObjectData * data,VDMObjectDelegateDataType * type)843     QQmlDMObjectDataMetaObject(QQmlDMObjectData *data, VDMObjectDelegateDataType *type)
844         : m_data(data)
845         , m_type(type)
846     {
847         QObjectPrivate *op = QObjectPrivate::get(m_data);
848         *static_cast<QMetaObject *>(this) = *type->metaObject;
849         op->metaObject = this;
850         m_type->addref();
851     }
852 
~QQmlDMObjectDataMetaObject()853     ~QQmlDMObjectDataMetaObject()
854     {
855         m_type->release();
856     }
857 
metaCall(QObject * o,QMetaObject::Call call,int id,void ** arguments)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(m_data, this, id - m_type->signalOffset, nullptr);
873             return -1;
874         } else {
875             return m_data->qt_metacall(call, id, arguments);
876         }
877     }
878 
createProperty(const char * name,const char *)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(propertyId + objectPropertyOffset);
903             QMetaPropertyBuilder propertyBuilder;
904             if (property.hasNotifySignal()) {
905                 m_type->builder.addSignal("__" + QByteArray::number(propertyId) + "()");
906                 propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName(), notifierId);
907                 ++notifierId;
908             } else {
909                 propertyBuilder = m_type->builder.addProperty(property.name(), 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(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(i + objectPropertyOffset);
922             if (property.hasNotifySignal()) {
923                 QQmlPropertyPrivate::connect(
924                         m_data->object, property.notifySignalIndex(), m_data, notifierId);
925                 ++notifierId;
926             }
927         }
928         return propertyIndex + m_type->propertyOffset - objectPropertyOffset;
929     }
930 
931     QQmlDMObjectData *m_data;
932     VDMObjectDelegateDataType *m_type;
933 };
934 
QQmlDMObjectData(const QQmlRefPointer<QQmlDelegateModelItemMetaType> & metaType,VDMObjectDelegateDataType * dataType,int index,int row,int column,QObject * object)935 QQmlDMObjectData::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 
949 static const QQmlAdaptorModel::Accessors qt_vdm_null_accessors;
950 
~Accessors()951 QQmlAdaptorModel::Accessors::~Accessors()
952 {
953 }
954 
QQmlAdaptorModel()955 QQmlAdaptorModel::QQmlAdaptorModel()
956     : accessors(&qt_vdm_null_accessors)
957 {
958 }
959 
~QQmlAdaptorModel()960 QQmlAdaptorModel::~QQmlAdaptorModel()
961 {
962     accessors->cleanup(*this);
963 }
964 
setModel(const QVariant & variant,QObject * parent,QQmlEngine * engine)965 void QQmlAdaptorModel::setModel(const QVariant &variant, QObject *parent, QQmlEngine *engine)
966 {
967     accessors->cleanup(*this);
968 
969     list.setList(variant, engine);
970 
971     if (QObject *object = qvariant_cast<QObject *>(list.list())) {
972         setObject(object, parent);
973         if (qobject_cast<QAbstractItemModel *>(object))
974             accessors = new VDMAbstractItemModelDataType(this);
975         else
976             accessors = new VDMObjectDelegateDataType;
977     } else if (list.type() == QQmlListAccessor::ListProperty) {
978         setObject(static_cast<const QQmlListReference *>(variant.constData())->object(), parent);
979         accessors = new VDMObjectDelegateDataType;
980     } else if (list.type() == QQmlListAccessor::ObjectList) {
981         setObject(nullptr, parent);
982         accessors = new VDMObjectDelegateDataType;
983     } else if (list.type() != QQmlListAccessor::Invalid
984             && list.type() != QQmlListAccessor::Instance) { // Null QObject
985         setObject(nullptr, parent);
986         accessors = new VDMListDelegateDataType;
987     } else {
988         setObject(nullptr, parent);
989         accessors = &qt_vdm_null_accessors;
990     }
991 }
992 
invalidateModel()993 void QQmlAdaptorModel::invalidateModel()
994 {
995     accessors->cleanup(*this);
996     accessors = &qt_vdm_null_accessors;
997     // Don't clear the model object as we still need the guard to clear the list variant if the
998     // object is destroyed.
999 }
1000 
isValid() const1001 bool QQmlAdaptorModel::isValid() const
1002 {
1003     return accessors != &qt_vdm_null_accessors;
1004 }
1005 
count() const1006 int QQmlAdaptorModel::count() const
1007 {
1008     return rowCount() * columnCount();
1009 }
1010 
rowCount() const1011 int QQmlAdaptorModel::rowCount() const
1012 {
1013     return qMax(0, accessors->rowCount(*this));
1014 }
1015 
columnCount() const1016 int QQmlAdaptorModel::columnCount() const
1017 {
1018     return qMax(0, accessors->columnCount(*this));
1019 }
1020 
rowAt(int index) const1021 int QQmlAdaptorModel::rowAt(int index) const
1022 {
1023     int count = rowCount();
1024     return count <= 0 ? -1 : index % count;
1025 }
1026 
columnAt(int index) const1027 int QQmlAdaptorModel::columnAt(int index) const
1028 {
1029     int count = rowCount();
1030     return count <= 0 ? -1 : index / count;
1031 }
1032 
indexAt(int row,int column) const1033 int QQmlAdaptorModel::indexAt(int row, int column) const
1034 {
1035     return column * rowCount() + row;
1036 }
1037 
useImportVersion(int minorVersion)1038 void QQmlAdaptorModel::useImportVersion(int minorVersion)
1039 {
1040     modelItemRevision = minorVersion;
1041 }
1042 
objectDestroyed(QObject *)1043 void QQmlAdaptorModel::objectDestroyed(QObject *)
1044 {
1045     setModel(QVariant(), nullptr, nullptr);
1046 }
1047 
QQmlAdaptorModelEngineData(QV4::ExecutionEngine * v4)1048 QQmlAdaptorModelEngineData::QQmlAdaptorModelEngineData(QV4::ExecutionEngine *v4)
1049     : v4(v4)
1050 {
1051     QV4::Scope scope(v4);
1052     QV4::ScopedObject proto(scope, v4->newObject());
1053     proto->defineAccessorProperty(QStringLiteral("index"), get_index, nullptr);
1054     proto->defineAccessorProperty(QStringLiteral("modelData"),
1055                                   QQmlDMListAccessorData::get_modelData, QQmlDMListAccessorData::set_modelData);
1056     listItemProto.set(v4, proto);
1057 }
1058 
~QQmlAdaptorModelEngineData()1059 QQmlAdaptorModelEngineData::~QQmlAdaptorModelEngineData()
1060 {
1061 }
1062 
1063 QT_END_NAMESPACE
1064 
1065 #include <qqmladaptormodel.moc>
1066