Files
qt-creator/share/qtcreator/qml/qmlpuppet/qml2puppet/instances/nodeinstancemetaobject.cpp
Thomas Hartmann 2a624649af Support for QQmlVMEMetaObject and QmlPropertyCache
We have to derive NodeInstanceMetaObject from QQmlVMEMetaObject
to support e. g. alias properties.
The QmlEngine uses QQmlVMEMetaObject to resolve
alias properties.
Also we have to invalidate/recreate the
QmlPropertyCache if we add dynamic properties.

Change-Id: Id52dd8b380eadeb6f74b5171168f2f70c16be118
Reviewed-by: Thomas Hartmann <Thomas.Hartmann@digia.com>
2012-10-23 16:37:42 +02:00

360 lines
12 KiB
C++

/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: http://www.qt-project.org/
**
**
** GNU Lesser General Public License Usage
**
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this file.
** Please review the following information to ensure the GNU Lesser General
** Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**************************************************************************/
#include "nodeinstancemetaobject.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 {
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);
}
static QAbstractDynamicMetaObject *abstractDynamicMetaObject(QObject *object)
{
QObjectPrivate *op = QObjectPrivate::get(object);
if (op->metaObject)
return static_cast<QAbstractDynamicMetaObject *>(op->metaObject);
return const_cast<QAbstractDynamicMetaObject *>(static_cast<const QAbstractDynamicMetaObject *>(object->metaObject()));
}
NodeInstanceMetaObject *NodeInstanceMetaObject::createNodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QQmlEngine *engine)
{
//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<NodeInstanceMetaObject *>(parent);
return new NodeInstanceMetaObject(nodeInstance, engine);
}
NodeInstanceMetaObject *NodeInstanceMetaObject::createNodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QObject *object, const QString &prefix, QQmlEngine *engine)
{
//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<NodeInstanceMetaObject *>(parent);
return new NodeInstanceMetaObject(nodeInstance, object, prefix, engine);
}
void NodeInstanceMetaObject::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);
//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);
}
NodeInstanceMetaObject::NodeInstanceMetaObject(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) {
ddata->propertyCache = m_cache;
}
}
NodeInstanceMetaObject::NodeInstanceMetaObject(const ObjectNodeInstancePointer &nodeInstance, QObject *object, const QString &prefix, QQmlEngine *engine)
: QQmlVMEMetaObject(object, cacheForObject(object, engine), vMEMetaDataForObject(object)),
m_nodeInstance(nodeInstance),
m_context(engine->contextForObject(object)),
m_prefix(prefix),
m_data(new MetaPropertyData),
m_cache(0)
{
init(object, engine);
}
NodeInstanceMetaObject::~NodeInstanceMetaObject()
{
m_type->release();
nodeInstanceMetaObjectList.remove(this);
}
void NodeInstanceMetaObject::createNewProperty(const QString &name)
{
int id = createProperty(name.toLatin1(), 0);
setValue(id, QVariant());
Q_ASSERT(id >= 0);
Q_UNUSED(id);
//Updating cache
QQmlEnginePrivate::get(m_context->engine())->cache(this)->invalidate(m_context->engine(), this);
QQmlProperty property(myObject(), name, m_context);
Q_ASSERT(property.isValid());
}
int NodeInstanceMetaObject::createProperty(const char *name, const char *)
{
int id = m_type->createProperty(name);
copyTypeMetaObject();
return id;
}
void NodeInstanceMetaObject::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 NodeInstanceMetaObject::propertyWriteValue(int, const QVariant &value)
{
return value;
}
int NodeInstanceMetaObject::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 NodeInstanceMetaObject::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 (metaCallReturnValue >= 0
&& call == QMetaObject::WriteProperty
&& !propertyById.hasNotifySignal()
&& oldValue != propertyById.read(myObject()))
notifyPropertyChange(id);
return metaCallReturnValue;
}
void NodeInstanceMetaObject::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(), m_prefix + propertyById.name());
} else {
objectNodeInstance->nodeInstanceServer()->notifyPropertyChange(objectNodeInstance->instanceId(), m_prefix + name(id - propertyOffset()));
}
}
}
int NodeInstanceMetaObject::count() const
{
return m_type->propertyCount();
}
QByteArray NodeInstanceMetaObject::name(int idx) const
{
return m_type->propertyName(idx);
}
void NodeInstanceMetaObject::copyTypeMetaObject()
{
*static_cast<QMetaObject *>(this) = *m_type->metaObject();
}
} // namespace Internal
} // namespace QmlDesigner