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 FINAL)
29 Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged FINAL)
30 QT_ANONYMOUS_PROPERTY(QVariant READ modelData NOTIFY modelDataChanged)
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
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 bool notify(
89 const QQmlAdaptorModel &,
90 const QList<QQmlDelegateModelItem *> &items,
91 int index,
92 int count,
93 const QVector<int> &roles) const override
94 {
95 bool changed = roles.isEmpty() && !watchedRoles.isEmpty();
96 if (!changed && !watchedRoles.isEmpty() && watchedRoleIds.isEmpty()) {
97 QList<int> roleIds;
98 for (const QByteArray &r : watchedRoles) {
99 QHash<QByteArray, int>::const_iterator it = roleNames.find(key: r);
100 if (it != roleNames.end())
101 roleIds << it.value();
102 }
103 const_cast<VDMAbstractItemModelDataType *>(this)->watchedRoleIds = roleIds;
104 }
105
106 QVector<int> signalIndexes;
107 for (int i = 0; i < roles.size(); ++i) {
108 const int role = roles.at(i);
109 if (!changed && watchedRoleIds.contains(t: role))
110 changed = true;
111
112 int propertyId = propertyRoles.indexOf(t: role);
113 if (propertyId != -1)
114 signalIndexes.append(t: propertyId + signalOffset);
115 }
116 if (roles.isEmpty()) {
117 const int propertyRolesCount = propertyRoles.size();
118 signalIndexes.reserve(asize: propertyRolesCount);
119 for (int propertyId = 0; propertyId < propertyRolesCount; ++propertyId)
120 signalIndexes.append(t: propertyId + signalOffset);
121 }
122
123 QVarLengthArray<QQmlGuard<QQmlDMAbstractItemModelData>> guardedItems;
124 for (const auto item : items) {
125 Q_ASSERT(qobject_cast<QQmlDMAbstractItemModelData *>(item) == item);
126 guardedItems.append(t: static_cast<QQmlDMAbstractItemModelData *>(item));
127 }
128
129 for (const auto &item : std::as_const(t&: guardedItems)) {
130 if (item.isNull())
131 continue;
132
133 const int idx = item->modelIndex();
134 if (idx >= index && idx < index + count) {
135 for (int i = 0; i < signalIndexes.size(); ++i)
136 QMetaObject::activate(sender: item, signal_index: signalIndexes.at(i), argv: nullptr);
137 emit item->modelDataChanged();
138 }
139 }
140 return changed;
141 }
142
143 void replaceWatchedRoles(
144 QQmlAdaptorModel &,
145 const QList<QByteArray> &oldRoles,
146 const QList<QByteArray> &newRoles) const override
147 {
148 VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this);
149
150 dataType->watchedRoleIds.clear();
151 for (const QByteArray &oldRole : oldRoles)
152 dataType->watchedRoles.removeOne(t: oldRole);
153 dataType->watchedRoles += newRoles;
154 }
155
156 static QV4::ReturnedValue get_hasModelChildren(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
157 {
158 QV4::Scope scope(b);
159 QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
160 if (!o)
161 RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")));
162
163 const QQmlAdaptorModel *const model
164 = static_cast<QQmlDMAbstractItemModelData *>(o->d()->item)->type()->model;
165 if (o->d()->item->index >= 0) {
166 if (const QAbstractItemModel *const aim = model->aim())
167 RETURN_RESULT(QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex))));
168 }
169 RETURN_RESULT(QV4::Encode(false));
170 }
171
172
173 void initializeConstructor(QQmlAdaptorModelEngineData *const data)
174 {
175 QV4::ExecutionEngine *v4 = data->v4;
176 QV4::Scope scope(v4);
177 QV4::ScopedObject proto(scope, v4->newObject());
178 proto->defineAccessorProperty(QStringLiteral("index"), getter: QQmlAdaptorModelEngineData::get_index, setter: nullptr);
179 proto->defineAccessorProperty(QStringLiteral("hasModelChildren"), getter: get_hasModelChildren, setter: nullptr);
180 proto->defineAccessorProperty(QStringLiteral("modelData"),
181 getter: QQmlDMAbstractItemModelData::get_modelData,
182 setter: QQmlDMAbstractItemModelData::set_modelData);
183 QV4::ScopedProperty p(scope);
184
185 typedef QHash<QByteArray, int>::const_iterator iterator;
186 for (iterator it = roleNames.constBegin(), end = roleNames.constEnd(); it != end; ++it) {
187 const qsizetype propertyId = propertyRoles.indexOf(t: it.value());
188 const QByteArray &propertyName = it.key();
189
190 QV4::ScopedString name(scope, v4->newString(s: QString::fromUtf8(ba: propertyName)));
191 QV4::ExecutionContext *global = v4->rootContext();
192 QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(args&: global, args: propertyId, args&: QQmlDMAbstractItemModelData::get_property));
193 QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(args&: global, args: propertyId, args&: QQmlDMAbstractItemModelData::set_property));
194 p->setGetter(g);
195 p->setSetter(s);
196 proto->insertMember(s: name, p, attributes: QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable);
197 }
198 prototype.set(engine: v4, value: proto);
199 }
200
201 // QAbstractDynamicMetaObject
202
203 void objectDestroyed(QObject *) override
204 {
205 release();
206 }
207
208 int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) override
209 {
210 return static_cast<QQmlDMAbstractItemModelData *>(object)->metaCall(call, id, arguments);
211 }
212
213 int rowCount(const QQmlAdaptorModel &model) const override
214 {
215 if (const QAbstractItemModel *aim = model.aim())
216 return aim->rowCount(parent: model.rootIndex);
217 return 0;
218 }
219
220 int columnCount(const QQmlAdaptorModel &model) const override
221 {
222 if (const QAbstractItemModel *aim = model.aim())
223 return aim->columnCount(parent: model.rootIndex);
224 return 0;
225 }
226
227 void cleanup(QQmlAdaptorModel &) const override
228 {
229 release();
230 }
231
232 QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
233 {
234 if (!metaObject) {
235 VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this);
236 dataType->initializeMetaType(model);
237 }
238
239 if (const QAbstractItemModel *aim = model.aim()) {
240 const QModelIndex modelIndex
241 = aim->index(row: model.rowAt(index), column: model.columnAt(index), parent: model.rootIndex);
242
243 const auto it = roleNames.find(key: role.toUtf8()), end = roleNames.end();
244 if (it != roleNames.end())
245 return modelIndex.data(arole: *it);
246
247 if (role.isEmpty() || role == QLatin1String("modelData")) {
248 if (roleNames.size() == 1)
249 return modelIndex.data(arole: roleNames.begin().value());
250
251 QVariantMap modelData;
252 for (auto jt = roleNames.begin(); jt != end; ++jt)
253 modelData.insert(key: QString::fromUtf8(ba: jt.key()), value: modelIndex.data(arole: jt.value()));
254 return modelData;
255 }
256
257 if (role == QLatin1String("hasModelChildren"))
258 return QVariant(aim->hasChildren(parent: modelIndex));
259 }
260 return QVariant();
261 }
262
263 QVariant parentModelIndex(const QQmlAdaptorModel &model) const override
264 {
265 if (const QAbstractItemModel *aim = model.aim())
266 return QVariant::fromValue(value: aim->parent(child: model.rootIndex));
267 return QVariant();
268 }
269
270 QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override
271 {
272 if (const QAbstractItemModel *aim = model.aim())
273 return QVariant::fromValue(value: aim->index(row: model.rowAt(index), column: model.columnAt(index),
274 parent: model.rootIndex));
275 return QVariant();
276 }
277
278 bool canFetchMore(const QQmlAdaptorModel &model) const override
279 {
280 if (const QAbstractItemModel *aim = model.aim())
281 return aim->canFetchMore(parent: model.rootIndex);
282 return false;
283 }
284
285 void fetchMore(QQmlAdaptorModel &model) const override
286 {
287 if (QAbstractItemModel *aim = model.aim())
288 aim->fetchMore(parent: model.rootIndex);
289 }
290
291 QQmlDelegateModelItem *createItem(
292 QQmlAdaptorModel &model,
293 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
294 int index, int row, int column) override
295 {
296 if (!metaObject)
297 initializeMetaType(model);
298 return new QQmlDMAbstractItemModelData(metaType, this, index, row, column);
299 }
300
301 void initializeMetaType(const QQmlAdaptorModel &model)
302 {
303 QMetaObjectBuilder builder;
304 QQmlAdaptorModelEngineData::setModelDataType<QQmlDMAbstractItemModelData>(builder: &builder, metaType: this);
305
306 const QByteArray propertyType = QByteArrayLiteral("QVariant");
307 const QAbstractItemModel *aim = model.aim();
308 const QHash<int, QByteArray> names = aim ? aim->roleNames() : QHash<int, QByteArray>();
309 for (QHash<int, QByteArray>::const_iterator it = names.begin(), cend = names.end(); it != cend; ++it) {
310 const int propertyId = propertyRoles.size();
311 propertyRoles.append(t: it.key());
312 roleNames.insert(key: it.value(), value: it.key());
313 QQmlAdaptorModelEngineData::addProperty(builder: &builder, propertyId, propertyName: it.value(), propertyType);
314 }
315
316 metaObject.reset(other: builder.toMetaObject());
317 *static_cast<QMetaObject *>(this) = *metaObject;
318 propertyCache = QQmlPropertyCache::createStandalone(
319 metaObject.data(), metaObjectRevision: model.modelItemRevision);
320 }
321
322 QV4::PersistentValue prototype;
323 QList<int> propertyRoles;
324 QList<int> watchedRoleIds;
325 QList<QByteArray> watchedRoles;
326 QHash<QByteArray, int> roleNames;
327 QQmlAdaptorModel *model;
328 int propertyOffset;
329 int signalOffset;
330};
331
332QT_END_NAMESPACE
333
334#endif // QQMLDMABSTRACTITEMMODELDATA_P_H
335

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