diff options
Diffstat (limited to 'src/qmlmodels/qqmldmabstractitemmodeldata.cpp')
-rw-r--r-- | src/qmlmodels/qqmldmabstractitemmodeldata.cpp | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/src/qmlmodels/qqmldmabstractitemmodeldata.cpp b/src/qmlmodels/qqmldmabstractitemmodeldata.cpp new file mode 100644 index 0000000000..bf4a0226b1 --- /dev/null +++ b/src/qmlmodels/qqmldmabstractitemmodeldata.cpp @@ -0,0 +1,253 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include <private/qqmldmabstractitemmodeldata_p.h> + +QT_BEGIN_NAMESPACE + +QQmlDMAbstractItemModelData::QQmlDMAbstractItemModelData( + const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType, + VDMAbstractItemModelDataType *dataType, int index, int row, int column) + : QQmlDelegateModelItem(metaType, dataType, index, row, column) + , m_type(dataType) +{ + if (index == -1) + m_cachedData.resize(m_type->propertyRoles.size()); + + QObjectPrivate::get(this)->metaObject = m_type; + + m_type->addref(); +} + +int QQmlDMAbstractItemModelData::metaCall(QMetaObject::Call call, int id, void **arguments) +{ + if (call == QMetaObject::ReadProperty && id >= m_type->propertyOffset) { + const int propertyIndex = id - m_type->propertyOffset; + if (index == -1) { + if (!m_cachedData.isEmpty()) + *static_cast<QVariant *>(arguments[0]) = m_cachedData.at(propertyIndex); + } else if (*m_type->model) { + *static_cast<QVariant *>(arguments[0]) = value(m_type->propertyRoles.at(propertyIndex)); + } + return -1; + } else if (call == QMetaObject::WriteProperty && id >= m_type->propertyOffset) { + const int propertyIndex = id - m_type->propertyOffset; + const QMetaObject *meta = metaObject(); + if (index == -1) { + if (m_cachedData.size() > 1) { + m_cachedData[propertyIndex] = *static_cast<QVariant *>(arguments[0]); + QMetaObject::activate(this, meta, propertyIndex, nullptr); + } else if (m_cachedData.size() == 1) { + m_cachedData[0] = *static_cast<QVariant *>(arguments[0]); + QMetaObject::activate(this, meta, 0, nullptr); + } + } else if (*m_type->model) { + QQmlGuard<QQmlDMAbstractItemModelData> guard(this); + setValue(m_type->propertyRoles.at(propertyIndex), *static_cast<QVariant *>(arguments[0])); + if (guard.isNull()) + return -1; + + QMetaObject::activate(this, meta, propertyIndex, nullptr); + } + emit modelDataChanged(); + return -1; + } else { + return qt_metacall(call, id, arguments); + } +} + +void QQmlDMAbstractItemModelData::setValue(const QString &role, const QVariant &value) +{ + // Used only for initialization of the cached data. Does not have to emit change signals. + + if (m_type->propertyRoles.size() == 1 + && (role.isEmpty() || role == QLatin1String("modelData"))) { + // If the model has only a single role, the modelData is that role. + m_cachedData[0] = value; + return; + } + + const auto it = m_type->roleNames.constFind(role.toUtf8()); + if (it != m_type->roleNames.cend()) { + for (int i = 0; i < m_type->propertyRoles.size(); ++i) { + if (m_type->propertyRoles.at(i) == *it) { + m_cachedData[i] = value; + return; + } + } + } +} + +bool QQmlDMAbstractItemModelData::resolveIndex(const QQmlAdaptorModel &adaptorModel, int idx) +{ + if (index == -1) { + Q_ASSERT(idx >= 0); + m_cachedData.clear(); + setModelIndex(idx, adaptorModel.rowAt(idx), adaptorModel.columnAt(idx)); + const QMetaObject *meta = metaObject(); + const int propertyCount = m_type->propertyRoles.size(); + for (int i = 0; i < propertyCount; ++i) + QMetaObject::activate(this, meta, i, nullptr); + emit modelDataChanged(); + return true; + } else { + return false; + } +} + +QV4::ReturnedValue QQmlDMAbstractItemModelData::get_property(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) +{ + QV4::Scope scope(b); + QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); + if (!o) + return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); + + const qsizetype propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index; + + QQmlDMAbstractItemModelData *modelData = static_cast<QQmlDMAbstractItemModelData *>(o->d()->item); + if (o->d()->item->index == -1) { + if (!modelData->m_cachedData.isEmpty()) + return scope.engine->fromVariant(modelData->m_cachedData.at(propertyId)); + } else if (*modelData->m_type->model) { + return scope.engine->fromVariant( + modelData->value(modelData->m_type->propertyRoles.at(propertyId))); + } + return QV4::Encode::undefined(); +} + +QV4::ReturnedValue QQmlDMAbstractItemModelData::set_property(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) +{ + QV4::Scope scope(b); + QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); + if (!o) + return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); + if (!argc) + return scope.engine->throwTypeError(); + + const qsizetype propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index; + + if (o->d()->item->index == -1) { + QQmlDMAbstractItemModelData *modelData = static_cast<QQmlDMAbstractItemModelData *>(o->d()->item); + if (!modelData->m_cachedData.isEmpty()) { + if (modelData->m_cachedData.size() > 1) { + modelData->m_cachedData[propertyId] + = QV4::ExecutionEngine::toVariant(argv[0], QMetaType {}); + QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), propertyId, nullptr); + } else if (modelData->m_cachedData.size() == 1) { + modelData->m_cachedData[0] = QV4::ExecutionEngine::toVariant(argv[0], QMetaType {}); + QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), 0, nullptr); + } + emit modelData->modelDataChanged(); + } + } + return QV4::Encode::undefined(); +} + +QV4::ReturnedValue QQmlDMAbstractItemModelData::get_modelData( + const QV4::FunctionObject *b, const QV4::Value *thisObject, + const QV4::Value *argv, int argc) +{ + Q_UNUSED(argv) + Q_UNUSED(argc) + + QV4::Scope scope(b); + QV4::Scoped<QQmlDelegateModelItemObject> o( + scope, thisObject->as<QQmlDelegateModelItemObject>()); + if (!o) + return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); + + return scope.engine->fromVariant( + static_cast<QQmlDMAbstractItemModelData *>(o->d()->item)->modelData()); +} + +QV4::ReturnedValue QQmlDMAbstractItemModelData::set_modelData( + const QV4::FunctionObject *b, const QV4::Value *thisObject, + const QV4::Value *argv, int argc) +{ + QV4::Scope scope(b); + QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); + if (!o) + return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); + if (!argc) + return scope.engine->throwTypeError(); + + static_cast<QQmlDMAbstractItemModelData *>(o->d()->item)->setModelData( + QV4::ExecutionEngine::toVariant(argv[0], QMetaType())); + + return QV4::Encode::undefined(); +} + +QVariant QQmlDMAbstractItemModelData::modelData() const +{ + if (m_type->propertyRoles.size() == 1) { + // If the model has only a single role, the modelData is that role. + return index == -1 + ? m_cachedData.isEmpty() ? QVariant() : m_cachedData[0] + : value(m_type->propertyRoles[0]); + } + + // If we're using context properties, the model object is also the context object. + // In that case we cannot provide modelData. Otherwise return the object itself as modelData. + return (contextData->contextObject() == this) + ? QVariant() + : QVariant::fromValue(this); +} + +void QQmlDMAbstractItemModelData::setModelData(const QVariant &modelData) +{ + if (m_type->propertyRoles.size() != 1) { + qWarning() << "Cannot overwrite model object"; + return; + } + + // If the model has only a single role, the modelData is that role. + if (index == -1) { + if (m_cachedData.isEmpty()) + m_cachedData.append(modelData); + else + m_cachedData[0] = modelData; + } else { + setValue(m_type->propertyRoles[0], modelData); + } + + QMetaObject::activate(this, metaObject(), 0, nullptr); + emit modelDataChanged(); +} + +bool QQmlDMAbstractItemModelData::hasModelChildren() const +{ + if (index >= 0) { + if (const QAbstractItemModel *const model = m_type->model->aim()) + return model->hasChildren(model->index(row, column, m_type->model->rootIndex)); + } + return false; +} + +QVariant QQmlDMAbstractItemModelData::value(int role) const +{ + if (const QAbstractItemModel *aim = m_type->model->aim()) + return aim->index(row, column, m_type->model->rootIndex).data(role); + return QVariant(); +} + +void QQmlDMAbstractItemModelData::setValue(int role, const QVariant &value) +{ + if (QAbstractItemModel *aim = m_type->model->aim()) + aim->setData(aim->index(row, column, m_type->model->rootIndex), value, role); +} + +QV4::ReturnedValue QQmlDMAbstractItemModelData::get() +{ + if (m_type->prototype.isUndefined()) { + QQmlAdaptorModelEngineData * const data = QQmlAdaptorModelEngineData::get(v4); + m_type->initializeConstructor(data); + } + QV4::Scope scope(v4); + QV4::ScopedObject proto(scope, m_type->prototype.value()); + QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocate<QQmlDelegateModelItemObject>(this)); + o->setPrototypeOf(proto); + ++scriptRef; + return o.asReturnedValue(); +} + +QT_END_NAMESPACE |