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
#ifndef QQMLDMOBJECTDATA_P_H
#define QQMLDMOBJECTDATA_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <private/qqmladaptormodelenginedata_p.h>
#include <private/qqmldelegatemodel_p_p.h>
#include <private/qobject_p.h>
#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
class VDMObjectDelegateDataType;
class QQmlDMObjectData : public QQmlDelegateModelItem
{
Q_OBJECT
Q_PROPERTY(QObject *modelData READ modelData NOTIFY modelDataChanged)
QT_ANONYMOUS_PROPERTY(QObject * READ modelData NOTIFY modelDataChanged FINAL)
public:
QQmlDMObjectData(
const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
VDMObjectDelegateDataType *dataType,
int index, int row, int column,
QObject *object);
void setModelData(QObject *modelData)
{
if (modelData == m_modelData)
return;
m_modelData = modelData;
emit modelDataChanged();
}
QObject *modelData() const { return m_modelData; }
QQmlRefPointer<QQmlContextData> initProxy() final;
Q_SIGNALS:
void modelDataChanged();
private:
QPointer<QObject> m_modelData;
};
class VDMObjectDelegateDataType final
: public QQmlRefCounted<VDMObjectDelegateDataType>,
public QQmlAdaptorModel::Accessors
{
public:
QMetaObjectBuilder builder;
QQmlAdaptorModel *model = nullptr;
int propertyOffset = 0;
int signalOffset = 0;
bool shared = false;
VDMObjectDelegateDataType(QQmlAdaptorModel *model)
: model(model)
, shared(true)
{
}
VDMObjectDelegateDataType(const VDMObjectDelegateDataType &type)
: builder(type.metaObject.data(), QMetaObjectBuilder::Properties
| QMetaObjectBuilder::Signals
| QMetaObjectBuilder::SuperClass
| QMetaObjectBuilder::ClassName)
, model(type.model)
, propertyOffset(type.propertyOffset)
, signalOffset(type.signalOffset)
{
builder.setFlags(MetaObjectFlag::DynamicMetaObject);
}
int rowCount(const QQmlAdaptorModel &model) const override
{
return model.list.count();
}
int columnCount(const QQmlAdaptorModel &) const override
{
return 1;
}
QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override
{
if (QObject *object = model.list.at(index).value<QObject *>())
return object->property(role.toUtf8());
return QVariant();
}
QQmlDelegateModelItem *createItem(
QQmlAdaptorModel &model,
const QQmlRefPointer<QQmlDelegateModelItemMetaType> &metaType,
int index, int row, int column) override
{
if (!metaObject)
initializeMetaObject();
return index >= 0 && index < model.list.count()
? new QQmlDMObjectData(metaType, this, index, row, column, qvariant_cast<QObject *>(model.list.at(index)))
: nullptr;
}
void initializeMetaObject()
{
QQmlAdaptorModelEngineData::setModelDataType<QQmlDMObjectData>(&builder, this);
metaObject.reset(builder.toMetaObject());
// Note: ATM we cannot create a shared property cache for this class, since each model
// object can have different properties. And to make those properties available to the
// delegate, QQmlDMObjectData makes use of a QAbstractDynamicMetaObject subclass
// (QQmlDMObjectDataMetaObject), which we cannot represent in a QQmlPropertyCache.
// By not having a shared property cache, revisioned properties in QQmlDelegateModelItem
// will always be available to the delegate, regardless of the import version.
}
void cleanup(QQmlAdaptorModel &) const override
{
release();
}
bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QList<int> &) const override
{
for (auto modelItem : items) {
const int modelItemIndex = modelItem->modelIndex();
if (modelItemIndex < index || modelItemIndex >= index + count)
continue;
auto objectModelItem = static_cast<QQmlDMObjectData *>(modelItem);
QObject *updatedModelData = qvariant_cast<QObject *>(
model.list.at(objectModelItem->modelIndex()));
objectModelItem->setModelData(updatedModelData);
}
return true;
}
};
class QQmlDMObjectDataMetaObject : public QAbstractDynamicMetaObject
{
public:
QQmlDMObjectDataMetaObject(QQmlDMObjectData *data, VDMObjectDelegateDataType *type)
: m_data(data)
, m_type(type)
{
QObjectPrivate *op = QObjectPrivate::get(m_data);
*static_cast<QMetaObject *>(this) = *type->metaObject;
op->metaObject = this;
m_type->addref();
}
~QQmlDMObjectDataMetaObject()
{
m_type->release();
}
int metaCall(QObject *o, QMetaObject::Call call, int id, void **arguments) override
{
Q_ASSERT(o == m_data);
Q_UNUSED(o);
static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount();
if (id >= m_type->propertyOffset
&& (call == QMetaObject::ReadProperty
|| call == QMetaObject::WriteProperty
|| call == QMetaObject::ResetProperty)) {
if (QObject *modelData = m_data->modelData()) {
QMetaObject::metacall(
modelData, call, id - m_type->propertyOffset + objectPropertyOffset,
arguments);
}
return -1;
} else if (id >= m_type->signalOffset && call == QMetaObject::InvokeMetaMethod) {
QMetaObject::activate(m_data, this, id - m_type->signalOffset, nullptr);
return -1;
} else {
return m_data->qt_metacall(call, id, arguments);
}
}
int createProperty(const char *name, const char *) override
{
QObject *modelData = m_data->modelData();
if (!modelData)
return -1;
const QMetaObject *metaObject = modelData->metaObject();
static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount();
const int previousPropertyCount = propertyCount() - propertyOffset();
int propertyIndex = metaObject->indexOfProperty(name);
if (propertyIndex == -1)
return -1;
if (previousPropertyCount + objectPropertyOffset == metaObject->propertyCount())
return propertyIndex + m_type->propertyOffset - objectPropertyOffset;
if (m_type->shared) {
VDMObjectDelegateDataType *type = m_type;
m_type = new VDMObjectDelegateDataType(*m_type);
type->release();
}
const int previousMethodCount = methodCount();
int notifierId = previousMethodCount - methodOffset();
for (int propertyId = previousPropertyCount; propertyId < metaObject->propertyCount() - objectPropertyOffset; ++propertyId) {
QMetaProperty property = metaObject->property(propertyId + objectPropertyOffset);
QMetaPropertyBuilder propertyBuilder;
if (property.hasNotifySignal()) {
m_type->builder.addSignal("__" + QByteArray::number(propertyId) + "()");
propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName(), notifierId);
++notifierId;
} else {
propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName());
}
const bool modelWritable = m_type->model->delegateModelAccess != QQmlDelegateModel::ReadOnly;
propertyBuilder.setWritable(modelWritable && property.isWritable());
propertyBuilder.setResettable(modelWritable && property.isResettable());
propertyBuilder.setConstant(property.isConstant());
}
m_type->metaObject.reset(m_type->builder.toMetaObject());
*static_cast<QMetaObject *>(this) = *m_type->metaObject;
notifierId = previousMethodCount;
for (int i = previousPropertyCount; i < metaObject->propertyCount() - objectPropertyOffset; ++i) {
QMetaProperty property = metaObject->property(i + objectPropertyOffset);
if (property.hasNotifySignal()) {
QQmlPropertyPrivate::connect(
modelData, property.notifySignalIndex(), m_data, notifierId);
++notifierId;
}
}
return propertyIndex + m_type->propertyOffset - objectPropertyOffset;
}
QQmlDMObjectData *m_data;
VDMObjectDelegateDataType *m_type;
};
QT_END_NAMESPACE
#endif // QQMLDMOBJECTDATA_P_H