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 | |
22 | QT_BEGIN_NAMESPACE |
23 | |
24 | class VDMAbstractItemModelDataType; |
25 | class 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 | |
32 | public: |
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 | |
64 | Q_SIGNALS: |
65 | void modelDataChanged(); |
66 | |
67 | private: |
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 | |
75 | class VDMAbstractItemModelDataType |
76 | : public QQmlRefCounted<VDMAbstractItemModelDataType> |
77 | , public QQmlAdaptorModel::Accessors |
78 | , public QAbstractDynamicMetaObject |
79 | { |
80 | public: |
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 | |
332 | QT_END_NAMESPACE |
333 | |
334 | #endif // QQMLDMABSTRACTITEMMODELDATA_P_H |
335 | |