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 | |
6 | QT_BEGIN_NAMESPACE |
7 | |
8 | QQmlDMAbstractItemModelData::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 | |
22 | int 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 | |
59 | void 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 | |
81 | bool 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 | |
98 | QV4::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 | |
118 | QV4::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 | |
146 | QV4::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 | |
163 | QV4::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 | |
180 | QVariant 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 | |
196 | void 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 | |
217 | bool 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 | |
226 | QVariant 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 | |
233 | void 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 | |
239 | QV4::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 | |
253 | QT_END_NAMESPACE |
254 | |