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