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

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