1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QQMLDMABSTRACTITEMMODELDATA_P_H
5#define QQMLDMABSTRACTITEMMODELDATA_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <private/qqmladaptormodelenginedata_p.h>
19#include <private/qqmldelegatemodel_p_p.h>
20#include <private/qobject_p.h>
21
22QT_BEGIN_NAMESPACE
23
24class VDMAbstractItemModelDataType;
25class QQmlDMAbstractItemModelData : public QQmlDelegateModelItem
26{
27 Q_OBJECT
28 Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT)
29 Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
30 QT_ANONYMOUS_PROPERTY(QVariant READ modelData NOTIFY modelDataChanged FINAL)
31
32public:
33 QQmlDMAbstractItemModelData(
34 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
35 VDMAbstractItemModelDataType *dataType,
36 int index, int row, int column);
37
38 int metaCall(QMetaObject::Call call, int id, void **arguments);
39 bool hasModelChildren() const;
40
41 QV4::ReturnedValue get() override;
42 void setValue(const QString &role, const QVariant &value) override;
43 bool resolveIndex(const QQmlAdaptorModel &model, int idx) override;
44
45 static QV4::ReturnedValue get_property(
46 const QV4::FunctionObject *b, const QV4::Value *thisObject,
47 const QV4::Value *argv, int argc);
48 static QV4::ReturnedValue set_property(
49 const QV4::FunctionObject *b, const QV4::Value *thisObject,
50 const QV4::Value *argv, int argc);
51
52 static QV4::ReturnedValue get_modelData(
53 const QV4::FunctionObject *b, const QV4::Value *thisObject,
54 const QV4::Value *argv, int argc);
55 static QV4::ReturnedValue set_modelData(
56 const QV4::FunctionObject *b, const QV4::Value *thisObject,
57 const QV4::Value *argv, int argc);
58
59 QVariant modelData() const;
60 void setModelData(const QVariant &modelData);
61
62 const VDMAbstractItemModelDataType *type() const { return m_type; }
63
64Q_SIGNALS:
65 void modelDataChanged();
66
67private:
68 QVariant value(int role) const;
69 void setValue(int role, const QVariant &value);
70
71 VDMAbstractItemModelDataType *m_type;
72 QVector<QVariant> m_cachedData;
73};
74
75class VDMAbstractItemModelDataType final
76 : public QQmlRefCounted<VDMAbstractItemModelDataType>
77 , public QQmlAdaptorModel::Accessors
78 , public QAbstractDynamicMetaObject
79{
80public:
81 VDMAbstractItemModelDataType(QQmlAdaptorModel *model)
82 : model(model)
83 , propertyOffset(0)
84 , signalOffset(0)
85 {
86 }
87
88 void notifyItem(const QQmlGuard<QQmlDMAbstractItemModelData> &item, const QVector<int> &signalIndexes) const
89 {
90 for (const int signalIndex : signalIndexes) {
91 QMetaObject::activate(sender: item, signal_index: signalIndex, argv: nullptr);
92 if (item.isNull())
93 return;
94 }
95 emit item->modelDataChanged();
96 }
97
98 bool notify(
99 const QQmlAdaptorModel &,
100 const QList<QQmlDelegateModelItem *> &items,
101 int index,
102 int count,
103 const QVector<int> &roles) const override
104 {
105 bool changed = roles.isEmpty() && !watchedRoles.isEmpty();
106 if (!changed && !watchedRoles.isEmpty() && watchedRoleIds.isEmpty()) {
107 QList<int> roleIds;
108 for (const QByteArray &r : watchedRoles) {
109 QHash<QByteArray, int>::const_iterator it = roleNames.find(key: r);
110 if (it != roleNames.end())
111 roleIds << it.value();
112 }
113 const_cast<VDMAbstractItemModelDataType *>(this)->watchedRoleIds = roleIds;
114 }
115
116 QVector<int> signalIndexes;
117 for (int i = 0; i < roles.size(); ++i) {
118 const int role = roles.at(i);
119 if (!changed && watchedRoleIds.contains(t: role))
120 changed = true;
121
122 int propertyId = propertyRoles.indexOf(t: role);
123 if (propertyId != -1)
124 signalIndexes.append(t: propertyId + signalOffset);
125 }
126 if (roles.isEmpty()) {
127 const int propertyRolesCount = propertyRoles.size();
128 signalIndexes.reserve(asize: propertyRolesCount);
129 for (int propertyId = 0; propertyId < propertyRolesCount; ++propertyId)
130 signalIndexes.append(t: propertyId + signalOffset);
131 }
132
133 QVarLengthArray<QQmlGuard<QQmlDMAbstractItemModelData>> guardedItems;
134 for (const auto item : items) {
135 Q_ASSERT(qobject_cast<QQmlDMAbstractItemModelData *>(item) == item);
136 guardedItems.append(t: static_cast<QQmlDMAbstractItemModelData *>(item));
137 }
138
139 for (const auto &item : std::as_const(t&: guardedItems)) {
140 if (item.isNull())
141 continue;
142
143 const int idx = item->modelIndex();
144 if (idx >= index && idx < index + count)
145 notifyItem(item, signalIndexes);
146 }
147 return changed;
148 }
149
150 void replaceWatchedRoles(
151 QQmlAdaptorModel &,
152 const QList<QByteArray> &oldRoles,
153 const QList<QByteArray> &newRoles) const override
154 {
155 VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this);
156
157 dataType->watchedRoleIds.clear();
158 for (const QByteArray &oldRole : oldRoles)
159 dataType->watchedRoles.removeOne(t: oldRole);
160 dataType->watchedRoles += newRoles;
161 }
162
163 static QV4::ReturnedValue get_hasModelChildren(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
164 {
165 QV4::Scope scope(b);
166 QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
167 if (!o)
168 RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")));
169
170 const QQmlAdaptorModel *const model
171 = static_cast<QQmlDMAbstractItemModelData *>(o->d()->item)->type()->model;
172 if (o->d()->item->modelIndex() >= 0) {
173 if (const QAbstractItemModel *const aim = model->aim())
174 RETURN_RESULT(QV4::Encode(aim->hasChildren(
175 aim->index(o->d()->item->modelIndex(), 0, model->rootIndex))));
176 }
177 RETURN_RESULT(QV4::Encode(false));
178 }
179
180
181 void initializeConstructor(QQmlAdaptorModelEngineData *const data)
182 {
183 QV4::ExecutionEngine *v4 = data->v4;
184 QV4::Scope scope(v4);
185 QV4::ScopedObject proto(scope, v4->newObject());
186 proto->defineAccessorProperty(QStringLiteral("index"), getter: QQmlAdaptorModelEngineData::get_index, setter: nullptr);
187 proto->defineAccessorProperty(QStringLiteral("hasModelChildren"), getter: get_hasModelChildren, setter: nullptr);
188 proto->defineAccessorProperty(QStringLiteral("modelData"),
189 getter: QQmlDMAbstractItemModelData::get_modelData,
190 setter: QQmlDMAbstractItemModelData::set_modelData);
191 QV4::ScopedProperty p(scope);
192
193 typedef QHash<QByteArray, int>::const_iterator iterator;
194 for (iterator it = roleNames.constBegin(), end = roleNames.constEnd(); it != end; ++it) {
195 const qsizetype propertyId = propertyRoles.indexOf(t: it.value());
196 const QByteArray &propertyName = it.key();
197
198 QV4::ScopedString name(scope, v4->newString(s: QString::fromUtf8(ba: propertyName)));
199 QV4::ScopedFunctionObject g(
200 scope,
201 v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(
202 args&: v4, args: propertyId, args&: QQmlDMAbstractItemModelData::get_property));
203 QV4::ScopedFunctionObject s(
204 scope,
205 v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(
206 args&: v4, args: propertyId, args&: QQmlDMAbstractItemModelData::set_property));
207 p->setGetter(g);
208 p->setSetter(s);
209 proto->insertMember(s: name, p, attributes: QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable);
210 }
211 prototype.set(engine: v4, value: proto);
212 }
213
214 // QAbstractDynamicMetaObject
215
216 void objectDestroyed(QObject *) override
217 {
218 release();
219 }
220
221 int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) override
222 {
223 return static_cast<QQmlDMAbstractItemModelData *>(object)->metaCall(call, id, arguments);
224 }
225
226 int rowCount(const QQmlAdaptorModel &model) const override
227 {
228 if (const QAbstractItemModel *aim = model.aim())
229 return aim->rowCount(parent: model.rootIndex);
230 return 0;
231 }
232
233 int columnCount(const QQmlAdaptorModel &model) const override
234 {
235 if (const QAbstractItemModel *aim = model.aim())
236 return aim->columnCount(parent: model.rootIndex);
237 return 0;
238 }
239
240 void cleanup(QQmlAdaptorModel &) const override
241 {
242 release();
243 }
244
245 QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
246 {
247 if (!metaObject) {
248 VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this);
249 dataType->initializeMetaObject(model);
250 }
251
252 if (const QAbstractItemModel *aim = model.aim()) {
253 const QModelIndex modelIndex
254 = aim->index(row: model.rowAt(index), column: model.columnAt(index), parent: model.rootIndex);
255
256 const auto it = roleNames.find(key: role.toUtf8()), end = roleNames.end();
257 if (it != roleNames.end())
258 return modelIndex.data(arole: *it);
259
260 if (role.isEmpty() || role == QLatin1String("modelData")) {
261 if (roleNames.size() == 1)
262 return modelIndex.data(arole: roleNames.begin().value());
263
264 QVariantMap modelData;
265 for (auto jt = roleNames.begin(); jt != end; ++jt)
266 modelData.insert(key: QString::fromUtf8(ba: jt.key()), value: modelIndex.data(arole: jt.value()));
267 return modelData;
268 }
269
270 if (role == QLatin1String("hasModelChildren"))
271 return QVariant(aim->hasChildren(parent: modelIndex));
272 }
273 return QVariant();
274 }
275
276 QVariant parentModelIndex(const QQmlAdaptorModel &model) const override
277 {
278 if (const QAbstractItemModel *aim = model.aim())
279 return QVariant::fromValue(value: aim->parent(child: model.rootIndex));
280 return QVariant();
281 }
282
283 QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override
284 {
285 if (const QAbstractItemModel *aim = model.aim())
286 return QVariant::fromValue(value: aim->index(row: model.rowAt(index), column: model.columnAt(index),
287 parent: model.rootIndex));
288 return QVariant();
289 }
290
291 bool canFetchMore(const QQmlAdaptorModel &model) const override
292 {
293 if (const QAbstractItemModel *aim = model.aim())
294 return aim->canFetchMore(parent: model.rootIndex);
295 return false;
296 }
297
298 void fetchMore(QQmlAdaptorModel &model) const override
299 {
300 if (QAbstractItemModel *aim = model.aim())
301 aim->fetchMore(parent: model.rootIndex);
302 }
303
304 QQmlDelegateModelItem *createItem(
305 QQmlAdaptorModel &model,
306 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
307 int index, int row, int column) override
308 {
309 if (!metaObject)
310 initializeMetaObject(model);
311 return new QQmlDMAbstractItemModelData(metaType, this, index, row, column);
312 }
313
314 void initializeMetaObject(const QQmlAdaptorModel &model)
315 {
316 QMetaObjectBuilder builder;
317 QQmlAdaptorModelEngineData::setModelDataType<QQmlDMAbstractItemModelData>(builder: &builder, metaType: this);
318
319 const QByteArray propertyType = QByteArrayLiteral("QVariant");
320 const QAbstractItemModel *aim = model.aim();
321 const QHash<int, QByteArray> names = aim ? aim->roleNames() : QHash<int, QByteArray>();
322 for (QHash<int, QByteArray>::const_iterator it = names.begin(), cend = names.end(); it != cend; ++it) {
323 const int propertyId = propertyRoles.size();
324 propertyRoles.append(t: it.key());
325 roleNames.insert(key: it.value(), value: it.key());
326 QQmlAdaptorModelEngineData::addProperty(
327 builder: &builder, propertyId, propertyName: it.value(), propertyType,
328 isWritable: model.delegateModelAccess != QQmlDelegateModel::ReadOnly);
329 }
330
331 metaObject.reset(other: builder.toMetaObject());
332 *static_cast<QMetaObject *>(this) = *metaObject;
333 propertyCache = QQmlPropertyCache::createStandalone(
334 metaObject.data(), metaObjectRevision: model.modelItemRevision);
335 }
336
337 QV4::PersistentValue prototype;
338 QList<int> propertyRoles;
339 QList<int> watchedRoleIds;
340 QList<QByteArray> watchedRoles;
341 QHash<QByteArray, int> roleNames;
342 QQmlAdaptorModel *model;
343 int propertyOffset;
344 int signalOffset;
345};
346
347QT_END_NAMESPACE
348
349#endif // QQMLDMABSTRACTITEMMODELDATA_P_H
350

source code of qtdeclarative/src/qmlmodels/qqmldmabstractitemmodeldata_p.h