Files
qt-creator/share/qtcreator/qml/qmlpuppet/qmlprivategate/metaobject.cpp
Thomas Hartmann e0a3e25491 Qml2Puppet: Introduce QmlPrivateGate
The goal is to avoid direct usage of private API.
Any access to private QML API is supposed to go through QmlPrivateGate.

NodeInstanceMetaObject is moved into QmlPrivateGate and renamed to just
MetaObject.

Change-Id: Iba0b574605e455b01e7b86306ec3ca47be7b4e58
Reviewed-by: Tim Jenssen <tim.jenssen@theqtcompany.com>
2015-05-19 12:04:41 +00:00

358 lines
12 KiB
C++

/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms and
** conditions see http://www.qt.io/terms-conditions. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "metaobject.h"
#include <objectnodeinstance.h>
#include <QSharedPointer>
#include <QMetaProperty>
#include <qnumeric.h>
#include <QDebug>
#include <private/qqmlengine_p.h>
#include <private/qqmlpropertycache_p.h>
namespace QmlDesigner {
namespace Internal {
namespace QmlPrivateGate {
static QHash<QDynamicMetaObjectData *, bool> nodeInstanceMetaObjectList;
struct MetaPropertyData {
inline QPair<QVariant, bool> &getDataRef(int idx) {
while (m_data.count() <= idx)
m_data << QPair<QVariant, bool>(QVariant(), false);
return m_data[idx];
}
inline QVariant &getData(int idx) {
QPair<QVariant, bool> &prop = getDataRef(idx);
if (!prop.second) {
prop.first = QVariant();
prop.second = true;
}
return prop.first;
}
inline bool hasData(int idx) const {
if (idx >= m_data.count())
return false;
return m_data[idx].second;
}
inline int count() { return m_data.count(); }
QList<QPair<QVariant, bool> > m_data;
};
static bool constructedMetaData(const QQmlVMEMetaData* data)
{
return data->varPropertyCount == 0
&& data->propertyCount == 0
&& data->aliasCount == 0
&& data->signalCount == 0
&& data->methodCount == 0;
}
static QQmlVMEMetaData* fakeMetaData()
{
QQmlVMEMetaData* data = new QQmlVMEMetaData;
data->varPropertyCount = 0;
data->propertyCount = 0;
data->aliasCount = 0;
data->signalCount = 0;
data->methodCount = 0;
return data;
}
static const QQmlVMEMetaData* vMEMetaDataForObject(QObject *object)
{
QQmlVMEMetaObject *metaObject = QQmlVMEMetaObject::get(object);
if (metaObject)
return metaObject->metaData;
return fakeMetaData();
}
static QQmlPropertyCache *cacheForObject(QObject *object, QQmlEngine *engine)
{
QQmlVMEMetaObject *metaObject = QQmlVMEMetaObject::get(object);
if (metaObject)
return metaObject->cache;
return QQmlEnginePrivate::get(engine)->cache(object);
}
MetaObject* MetaObject::getNodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance)
{
//Avoid setting up multiple NodeInstanceMetaObjects on the same QObject
QObjectPrivate *op = QObjectPrivate::get(nodeInstance->object());
QDynamicMetaObjectData *parent = op->metaObject;
if (nodeInstanceMetaObjectList.contains(parent))
return static_cast<MetaObject *>(parent);
// we just create one and the ownership goes automatically to the object in nodeinstance see init method
return new MetaObject(nodeInstance, nodeInstance->nodeInstanceServer()->engine());
}
void MetaObject::init(QObject *object, QQmlEngine *engine)
{
//Creating QQmlOpenMetaObjectType
m_type = new QQmlOpenMetaObjectType(metaObjectParent(), engine);
m_type->addref();
//Assigning type to this
copyTypeMetaObject();
//Assign this to object
QObjectPrivate *op = QObjectPrivate::get(object);
op->metaObject = this;
//create cache
cache = m_cache = QQmlEnginePrivate::get(engine)->cache(this);
cache->addref();
//If our parent is not a VMEMetaObject we just se the flag to false again
if (constructedMetaData(metaData))
QQmlData::get(object)->hasVMEMetaObject = false;
nodeInstanceMetaObjectList.insert(this, true);
hasAssignedMetaObjectData = true;
}
MetaObject::MetaObject(const ObjectNodeInstance::Pointer &nodeInstance, QQmlEngine *engine)
: QQmlVMEMetaObject(nodeInstance->object(), cacheForObject(nodeInstance->object(), engine), vMEMetaDataForObject(nodeInstance->object())),
m_nodeInstance(nodeInstance),
m_context(engine->contextForObject(nodeInstance->object())),
m_data(new MetaPropertyData),
m_cache(0)
{
init(nodeInstance->object(), engine);
QQmlData *ddata = QQmlData::get(nodeInstance->object(), false);
//Assign cache to object
if (ddata && ddata->propertyCache) {
cache->setParent(ddata->propertyCache);
cache->invalidate(engine, this);
ddata->propertyCache = m_cache;
}
}
MetaObject::~MetaObject()
{
if (cache->count() > 1) // qml is crashing because the property cache is not removed from the engine
cache->release();
else
m_type->release();
nodeInstanceMetaObjectList.remove(this);
}
void MetaObject::createNewDynamicProperty(const QString &name)
{
int id = m_type->createProperty(name.toUtf8());
copyTypeMetaObject();
setValue(id, QVariant());
Q_ASSERT(id >= 0);
Q_UNUSED(id);
//Updating cache
QQmlPropertyCache *oldParent = m_cache->parent();
QQmlEnginePrivate::get(m_context->engine())->cache(this)->invalidate(m_context->engine(), this);
m_cache->setParent(oldParent);
QQmlProperty property(myObject(), name, m_context);
Q_ASSERT(property.isValid());
}
void MetaObject::setValue(int id, const QVariant &value)
{
QPair<QVariant, bool> &prop = m_data->getDataRef(id);
prop.first = propertyWriteValue(id, value);
prop.second = true;
QMetaObject::activate(myObject(), id + m_type->signalOffset(), 0);
}
QVariant MetaObject::propertyWriteValue(int, const QVariant &value)
{
return value;
}
const QAbstractDynamicMetaObject *MetaObject::dynamicMetaObjectParent() const
{
if (QQmlVMEMetaObject::parent.isT1())
return QQmlVMEMetaObject::parent.asT1()->toDynamicMetaObject(QQmlVMEMetaObject::object);
else
return 0;
}
const QMetaObject *MetaObject::metaObjectParent() const
{
if (QQmlVMEMetaObject::parent.isT1())
return QQmlVMEMetaObject::parent.asT1()->toDynamicMetaObject(QQmlVMEMetaObject::object);
return QQmlVMEMetaObject::parent.asT2();
}
int MetaObject::propertyOffset() const
{
return cache->propertyOffset();
}
int MetaObject::openMetaCall(QMetaObject::Call call, int id, void **a)
{
if ((call == QMetaObject::ReadProperty || call == QMetaObject::WriteProperty)
&& id >= m_type->propertyOffset()) {
int propId = id - m_type->propertyOffset();
if (call == QMetaObject::ReadProperty) {
//propertyRead(propId);
*reinterpret_cast<QVariant *>(a[0]) = m_data->getData(propId);
} else if (call == QMetaObject::WriteProperty) {
if (propId <= m_data->count() || m_data->m_data[propId].first != *reinterpret_cast<QVariant *>(a[0])) {
//propertyWrite(propId);
QPair<QVariant, bool> &prop = m_data->getDataRef(propId);
prop.first = propertyWriteValue(propId, *reinterpret_cast<QVariant *>(a[0]));
prop.second = true;
//propertyWritten(propId);
activate(myObject(), m_type->signalOffset() + propId, 0);
}
}
return -1;
} else {
QAbstractDynamicMetaObject *directParent = parent();
if (directParent)
return directParent->metaCall(call, id, a);
else
return myObject()->qt_metacall(call, id, a);
}
}
int MetaObject::metaCall(QMetaObject::Call call, int id, void **a)
{
int metaCallReturnValue = -1;
const QMetaProperty propertyById = QQmlVMEMetaObject::property(id);
if (call == QMetaObject::WriteProperty
&& propertyById.userType() == QMetaType::QVariant
&& reinterpret_cast<QVariant *>(a[0])->type() == QVariant::Double
&& qIsNaN(reinterpret_cast<QVariant *>(a[0])->toDouble())) {
return -1;
}
if (call == QMetaObject::WriteProperty
&& propertyById.userType() == QMetaType::Double
&& qIsNaN(*reinterpret_cast<double*>(a[0]))) {
return -1;
}
if (call == QMetaObject::WriteProperty
&& propertyById.userType() == QMetaType::Float
&& qIsNaN(*reinterpret_cast<float*>(a[0]))) {
return -1;
}
QVariant oldValue;
if (call == QMetaObject::WriteProperty && !propertyById.hasNotifySignal())
{
oldValue = propertyById.read(myObject());
}
ObjectNodeInstance::Pointer objectNodeInstance = m_nodeInstance.toStrongRef();
QAbstractDynamicMetaObject *directParent = parent();
if (directParent && id < directParent->propertyOffset()) {
metaCallReturnValue = directParent->metaCall(call, id, a);
} else {
openMetaCall(call, id, a);
}
if ((call == QMetaObject::WriteProperty || call == QMetaObject::ReadProperty) && metaCallReturnValue < 0) {
if (objectNodeInstance
&& objectNodeInstance->nodeInstanceServer()
&& objectNodeInstance->nodeInstanceServer()->dummyContextObject()
&& !(objectNodeInstance && !objectNodeInstance->isRootNodeInstance()
&& property(id).name() == QLatin1String("parent"))) {
QObject *contextDummyObject = objectNodeInstance->nodeInstanceServer()->dummyContextObject();
int properyIndex = contextDummyObject->metaObject()->indexOfProperty(propertyById.name());
if (properyIndex >= 0)
metaCallReturnValue = contextDummyObject->qt_metacall(call, properyIndex, a);
}
}
if (call == QMetaObject::WriteProperty
&& !propertyById.hasNotifySignal()
&& oldValue != propertyById.read(myObject()))
notifyPropertyChange(id);
return metaCallReturnValue;
}
void MetaObject::notifyPropertyChange(int id)
{
ObjectNodeInstance::Pointer objectNodeInstance = m_nodeInstance.toStrongRef();
const QMetaProperty propertyById = property(id);
if (objectNodeInstance && objectNodeInstance->nodeInstanceServer()) {
if (id < propertyOffset()) {
objectNodeInstance->nodeInstanceServer()->notifyPropertyChange(objectNodeInstance->instanceId(), propertyById.name());
} else {
objectNodeInstance->nodeInstanceServer()->notifyPropertyChange(objectNodeInstance->instanceId(), name(id - propertyOffset()));
}
}
}
int MetaObject::count() const
{
return m_type->propertyCount();
}
QByteArray MetaObject::name(int idx) const
{
return m_type->propertyName(idx);
}
void MetaObject::copyTypeMetaObject()
{
*static_cast<QMetaObject *>(this) = *m_type->metaObject();
}
} // namespace QmlPrivateGate
} // namespace Internal
} // namespace QmlDesigner