Revision control

Copy as Markdown

// 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
// Qt-Security score:significant
#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 (!hasValidModelIndex()) {
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 (!hasValidModelIndex()) {
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);
}
emit modelDataChanged();
} else if (*m_type->model) {
setValue(m_type->propertyRoles.at(propertyIndex),
*static_cast<QVariant *>(arguments[0]));
}
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 (hasValidModelIndex())
return false;
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;
}
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;
QQmlDelegateModelItem *item = o->d()->item();
QQmlDMAbstractItemModelData *modelData = static_cast<QQmlDMAbstractItemModelData *>(item);
if (item->modelIndex() == -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;
QQmlDelegateModelItem *item = o->d()->item();
if (item->modelIndex() == -1) {
QQmlDMAbstractItemModelData *modelData = static_cast<QQmlDMAbstractItemModelData *>(item);
if (!modelData->m_cachedData.isEmpty()) {
if (modelData->m_cachedData.size() > 1) {
modelData->m_cachedData[propertyId]
= QV4::ExecutionEngine::toVariant(argv[0], QMetaType {});
QMetaObject::activate(item, item->metaObject(), propertyId, nullptr);
} else if (modelData->m_cachedData.size() == 1) {
modelData->m_cachedData[0] = QV4::ExecutionEngine::toVariant(argv[0], QMetaType {});
QMetaObject::activate(item, 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 hasValidModelIndex()
? value(m_type->propertyRoles[0])
: m_cachedData.isEmpty() ? QVariant() : m_cachedData[0];
}
return usesStructuredModelData()
? QVariant::fromValue(this)
: QVariant();
}
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 (hasValidModelIndex()) {
setValue(m_type->propertyRoles[0], modelData);
} else {
if (m_cachedData.isEmpty())
m_cachedData.append(modelData);
else
m_cachedData[0] = modelData;
}
QMetaObject::activate(this, metaObject(), 0, nullptr);
emit modelDataChanged();
}
bool QQmlDMAbstractItemModelData::hasModelChildren() const
{
if (hasValidModelIndex()) {
if (const QAbstractItemModel *const model = m_type->model->aim()) {
return model->hasChildren(
model->index(modelRow(), modelColumn(), m_type->model->rootIndex));
}
}
return false;
}
QVariant QQmlDMAbstractItemModelData::value(int role) const
{
if (const QAbstractItemModel *aim = m_type->model->aim())
return aim->index(modelRow(), modelColumn(), 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(modelRow(), modelColumn(), m_type->model->rootIndex), value, role);
}
QV4::ReturnedValue QQmlDMAbstractItemModelData::get()
{
QV4::Scope scope(metaType()->v4Engine);
if (m_type->prototype.isUndefined()) {
QQmlAdaptorModelEngineData *const data = QQmlAdaptorModelEngineData::get(scope.engine);
m_type->initializeConstructor(data);
}
QV4::ScopedObject proto(scope, m_type->prototype.value());
QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocate<QQmlDelegateModelItemObject>(this));
o->setPrototypeOf(proto);
return o.asReturnedValue();
}
QT_END_NAMESPACE