Revision control

++ // Copyright (C) 2016 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 reason:default
++
++ #include "qquickdesignersupportmetainfo_p.h"
++ #include "qquickdesignersupportproperties_p.h"
++
++ #include "qquickdesignercustomobjectdata_p.h"
++
++ #include <QGlobalStatic>
++ #include <QQmlContext>
++ #include <QQmlEngine>
++
++ #include <private/qqmlanybinding_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ using namespace Qt::StringLiterals;
++
++ typedef QHash<QObject*, QQuickDesignerCustomObjectData*> CustomObjectDataHash;
++ Q_GLOBAL_STATIC(CustomObjectDataHash, s_designerObjectToDataHash)
++
++ struct HandleDestroyedFunctor {
++ QQuickDesignerCustomObjectData *data;
++ void operator()() { data->handleDestroyed(); }
++ };
++
++ QQuickDesignerCustomObjectData::QQuickDesignerCustomObjectData(QObject *object)
++ : m_object(object)
++ {
++ if (object) {
++ populateResetHashes();
++ s_designerObjectToDataHash()->insert(object, this);
++
++ QObject::connect(object, &QObject::destroyed,
++ object, HandleDestroyedFunctor{this});
++ }
++ }
++
++ void QQuickDesignerCustomObjectData::registerData(QObject *object)
++ {
++ new QQuickDesignerCustomObjectData(object);
++ }
++
++ QQuickDesignerCustomObjectData *QQuickDesignerCustomObjectData::get(QObject *object)
++ {
++ return s_designerObjectToDataHash()->value(object);
++ }
++
++ QVariant QQuickDesignerCustomObjectData::getResetValue(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName)
++ {
++ QQuickDesignerCustomObjectData* data = get(object);
++
++ if (data)
++ return data->getResetValue(propertyName);
++
++ return QVariant();
++ }
++
++ void QQuickDesignerCustomObjectData::doResetProperty(QObject *object, QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName)
++ {
++ QQuickDesignerCustomObjectData* data = get(object);
++
++ if (data)
++ data->doResetProperty(context, propertyName);
++ }
++
++ bool QQuickDesignerCustomObjectData::hasValidResetBinding(QObject *object, const QQuickDesignerSupport::PropertyName &propertyName)
++ {
++ QQuickDesignerCustomObjectData* data = get(object);
++
++ if (data)
++ return data->hasValidResetBinding(propertyName);
++
++ return false;
++ }
++
++ bool QQuickDesignerCustomObjectData::hasBindingForProperty(QObject *object,
++ QQmlContext *context,
++ const QQuickDesignerSupport::PropertyName &propertyName,
++ bool *hasChanged)
++ {
++ QQuickDesignerCustomObjectData* data = get(object);
++
++ if (data)
++ return data->hasBindingForProperty(context, propertyName, hasChanged);
++
++ return false;
++ }
++
++ void QQuickDesignerCustomObjectData::setPropertyBinding(QObject *object,
++ QQmlContext *context,
++ const QQuickDesignerSupport::PropertyName &propertyName,
++ const QString &expression)
++ {
++ QQuickDesignerCustomObjectData* data = get(object);
++
++ if (data)
++ data->setPropertyBinding(context, propertyName, expression);
++ }
++
++ void QQuickDesignerCustomObjectData::keepBindingFromGettingDeleted(QObject *object,
++ QQmlContext *context,
++ const QQuickDesignerSupport::PropertyName &propertyName)
++ {
++ QQuickDesignerCustomObjectData* data = get(object);
++
++ if (data)
++ data->keepBindingFromGettingDeleted(context, propertyName);
++ }
++
++ void QQuickDesignerCustomObjectData::populateResetHashes()
++ {
++ const QQuickDesignerSupport::PropertyNameList propertyNameList =
++ QQuickDesignerSupportProperties::propertyNameListForWritableProperties(object());
++
++ const QMetaObject *mo = object()->metaObject();
++ QByteArrayList deferredPropertyNames;
++ const int namesIndex = mo->indexOfClassInfo("DeferredPropertyNames");
++ if (namesIndex != -1) {
++ QMetaClassInfo classInfo = mo->classInfo(namesIndex);
++ deferredPropertyNames = QByteArray(classInfo.value()).split(',');
++ }
++
++ for (const QQuickDesignerSupport::PropertyName &propertyName : propertyNameList) {
++
++ if (deferredPropertyNames.contains(propertyName))
++ continue;
++
++ QQmlProperty property(object(), QString::fromUtf8(propertyName), QQmlEngine::contextForObject(object()));
++
++ auto binding = QQmlAnyBinding::ofProperty(property);
++
++ if (binding) {
++ m_resetBindingHash.insert(propertyName, binding);
++ } else if (property.isWritable()) {
++ m_resetValueHash.insert(propertyName, property.read());
++ }
++ }
++ }
++
++ QObject *QQuickDesignerCustomObjectData::object() const
++ {
++ return m_object;
++ }
++
++ QVariant QQuickDesignerCustomObjectData::getResetValue(const QQuickDesignerSupport::PropertyName &propertyName) const
++ {
++ return m_resetValueHash.value(propertyName);
++ }
++
++ void QQuickDesignerCustomObjectData::doResetProperty(QQmlContext *context, const QQuickDesignerSupport::PropertyName &propertyName)
++ {
++ QQmlProperty property(object(), QString::fromUtf8(propertyName), context);
++
++ if (!property.isValid())
++ return;
++
++ // remove existing binding
++ QQmlAnyBinding::takeFrom(property);
++
++
++ if (hasValidResetBinding(propertyName)) {
++ QQmlAnyBinding binding = getResetBinding(propertyName);
++ binding.installOn(property);
++
++ if (binding.isAbstractPropertyBinding()) {
++ // for new style properties, we will evaluate during setBinding anyway
++ static_cast<QQmlBinding *>(binding.asAbstractBinding())->update();
++ }
++
++ } else if (property.isResettable()) {
++ property.reset();
++ } else if (property.propertyTypeCategory() == QQmlProperty::List) {
++ QQmlListReference list = qvariant_cast<QQmlListReference>(property.read());
++
++ if (!QQuickDesignerSupportProperties::hasFullImplementedListInterface(list)) {
++ qWarning() << "Property list interface not fully implemented for Class " << property.property().typeName() << " in property " << property.name() << "!";
++ return;
++ }
++
++ list.clear();
++ } else if (property.isWritable()) {
++ if (property.read() == getResetValue(propertyName))
++ return;
++
++ property.write(getResetValue(propertyName));
++ }
++ }
++
++ bool QQuickDesignerCustomObjectData::hasValidResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const
++ {
++ return m_resetBindingHash.contains(propertyName) && m_resetBindingHash.value(propertyName);
++ }
++
++ QQmlAnyBinding QQuickDesignerCustomObjectData::getResetBinding(const QQuickDesignerSupport::PropertyName &propertyName) const
++ {
++ return m_resetBindingHash.value(propertyName);
++ }
++
++ bool QQuickDesignerCustomObjectData::hasBindingForProperty(QQmlContext *context,
++ const QQuickDesignerSupport::PropertyName &propertyName,
++ bool *hasChanged) const
++ {
++ if (QQuickDesignerSupportProperties::isPropertyBlackListed(propertyName))
++ return false;
++
++ QQmlProperty property(object(), QString::fromUtf8(propertyName), context);
++
++ bool hasBinding = QQmlAnyBinding::ofProperty(property);
++
++ if (hasChanged) {
++ *hasChanged = hasBinding != m_hasBindingHash.value(propertyName, false);
++ if (*hasChanged)
++ m_hasBindingHash.insert(propertyName, hasBinding);
++ }
++
++ return hasBinding;
++ }
++
++ void QQuickDesignerCustomObjectData::setPropertyBinding(QQmlContext *context,
++ const QQuickDesignerSupport::PropertyName &propertyName,
++ const QString &expression)
++ {
++ QQmlProperty property(object(), QString::fromUtf8(propertyName), context);
++
++ if (!property.isValid())
++ return;
++
++ if (property.isProperty()) {
++ QString url = u"@designer"_s;
++ int lineNumber = 0;
++ QQmlAnyBinding binding = QQmlAnyBinding::createFromCodeString(property,
++ expression, object(), QQmlContextData::get(context), url, lineNumber);
++
++ binding.installOn(property);
++ if (binding.isAbstractPropertyBinding()) {
++ // for new style properties, we will evaluate during setBinding anyway
++ static_cast<QQmlBinding *>(binding.asAbstractBinding())->update();
++ }
++
++ if (binding.hasError()) {
++ if (property.property().userType() == QMetaType::QString)
++ property.write(QVariant(QLatin1Char('#') + expression + QLatin1Char('#')));
++ }
++
++ } else {
++ qWarning() << Q_FUNC_INFO << ": Cannot set binding for property" << propertyName << ": property is unknown for type";
++ }
++ }
++
++ void QQuickDesignerCustomObjectData::keepBindingFromGettingDeleted(QQmlContext *context,
++ const QQuickDesignerSupport::PropertyName &propertyName)
++ {
++ //Refcounting is taking care
++ Q_UNUSED(context);
++ Q_UNUSED(propertyName);
++ }
++
++ void QQuickDesignerCustomObjectData::handleDestroyed()
++ {
++ s_designerObjectToDataHash()->remove(m_object);
++ delete this;
++ }
++
++ QT_END_NAMESPACE
++