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 QQMLDMLISTACCESSORDATA_P_H
5#define QQMLDMLISTACCESSORDATA_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 VDMListDelegateDataType;
25
26class QQmlDMListAccessorData : public QQmlDelegateModelItem
27{
28 Q_OBJECT
29 Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged)
30 QT_ANONYMOUS_PROPERTY(QVariant READ modelData WRITE setModelData NOTIFY modelDataChanged FINAL)
31public:
32 QQmlDMListAccessorData(
33 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
34 VDMListDelegateDataType *dataType, int index, int row, int column,
35 const QVariant &value);
36 ~QQmlDMListAccessorData();
37
38 QVariant modelData() const
39 {
40 return cachedData;
41 }
42
43 void setModelData(const QVariant &data);
44
45 static QV4::ReturnedValue get_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
46 {
47 QV4::ExecutionEngine *v4 = b->engine();
48 const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>();
49 if (!o)
50 return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
51
52 return v4->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData);
53 }
54
55 static QV4::ReturnedValue set_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
56 {
57 QV4::ExecutionEngine *v4 = b->engine();
58 const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>();
59 if (!o)
60 return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
61 if (!argc)
62 return v4->throwTypeError();
63
64 static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData(
65 QV4::ExecutionEngine::toVariant(value: argv[0], typeHint: QMetaType {}));
66 return QV4::Encode::undefined();
67 }
68
69 QV4::ReturnedValue get() override
70 {
71 QV4::Scope scope(metaType->v4Engine);
72 QQmlAdaptorModelEngineData *data = QQmlAdaptorModelEngineData::get(engine: scope.engine);
73 QV4::ScopedObject o(
74 scope, scope.engine->memoryManager->allocate<QQmlDelegateModelItemObject>(args: this));
75 QV4::ScopedObject p(scope, data->listItemProto.value());
76 o->setPrototypeOf(p);
77 ++scriptRef;
78 return o.asReturnedValue();
79 }
80
81 void setValue(const QString &role, const QVariant &value) override;
82 bool resolveIndex(const QQmlAdaptorModel &model, int idx) override;
83
84Q_SIGNALS:
85 void modelDataChanged();
86
87private:
88 friend class VDMListDelegateDataType;
89 QVariant cachedData;
90
91 // Gets cleaned when the metaobject has processed it.
92 bool cachedDataClean = false;
93};
94
95
96class VDMListDelegateDataType final
97 : public QQmlRefCounted<VDMListDelegateDataType>
98 , public QQmlAdaptorModel::Accessors
99 , public QAbstractDynamicMetaObject
100{
101public:
102 VDMListDelegateDataType(QQmlAdaptorModel *model)
103 : model(model)
104 {
105 QQmlAdaptorModelEngineData::setModelDataType<QQmlDMListAccessorData>(builder: &builder, metaType: this);
106 metaObject.reset(other: builder.toMetaObject());
107 *static_cast<QMetaObject *>(this) = *metaObject.data();
108 }
109
110 void cleanup(QQmlAdaptorModel &) const override
111 {
112 release();
113 }
114
115 int rowCount(const QQmlAdaptorModel &model) const override
116 {
117 return model.list.count();
118 }
119
120 int columnCount(const QQmlAdaptorModel &model) const override
121 {
122 switch (model.list.type()) {
123 case QQmlListAccessor::Invalid:
124 return 0;
125 case QQmlListAccessor::StringList:
126 case QQmlListAccessor::UrlList:
127 case QQmlListAccessor::Integer:
128 return 1;
129 default:
130 break;
131 }
132
133 // If there are no properties, we can get modelData itself.
134 return std::max(a: 1, b: propertyCount() - propertyOffset);
135 }
136
137 static const QMetaObject *metaObjectFromType(QMetaType type)
138 {
139 if (const QMetaObject *metaObject = type.metaObject())
140 return metaObject;
141
142 // NB: This acquires the lock on QQmlMetaTypeData. If we had a QQmlEngine here,
143 // we could use QQmlGadgetPtrWrapper::instance() to avoid this.
144 if (const QQmlValueType *valueType = QQmlMetaType::valueType(metaType: type))
145 return valueType->staticMetaObject();
146
147 return nullptr;
148 }
149
150 template<typename String>
151 static QString toQString(const String &string)
152 {
153 if constexpr (std::is_same_v<String, QString>)
154 return string;
155 else if constexpr (std::is_same_v<String, QByteArray>)
156 return QString::fromUtf8(string);
157 else if constexpr (std::is_same_v<String, const char *>)
158 return QString::fromUtf8(string);
159 Q_UNREACHABLE_RETURN(QString());
160 }
161
162 template<typename String>
163 static QByteArray toUtf8(const String &string)
164 {
165 if constexpr (std::is_same_v<String, QString>)
166 return string.toUtf8();
167 else if constexpr (std::is_same_v<String, QByteArray>)
168 return string;
169 else if constexpr (std::is_same_v<String, const char *>)
170 return QByteArray::fromRawData(data: string, size: qstrlen(string));
171 Q_UNREACHABLE_RETURN(QByteArray());
172 }
173
174 template<typename String>
175 static QVariant value(const QVariant *row, const String &role)
176 {
177 const QMetaType type = row->metaType();
178 if (type == QMetaType::fromType<QVariantMap>())
179 return row->toMap().value(key: toQString(role));
180
181 if (type == QMetaType::fromType<QVariantHash>())
182 return row->toHash().value(toQString(role));
183
184 const QMetaType::TypeFlags typeFlags = type.flags();
185 if (typeFlags & QMetaType::PointerToQObject)
186 return row->value<QObject *>()->property(name: toUtf8(role));
187
188 if (const QMetaObject *metaObject = metaObjectFromType(type)) {
189 const int propertyIndex = metaObject->indexOfProperty(name: toUtf8(role));
190 if (propertyIndex >= 0)
191 return metaObject->property(index: propertyIndex).readOnGadget(gadget: row->constData());
192 }
193
194 return QVariant();
195 }
196
197 template<typename String>
198 void createPropertyIfMissing(const String &string)
199 {
200 for (int i = 0, end = propertyCount(); i < end; ++i) {
201 if (QAnyStringView(property(index: i).name()) == QAnyStringView(string))
202 return;
203 }
204
205 createProperty(name: toUtf8(string), nullptr);
206 }
207
208 void createMissingProperties(const QVariant *row)
209 {
210 const QMetaType type = row->metaType();
211 if (type == QMetaType::fromType<QVariantMap>()) {
212 const QVariantMap map = row->toMap();
213 for (auto it = map.keyBegin(), end = map.keyEnd(); it != end; ++it)
214 createPropertyIfMissing(string: *it);
215 } else if (type == QMetaType::fromType<QVariantHash>()) {
216 const QVariantHash map = row->toHash();
217 for (auto it = map.keyBegin(), end = map.keyEnd(); it != end; ++it)
218 createPropertyIfMissing(string: *it);
219 } else if (type.flags() & QMetaType::PointerToQObject) {
220 const QMetaObject *metaObject = row->value<QObject *>()->metaObject();
221 for (int i = 0, end = metaObject->propertyCount(); i < end; ++i)
222 createPropertyIfMissing(string: metaObject->property(index: i).name());
223 } else if (const QMetaObject *metaObject = metaObjectFromType(type)) {
224 for (int i = 0, end = metaObject->propertyCount(); i < end; ++i)
225 createPropertyIfMissing(string: metaObject->property(index: i).name());
226 }
227 }
228
229 template<typename String>
230 static void setValue(QVariant *row, const String &role, const QVariant &value)
231 {
232 const QMetaType type = row->metaType();
233 if (type == QMetaType::fromType<QVariantMap>()) {
234 static_cast<QVariantMap *>(row->data())->insert(toQString(role), value);
235 } else if (type == QMetaType::fromType<QVariantHash>()) {
236 static_cast<QVariantHash *>(row->data())->insert(toQString(role), value);
237 } else if (type.flags() & QMetaType::PointerToQObject) {
238 row->value<QObject *>()->setProperty(toUtf8(role), value);
239 } else if (const QMetaObject *metaObject = metaObjectFromType(type)) {
240 const int propertyIndex = metaObject->indexOfProperty(name: toUtf8(role));
241 if (propertyIndex >= 0)
242 metaObject->property(index: propertyIndex).writeOnGadget(gadget: row->data(), value);
243 }
244 }
245
246 QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
247 {
248 const QVariant entry = model.list.at(index);
249 if (role == QLatin1String("modelData") || role.isEmpty())
250 return entry;
251
252 return value(row: &entry, role);
253 }
254
255 QQmlDelegateModelItem *createItem(
256 QQmlAdaptorModel &model,
257 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
258 int index, int row, int column) override
259 {
260 const QVariant value = (index >= 0 && index < model.list.count())
261 ? model.list.at(index)
262 : QVariant();
263 return new QQmlDMListAccessorData(metaType, this, index, row, column, value);
264 }
265
266 bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override
267 {
268 for (auto modelItem : items) {
269 const int modelItemIndex = modelItem->modelIndex();
270 if (modelItemIndex < index || modelItemIndex >= index + count)
271 continue;
272
273 auto listModelItem = static_cast<QQmlDMListAccessorData *>(modelItem);
274 QVariant updatedModelData = model.list.at(listModelItem->modelIndex());
275 listModelItem->setModelData(updatedModelData);
276 }
277 return true;
278 }
279
280 void emitAllSignals(QQmlDMListAccessorData *accessor) const;
281
282 int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) final;
283 int createProperty(const char *name, const char *) final;
284#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
285 const QMetaObject *toDynamicMetaObject(QObject *accessors) const final;
286#else
287 QMetaObject *toDynamicMetaObject(QObject *accessors) final;
288#endif
289
290 QMetaObjectBuilder builder;
291 QQmlAdaptorModel *model = nullptr;
292 int propertyOffset = 0;
293 int signalOffset = 0;
294};
295
296QT_END_NAMESPACE
297
298#endif // QQMLDMLISTACCESSORDATA_P_H
299

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