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#include <private/qqmldmabstractitemmodeldata_p.h>
5
6QT_BEGIN_NAMESPACE
7
8QQmlDMAbstractItemModelData::QQmlDMAbstractItemModelData(
9 const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
10 VDMAbstractItemModelDataType *dataType, int index, int row, int column)
11 : QQmlDelegateModelItem(metaType, dataType, index, row, column)
12 , m_type(dataType)
13{
14 if (index == -1)
15 m_cachedData.resize(size: m_type->propertyRoles.size());
16
17 QObjectPrivate::get(o: this)->metaObject = m_type;
18
19 m_type->addref();
20}
21
22int QQmlDMAbstractItemModelData::metaCall(QMetaObject::Call call, int id, void **arguments)
23{
24 if (call == QMetaObject::ReadProperty && id >= m_type->propertyOffset) {
25 const int propertyIndex = id - m_type->propertyOffset;
26 if (index == -1) {
27 if (!m_cachedData.isEmpty())
28 *static_cast<QVariant *>(arguments[0]) = m_cachedData.at(i: propertyIndex);
29 } else if (*m_type->model) {
30 *static_cast<QVariant *>(arguments[0]) = value(role: m_type->propertyRoles.at(i: propertyIndex));
31 }
32 return -1;
33 } else if (call == QMetaObject::WriteProperty && id >= m_type->propertyOffset) {
34 const int propertyIndex = id - m_type->propertyOffset;
35 const QMetaObject *meta = metaObject();
36 if (index == -1) {
37 if (m_cachedData.size() > 1) {
38 m_cachedData[propertyIndex] = *static_cast<QVariant *>(arguments[0]);
39 QMetaObject::activate(sender: this, meta, local_signal_index: propertyIndex, argv: nullptr);
40 } else if (m_cachedData.size() == 1) {
41 m_cachedData[0] = *static_cast<QVariant *>(arguments[0]);
42 QMetaObject::activate(sender: this, meta, local_signal_index: 0, argv: nullptr);
43 }
44
45 emit modelDataChanged();
46 } else if (*m_type->model) {
47 setValue(role: m_type->propertyRoles.at(i: propertyIndex),
48 value: *static_cast<QVariant *>(arguments[0]));
49 }
50 return -1;
51 } else {
52 return qt_metacall(call, id, arguments);
53 }
54}
55
56void QQmlDMAbstractItemModelData::setValue(const QString &role, const QVariant &value)
57{
58 // Used only for initialization of the cached data. Does not have to emit change signals.
59
60 if (m_type->propertyRoles.size() == 1
61 && (role.isEmpty() || role == QLatin1String("modelData"))) {
62 // If the model has only a single role, the modelData is that role.
63 m_cachedData[0] = value;
64 return;
65 }
66
67 const auto it = m_type->roleNames.constFind(key: role.toUtf8());
68 if (it != m_type->roleNames.cend()) {
69 for (int i = 0; i < m_type->propertyRoles.size(); ++i) {
70 if (m_type->propertyRoles.at(i) == *it) {
71 m_cachedData[i] = value;
72 return;
73 }
74 }
75 }
76}
77
78bool QQmlDMAbstractItemModelData::resolveIndex(const QQmlAdaptorModel &adaptorModel, int idx)
79{
80 if (index == -1) {
81 Q_ASSERT(idx >= 0);
82 m_cachedData.clear();
83 setModelIndex(idx, newRow: adaptorModel.rowAt(index: idx), newColumn: adaptorModel.columnAt(index: idx));
84 const QMetaObject *meta = metaObject();
85 const int propertyCount = m_type->propertyRoles.size();
86 for (int i = 0; i < propertyCount; ++i)
87 QMetaObject::activate(sender: this, meta, local_signal_index: i, argv: nullptr);
88 emit modelDataChanged();
89 return true;
90 } else {
91 return false;
92 }
93}
94
95QV4::ReturnedValue QQmlDMAbstractItemModelData::get_property(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int)
96{
97 QV4::Scope scope(b);
98 QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
99 if (!o)
100 return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
101
102 const qsizetype propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index;
103
104 QQmlDMAbstractItemModelData *modelData = static_cast<QQmlDMAbstractItemModelData *>(o->d()->item);
105 if (o->d()->item->modelIndex() == -1) {
106 if (!modelData->m_cachedData.isEmpty())
107 return scope.engine->fromVariant(modelData->m_cachedData.at(i: propertyId));
108 } else if (*modelData->m_type->model) {
109 return scope.engine->fromVariant(
110 modelData->value(role: modelData->m_type->propertyRoles.at(i: propertyId)));
111 }
112 return QV4::Encode::undefined();
113}
114
115QV4::ReturnedValue QQmlDMAbstractItemModelData::set_property(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc)
116{
117 QV4::Scope scope(b);
118 QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
119 if (!o)
120 return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
121 if (!argc)
122 return scope.engine->throwTypeError();
123
124 const qsizetype propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index;
125
126 if (o->d()->item->modelIndex() == -1) {
127 QQmlDMAbstractItemModelData *modelData = static_cast<QQmlDMAbstractItemModelData *>(o->d()->item);
128 if (!modelData->m_cachedData.isEmpty()) {
129 if (modelData->m_cachedData.size() > 1) {
130 modelData->m_cachedData[propertyId]
131 = QV4::ExecutionEngine::toVariant(value: argv[0], typeHint: QMetaType {});
132 QMetaObject::activate(sender: o->d()->item, o->d()->item->metaObject(), local_signal_index: propertyId, argv: nullptr);
133 } else if (modelData->m_cachedData.size() == 1) {
134 modelData->m_cachedData[0] = QV4::ExecutionEngine::toVariant(value: argv[0], typeHint: QMetaType {});
135 QMetaObject::activate(sender: o->d()->item, o->d()->item->metaObject(), local_signal_index: 0, argv: nullptr);
136 }
137 emit modelData->modelDataChanged();
138 }
139 }
140 return QV4::Encode::undefined();
141}
142
143QV4::ReturnedValue QQmlDMAbstractItemModelData::get_modelData(
144 const QV4::FunctionObject *b, const QV4::Value *thisObject,
145 const QV4::Value *argv, int argc)
146{
147 Q_UNUSED(argv)
148 Q_UNUSED(argc)
149
150 QV4::Scope scope(b);
151 QV4::Scoped<QQmlDelegateModelItemObject> o(
152 scope, thisObject->as<QQmlDelegateModelItemObject>());
153 if (!o)
154 return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
155
156 return scope.engine->fromVariant(
157 static_cast<QQmlDMAbstractItemModelData *>(o->d()->item)->modelData());
158}
159
160QV4::ReturnedValue QQmlDMAbstractItemModelData::set_modelData(
161 const QV4::FunctionObject *b, const QV4::Value *thisObject,
162 const QV4::Value *argv, int argc)
163{
164 QV4::Scope scope(b);
165 QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>());
166 if (!o)
167 return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"));
168 if (!argc)
169 return scope.engine->throwTypeError();
170
171 static_cast<QQmlDMAbstractItemModelData *>(o->d()->item)->setModelData(
172 QV4::ExecutionEngine::toVariant(value: argv[0], typeHint: QMetaType()));
173
174 return QV4::Encode::undefined();
175}
176
177QVariant QQmlDMAbstractItemModelData::modelData() const
178{
179 if (m_type->propertyRoles.size() == 1) {
180 // If the model has only a single role, the modelData is that role.
181 return index == -1
182 ? m_cachedData.isEmpty() ? QVariant() : m_cachedData[0]
183 : value(role: m_type->propertyRoles[0]);
184 }
185
186 return useStructuredModelData
187 ? QVariant::fromValue(value: this)
188 : QVariant();
189}
190
191void QQmlDMAbstractItemModelData::setModelData(const QVariant &modelData)
192{
193 if (m_type->propertyRoles.size() != 1) {
194 qWarning() << "Cannot overwrite model object";
195 return;
196 }
197
198 // If the model has only a single role, the modelData is that role.
199 if (index == -1) {
200 if (m_cachedData.isEmpty())
201 m_cachedData.append(t: modelData);
202 else
203 m_cachedData[0] = modelData;
204 } else {
205 setValue(role: m_type->propertyRoles[0], value: modelData);
206 }
207
208 QMetaObject::activate(sender: this, metaObject(), local_signal_index: 0, argv: nullptr);
209 emit modelDataChanged();
210}
211
212bool QQmlDMAbstractItemModelData::hasModelChildren() const
213{
214 if (index >= 0) {
215 if (const QAbstractItemModel *const model = m_type->model->aim())
216 return model->hasChildren(parent: model->index(row, column, parent: m_type->model->rootIndex));
217 }
218 return false;
219}
220
221QVariant QQmlDMAbstractItemModelData::value(int role) const
222{
223 if (const QAbstractItemModel *aim = m_type->model->aim())
224 return aim->index(row, column, parent: m_type->model->rootIndex).data(arole: role);
225 return QVariant();
226}
227
228void QQmlDMAbstractItemModelData::setValue(int role, const QVariant &value)
229{
230 if (QAbstractItemModel *aim = m_type->model->aim())
231 aim->setData(index: aim->index(row, column, parent: m_type->model->rootIndex), value, role);
232}
233
234QV4::ReturnedValue QQmlDMAbstractItemModelData::get()
235{
236 QV4::Scope scope(metaType->v4Engine);
237 if (m_type->prototype.isUndefined()) {
238 QQmlAdaptorModelEngineData *const data = QQmlAdaptorModelEngineData::get(engine: scope.engine);
239 m_type->initializeConstructor(data);
240 }
241 QV4::ScopedObject proto(scope, m_type->prototype.value());
242 QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocate<QQmlDelegateModelItemObject>(args: this));
243 o->setPrototypeOf(proto);
244 ++scriptRef;
245 return o.asReturnedValue();
246}
247
248QT_END_NAMESPACE
249

source code of qtdeclarative/src/qmlmodels/qqmldmabstractitemmodeldata.cpp