/**************************************************************************** | |
** | |
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). | |
** All rights reserved. | |
** Contact: Nokia Corporation (qt-info@nokia.com) | |
** | |
** This file is part of the QtDeclarative module of the Qt Toolkit. | |
** | |
** $QT_BEGIN_LICENSE:LGPL$ | |
** 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. | |
** | |
** GNU General Public License Usage | |
** Alternatively, this file may be used under the terms of the GNU General | |
** Public License version 3.0 as published by the Free Software Foundation | |
** and appearing in the file LICENSE.GPL included in the packaging of this | |
** file. Please review the following information to ensure the GNU General | |
** Public License version 3.0 requirements will be met: | |
** http://www.gnu.org/copyleft/gpl.html. | |
** | |
** 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. | |
** | |
** | |
** | |
** | |
** | |
** $QT_END_LICENSE$ | |
** | |
****************************************************************************/ | |
#include "qdeclarativeproperty.h" | |
#include "private/qdeclarativeproperty_p.h" | |
#include "qdeclarative.h" | |
#include "private/qdeclarativebinding_p.h" | |
#include "qdeclarativecontext.h" | |
#include "private/qdeclarativecontext_p.h" | |
#include "private/qdeclarativeboundsignal_p.h" | |
#include "qdeclarativeengine.h" | |
#include "private/qdeclarativeengine_p.h" | |
#include "private/qdeclarativedata_p.h" | |
#include "private/qdeclarativestringconverters_p.h" | |
#include "private/qdeclarativelist_p.h" | |
#include "private/qdeclarativecompiler_p.h" | |
#include "private/qdeclarativevmemetaobject_p.h" | |
#include <QStringList> | |
#include <QtCore/qdebug.h> | |
#include <math.h> | |
QT_BEGIN_NAMESPACE | |
/*! | |
\class QDeclarativeProperty | |
\since 4.7 | |
\brief The QDeclarativeProperty class abstracts accessing properties on objects created from QML. | |
As QML uses Qt's meta-type system all of the existing QMetaObject classes can be used to introspect | |
and interact with objects created by QML. However, some of the new features provided by QML - such | |
as type safety and attached properties - are most easily used through the QDeclarativeProperty class | |
that simplifies some of their natural complexity. | |
Unlike QMetaProperty which represents a property on a class type, QDeclarativeProperty encapsulates | |
a property on a specific object instance. To read a property's value, programmers create a | |
QDeclarativeProperty instance and call the read() method. Likewise to write a property value the | |
write() method is used. | |
For example, for the following QML code: | |
\qml | |
// MyItem.qml | |
import QtQuick 1.0 | |
Text { text: "A bit of text" } | |
\endqml | |
The \l Text object's properties could be accessed using QDeclarativeProperty, like this: | |
\code | |
#include <QDeclarativeProperty> | |
#include <QGraphicsObject> | |
... | |
QDeclarativeView view(QUrl::fromLocalFile("MyItem.qml")); | |
QDeclarativeProperty property(view.rootObject(), "font.pixelSize"); | |
qWarning() << "Current pixel size:" << property.read().toInt(); | |
property.write(24); | |
qWarning() << "Pixel size should now be 24:" << property.read().toInt(); | |
\endcode | |
*/ | |
/*! | |
Create an invalid QDeclarativeProperty. | |
*/ | |
QDeclarativeProperty::QDeclarativeProperty() | |
: d(0) | |
{ | |
} | |
/*! \internal */ | |
QDeclarativeProperty::~QDeclarativeProperty() | |
{ | |
if (d) | |
d->release(); | |
d = 0; | |
} | |
/*! | |
Creates a QDeclarativeProperty for the default property of \a obj. If there is no | |
default property, an invalid QDeclarativeProperty will be created. | |
*/ | |
QDeclarativeProperty::QDeclarativeProperty(QObject *obj) | |
: d(new QDeclarativePropertyPrivate) | |
{ | |
d->initDefault(obj); | |
} | |
/*! | |
Creates a QDeclarativeProperty for the default property of \a obj | |
using the \l{QDeclarativeContext} {context} \a ctxt. If there is | |
no default property, an invalid QDeclarativeProperty will be | |
created. | |
*/ | |
QDeclarativeProperty::QDeclarativeProperty(QObject *obj, QDeclarativeContext *ctxt) | |
: d(new QDeclarativePropertyPrivate) | |
{ | |
d->context = ctxt?QDeclarativeContextData::get(ctxt):0; | |
d->engine = ctxt?ctxt->engine():0; | |
d->initDefault(obj); | |
} | |
/*! | |
Creates a QDeclarativeProperty for the default property of \a obj | |
using the environment for instantiating QML components that is | |
provided by \a engine. If there is no default property, an | |
invalid QDeclarativeProperty will be created. | |
*/ | |
QDeclarativeProperty::QDeclarativeProperty(QObject *obj, QDeclarativeEngine *engine) | |
: d(new QDeclarativePropertyPrivate) | |
{ | |
d->context = 0; | |
d->engine = engine; | |
d->initDefault(obj); | |
} | |
/*! | |
Initialize from the default property of \a obj | |
*/ | |
void QDeclarativePropertyPrivate::initDefault(QObject *obj) | |
{ | |
if (!obj) | |
return; | |
QMetaProperty p = QDeclarativeMetaType::defaultProperty(obj); | |
core.load(p); | |
if (core.isValid()) | |
object = obj; | |
} | |
/*! | |
Creates a QDeclarativeProperty for the property \a name of \a obj. | |
*/ | |
QDeclarativeProperty::QDeclarativeProperty(QObject *obj, const QString &name) | |
: d(new QDeclarativePropertyPrivate) | |
{ | |
d->initProperty(obj, name); | |
if (!isValid()) d->object = 0; | |
} | |
/*! | |
Creates a QDeclarativeProperty for the property \a name of \a obj | |
using the \l{QDeclarativeContext} {context} \a ctxt. | |
*/ | |
QDeclarativeProperty::QDeclarativeProperty(QObject *obj, const QString &name, QDeclarativeContext *ctxt) | |
: d(new QDeclarativePropertyPrivate) | |
{ | |
d->context = ctxt?QDeclarativeContextData::get(ctxt):0; | |
d->engine = ctxt?ctxt->engine():0; | |
d->initProperty(obj, name); | |
if (!isValid()) { d->object = 0; d->context = 0; d->engine = 0; } | |
} | |
/*! | |
Creates a QDeclarativeProperty for the property \a name of \a obj | |
using the environment for instantiating QML components that is | |
provided by \a engine. | |
*/ | |
QDeclarativeProperty::QDeclarativeProperty(QObject *obj, const QString &name, QDeclarativeEngine *engine) | |
: d(new QDeclarativePropertyPrivate) | |
{ | |
d->context = 0; | |
d->engine = engine; | |
d->initProperty(obj, name); | |
if (!isValid()) { d->object = 0; d->context = 0; d->engine = 0; } | |
} | |
Q_GLOBAL_STATIC(QDeclarativeValueTypeFactory, qmlValueTypes); | |
void QDeclarativePropertyPrivate::initProperty(QObject *obj, const QString &name) | |
{ | |
if (!obj) return; | |
QDeclarativeTypeNameCache *typeNameCache = context?context->imports:0; | |
QStringList path = name.split(QLatin1Char('.')); | |
if (path.isEmpty()) return; | |
QObject *currentObject = obj; | |
// Everything up to the last property must be an "object type" property | |
for (int ii = 0; ii < path.count() - 1; ++ii) { | |
const QString &pathName = path.at(ii); | |
if (QDeclarativeTypeNameCache::Data *data = typeNameCache?typeNameCache->data(pathName):0) { | |
if (data->type) { | |
QDeclarativeAttachedPropertiesFunc func = data->type->attachedPropertiesFunction(); | |
if (!func) return; // Not an attachable type | |
currentObject = qmlAttachedPropertiesObjectById(data->type->attachedPropertiesId(), currentObject); | |
if (!currentObject) return; // Something is broken with the attachable type | |
} else { | |
Q_ASSERT(data->typeNamespace); | |
if ((ii + 1) == path.count()) return; // No type following the namespace | |
++ii; data = data->typeNamespace->data(path.at(ii)); | |
if (!data || !data->type) return; // Invalid type in namespace | |
QDeclarativeAttachedPropertiesFunc func = data->type->attachedPropertiesFunction(); | |
if (!func) return; // Not an attachable type | |
currentObject = qmlAttachedPropertiesObjectById(data->type->attachedPropertiesId(), currentObject); | |
if (!currentObject) return; // Something is broken with the attachable type | |
} | |
} else { | |
QDeclarativePropertyCache::Data local; | |
QDeclarativePropertyCache::Data *property = | |
QDeclarativePropertyCache::property(engine, obj, pathName, local); | |
if (!property) return; // Not a property | |
if (property->flags & QDeclarativePropertyCache::Data::IsFunction) | |
return; // Not an object property | |
if (ii == (path.count() - 2) && QDeclarativeValueTypeFactory::isValueType(property->propType)) { | |
// We're now at a value type property. We can use a global valuetypes array as we | |
// never actually use the objects, just look up their properties. | |
QObject *typeObject = (*qmlValueTypes())[property->propType]; | |
if (!typeObject) return; // Not a value type | |
int idx = typeObject->metaObject()->indexOfProperty(path.last().toUtf8().constData()); | |
if (idx == -1) return; // Value type property does not exist | |
QMetaProperty vtProp = typeObject->metaObject()->property(idx); | |
object = currentObject; | |
core = *property; | |
valueType.flags = QDeclarativePropertyCache::Data::flagsForProperty(vtProp); | |
valueType.valueTypeCoreIdx = idx; | |
valueType.valueTypePropType = vtProp.userType(); | |
return; | |
} else { | |
if (!(property->flags & QDeclarativePropertyCache::Data::IsQObjectDerived)) | |
return; // Not an object property | |
void *args[] = { ¤tObject, 0 }; | |
QMetaObject::metacall(currentObject, QMetaObject::ReadProperty, property->coreIndex, args); | |
if (!currentObject) return; // No value | |
} | |
} | |
} | |
const QString &terminal = path.last(); | |
if (terminal.count() >= 3 && | |
terminal.at(0) == QLatin1Char('o') && | |
terminal.at(1) == QLatin1Char('n') && | |
terminal.at(2).isUpper()) { | |
QString signalName = terminal.mid(2); | |
signalName[0] = signalName.at(0).toLower(); | |
QMetaMethod method = findSignalByName(currentObject->metaObject(), signalName.toLatin1().constData()); | |
if (method.signature()) { | |
object = currentObject; | |
core.load(method); | |
return; | |
} | |
} | |
// Property | |
QDeclarativePropertyCache::Data local; | |
QDeclarativePropertyCache::Data *property = | |
QDeclarativePropertyCache::property(engine, currentObject, terminal, local); | |
if (property && !(property->flags & QDeclarativePropertyCache::Data::IsFunction)) { | |
object = currentObject; | |
core = *property; | |
nameCache = terminal; | |
isNameCached = true; | |
} | |
} | |
/*! | |
Create a copy of \a other. | |
*/ | |
QDeclarativeProperty::QDeclarativeProperty(const QDeclarativeProperty &other) | |
{ | |
d = other.d; | |
if (d) | |
d->addref(); | |
} | |
/*! | |
\enum QDeclarativeProperty::PropertyTypeCategory | |
This enum specifies a category of QML property. | |
\value InvalidCategory The property is invalid, or is a signal property. | |
\value List The property is a QDeclarativeListProperty list property | |
\value Object The property is a QObject derived type pointer | |
\value Normal The property is a normal value property. | |
*/ | |
/*! | |
\enum QDeclarativeProperty::Type | |
This enum specifies a type of QML property. | |
\value Invalid The property is invalid. | |
\value Property The property is a regular Qt property. | |
\value SignalProperty The property is a signal property. | |
*/ | |
/*! | |
Returns the property category. | |
*/ | |
QDeclarativeProperty::PropertyTypeCategory QDeclarativeProperty::propertyTypeCategory() const | |
{ | |
return d ? d->propertyTypeCategory() : InvalidCategory; | |
} | |
QDeclarativeProperty::PropertyTypeCategory | |
QDeclarativePropertyPrivate::propertyTypeCategory() const | |
{ | |
uint type = this->type(); | |
if (isValueType()) { | |
return QDeclarativeProperty::Normal; | |
} else if (type & QDeclarativeProperty::Property) { | |
int type = propertyType(); | |
if (type == QVariant::Invalid) | |
return QDeclarativeProperty::InvalidCategory; | |
else if (QDeclarativeValueTypeFactory::isValueType((uint)type)) | |
return QDeclarativeProperty::Normal; | |
else if (core.flags & QDeclarativePropertyCache::Data::IsQObjectDerived) | |
return QDeclarativeProperty::Object; | |
else if (core.flags & QDeclarativePropertyCache::Data::IsQList) | |
return QDeclarativeProperty::List; | |
else | |
return QDeclarativeProperty::Normal; | |
} else { | |
return QDeclarativeProperty::InvalidCategory; | |
} | |
} | |
/*! | |
Returns the type name of the property, or 0 if the property has no type | |
name. | |
*/ | |
const char *QDeclarativeProperty::propertyTypeName() const | |
{ | |
if (!d) | |
return 0; | |
if (d->isValueType()) { | |
QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(d->context); | |
QDeclarativeValueType *valueType = 0; | |
if (ep) valueType = ep->valueTypes[d->core.propType]; | |
else valueType = QDeclarativeValueTypeFactory::valueType(d->core.propType); | |
Q_ASSERT(valueType); | |
const char *rv = valueType->metaObject()->property(d->valueType.valueTypeCoreIdx).typeName(); | |
if (!ep) delete valueType; | |
return rv; | |
} else if (d->object && type() & Property && d->core.isValid()) { | |
return d->object->metaObject()->property(d->core.coreIndex).typeName(); | |
} else { | |
return 0; | |
} | |
} | |
/*! | |
Returns true if \a other and this QDeclarativeProperty represent the same | |
property. | |
*/ | |
bool QDeclarativeProperty::operator==(const QDeclarativeProperty &other) const | |
{ | |
if (!d || !other.d) | |
return false; | |
// category is intentially omitted here as it is generated | |
// from the other members | |
return d->object == other.d->object && | |
d->core == other.d->core && | |
d->valueType == other.d->valueType; | |
} | |
/*! | |
Returns the QVariant type of the property, or QVariant::Invalid if the | |
property has no QVariant type. | |
*/ | |
int QDeclarativeProperty::propertyType() const | |
{ | |
return d ? d->propertyType() : int(QVariant::Invalid); | |
} | |
bool QDeclarativePropertyPrivate::isValueType() const | |
{ | |
return valueType.valueTypeCoreIdx != -1; | |
} | |
int QDeclarativePropertyPrivate::propertyType() const | |
{ | |
uint type = this->type(); | |
if (isValueType()) { | |
return valueType.valueTypePropType; | |
} else if (type & QDeclarativeProperty::Property) { | |
if (core.propType == (int)QVariant::LastType) | |
return qMetaTypeId<QVariant>(); | |
else | |
return core.propType; | |
} else { | |
return QVariant::Invalid; | |
} | |
} | |
QDeclarativeProperty::Type QDeclarativePropertyPrivate::type() const | |
{ | |
if (core.flags & QDeclarativePropertyCache::Data::IsFunction) | |
return QDeclarativeProperty::SignalProperty; | |
else if (core.isValid()) | |
return QDeclarativeProperty::Property; | |
else | |
return QDeclarativeProperty::Invalid; | |
} | |
/*! | |
Returns the type of the property. | |
*/ | |
QDeclarativeProperty::Type QDeclarativeProperty::type() const | |
{ | |
return d ? d->type() : Invalid; | |
} | |
/*! | |
Returns true if this QDeclarativeProperty represents a regular Qt property. | |
*/ | |
bool QDeclarativeProperty::isProperty() const | |
{ | |
return type() & Property; | |
} | |
/*! | |
Returns true if this QDeclarativeProperty represents a QML signal property. | |
*/ | |
bool QDeclarativeProperty::isSignalProperty() const | |
{ | |
return type() & SignalProperty; | |
} | |
/*! | |
Returns the QDeclarativeProperty's QObject. | |
*/ | |
QObject *QDeclarativeProperty::object() const | |
{ | |
return d ? d->object : 0; | |
} | |
/*! | |
Assign \a other to this QDeclarativeProperty. | |
*/ | |
QDeclarativeProperty &QDeclarativeProperty::operator=(const QDeclarativeProperty &other) | |
{ | |
if (d) | |
d->release(); | |
d = other.d; | |
if (d) | |
d->addref(); | |
return *this; | |
} | |
/*! | |
Returns true if the property is writable, otherwise false. | |
*/ | |
bool QDeclarativeProperty::isWritable() const | |
{ | |
if (!d) | |
return false; | |
if (!d->object) | |
return false; | |
if (d->core.flags & QDeclarativePropertyCache::Data::IsQList) //list | |
return true; | |
else if (d->core.flags & QDeclarativePropertyCache::Data::IsFunction) //signal handler | |
return false; | |
else if (d->core.isValid()) //normal property | |
return d->core.flags & QDeclarativePropertyCache::Data::IsWritable; | |
else | |
return false; | |
} | |
/*! | |
Returns true if the property is designable, otherwise false. | |
*/ | |
bool QDeclarativeProperty::isDesignable() const | |
{ | |
if (!d) | |
return false; | |
if (type() & Property && d->core.isValid() && d->object) | |
return d->object->metaObject()->property(d->core.coreIndex).isDesignable(); | |
else | |
return false; | |
} | |
/*! | |
Returns true if the property is resettable, otherwise false. | |
*/ | |
bool QDeclarativeProperty::isResettable() const | |
{ | |
if (!d) | |
return false; | |
if (type() & Property && d->core.isValid() && d->object) | |
return d->core.flags & QDeclarativePropertyCache::Data::IsResettable; | |
else | |
return false; | |
} | |
/*! | |
Returns true if the QDeclarativeProperty refers to a valid property, otherwise | |
false. | |
*/ | |
bool QDeclarativeProperty::isValid() const | |
{ | |
if (!d) | |
return false; | |
return type() != Invalid; | |
} | |
/*! | |
Return the name of this QML property. | |
*/ | |
QString QDeclarativeProperty::name() const | |
{ | |
if (!d) | |
return QString(); | |
if (!d->isNameCached) { | |
// ### | |
if (!d->object) { | |
} else if (d->isValueType()) { | |
QString rv = d->core.name(d->object) + QLatin1Char('.'); | |
QDeclarativeEnginePrivate *ep = d->engine?QDeclarativeEnginePrivate::get(d->engine):0; | |
QDeclarativeValueType *valueType = 0; | |
if (ep) valueType = ep->valueTypes[d->core.propType]; | |
else valueType = QDeclarativeValueTypeFactory::valueType(d->core.propType); | |
Q_ASSERT(valueType); | |
rv += QString::fromUtf8(valueType->metaObject()->property(d->valueType.valueTypeCoreIdx).name()); | |
if (!ep) delete valueType; | |
d->nameCache = rv; | |
} else if (type() & SignalProperty) { | |
QString name = QLatin1String("on") + d->core.name(d->object); | |
name[2] = name.at(2).toUpper(); | |
d->nameCache = name; | |
} else { | |
d->nameCache = d->core.name(d->object); | |
} | |
d->isNameCached = true; | |
} | |
return d->nameCache; | |
} | |
/*! | |
Returns the \l{QMetaProperty} {Qt property} associated with | |
this QML property. | |
*/ | |
QMetaProperty QDeclarativeProperty::property() const | |
{ | |
if (!d) | |
return QMetaProperty(); | |
if (type() & Property && d->core.isValid() && d->object) | |
return d->object->metaObject()->property(d->core.coreIndex); | |
else | |
return QMetaProperty(); | |
} | |
/*! | |
Return the QMetaMethod for this property if it is a SignalProperty, | |
otherwise returns an invalid QMetaMethod. | |
*/ | |
QMetaMethod QDeclarativeProperty::method() const | |
{ | |
if (!d) | |
return QMetaMethod(); | |
if (type() & SignalProperty && d->object) | |
return d->object->metaObject()->method(d->core.coreIndex); | |
else | |
return QMetaMethod(); | |
} | |
/*! | |
Returns the binding associated with this property, or 0 if no binding | |
exists. | |
*/ | |
QDeclarativeAbstractBinding * | |
QDeclarativePropertyPrivate::binding(const QDeclarativeProperty &that) | |
{ | |
if (!that.d || !that.isProperty() || !that.d->object) | |
return 0; | |
return binding(that.d->object, that.d->core.coreIndex, that.d->valueType.valueTypeCoreIdx); | |
} | |
/*! | |
Set the binding associated with this property to \a newBinding. Returns | |
the existing binding (if any), otherwise 0. | |
\a newBinding will be enabled, and the returned binding (if any) will be | |
disabled. | |
Ownership of \a newBinding transfers to QML. Ownership of the return value | |
is assumed by the caller. | |
\a flags is passed through to the binding and is used for the initial update (when | |
the binding sets the initial value, it will use these flags for the write). | |
*/ | |
QDeclarativeAbstractBinding * | |
QDeclarativePropertyPrivate::setBinding(const QDeclarativeProperty &that, | |
QDeclarativeAbstractBinding *newBinding, | |
WriteFlags flags) | |
{ | |
if (!that.d || !that.isProperty() || !that.d->object) { | |
if (newBinding) | |
newBinding->destroy(); | |
return 0; | |
} | |
return that.d->setBinding(that.d->object, that.d->core.coreIndex, | |
that.d->valueType.valueTypeCoreIdx, newBinding, flags); | |
} | |
QDeclarativeAbstractBinding * | |
QDeclarativePropertyPrivate::binding(QObject *object, int coreIndex, int valueTypeIndex) | |
{ | |
QDeclarativeData *data = QDeclarativeData::get(object); | |
if (!data) | |
return 0; | |
QDeclarativePropertyCache::Data *propertyData = | |
data->propertyCache?data->propertyCache->property(coreIndex):0; | |
if (propertyData && propertyData->flags & QDeclarativePropertyCache::Data::IsAlias) { | |
const QDeclarativeVMEMetaObject *vme = | |
static_cast<const QDeclarativeVMEMetaObject *>(metaObjectForProperty(object->metaObject(), coreIndex)); | |
QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; | |
if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex) || aCoreIndex == -1) | |
return 0; | |
// This will either be a value type sub-reference or an alias to a value-type sub-reference not both | |
Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); | |
return binding(aObject, aCoreIndex, (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex); | |
} | |
if (!data->hasBindingBit(coreIndex)) | |
return 0; | |
QDeclarativeAbstractBinding *binding = data->bindings; | |
while (binding && binding->propertyIndex() != coreIndex) | |
binding = binding->m_nextBinding; | |
if (binding && valueTypeIndex != -1) { | |
if (binding->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy) { | |
int index = coreIndex | (valueTypeIndex << 24); | |
binding = static_cast<QDeclarativeValueTypeProxyBinding *>(binding)->binding(index); | |
} | |
} | |
return binding; | |
} | |
void QDeclarativePropertyPrivate::findAliasTarget(QObject *object, int bindingIndex, | |
QObject **targetObject, int *targetBindingIndex) | |
{ | |
int coreIndex = bindingIndex & 0xFFFFFF; | |
int valueTypeIndex = bindingIndex >> 24; | |
if (valueTypeIndex == 0) valueTypeIndex = -1; | |
QDeclarativeData *data = QDeclarativeData::get(object, false); | |
if (data) { | |
QDeclarativePropertyCache::Data *propertyData = | |
data->propertyCache?data->propertyCache->property(coreIndex):0; | |
if (propertyData && propertyData->flags & QDeclarativePropertyCache::Data::IsAlias) { | |
const QDeclarativeVMEMetaObject *vme = | |
static_cast<const QDeclarativeVMEMetaObject *>(metaObjectForProperty(object->metaObject(), coreIndex)); | |
QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; | |
if (vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { | |
// This will either be a value type sub-reference or an alias to a value-type sub-reference not both | |
Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); | |
int aBindingIndex = aCoreIndex; | |
if (aValueTypeIndex != -1) | |
aBindingIndex |= aValueTypeIndex << 24; | |
else if (valueTypeIndex != -1) | |
aBindingIndex |= valueTypeIndex << 24; | |
findAliasTarget(aObject, aBindingIndex, targetObject, targetBindingIndex); | |
return; | |
} | |
} | |
} | |
*targetObject = object; | |
*targetBindingIndex = bindingIndex; | |
} | |
QDeclarativeAbstractBinding * | |
QDeclarativePropertyPrivate::setBinding(QObject *object, int coreIndex, int valueTypeIndex, | |
QDeclarativeAbstractBinding *newBinding, WriteFlags flags) | |
{ | |
QDeclarativeData *data = QDeclarativeData::get(object, 0 != newBinding); | |
QDeclarativeAbstractBinding *binding = 0; | |
if (data) { | |
QDeclarativePropertyCache::Data *propertyData = | |
data->propertyCache?data->propertyCache->property(coreIndex):0; | |
if (propertyData && propertyData->flags & QDeclarativePropertyCache::Data::IsAlias) { | |
const QDeclarativeVMEMetaObject *vme = | |
static_cast<const QDeclarativeVMEMetaObject *>(metaObjectForProperty(object->metaObject(), coreIndex)); | |
QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; | |
if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { | |
if (newBinding) newBinding->destroy(); | |
return 0; | |
} | |
// This will either be a value type sub-reference or an alias to a value-type sub-reference not both | |
Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); | |
return setBinding(aObject, aCoreIndex, (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex, | |
newBinding, flags); | |
} | |
} | |
if (data && data->hasBindingBit(coreIndex)) { | |
binding = data->bindings; | |
while (binding && binding->propertyIndex() != coreIndex) | |
binding = binding->m_nextBinding; | |
} | |
int index = coreIndex; | |
if (valueTypeIndex != -1) | |
index |= (valueTypeIndex << 24); | |
if (binding && valueTypeIndex != -1 && binding->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy) | |
binding = static_cast<QDeclarativeValueTypeProxyBinding *>(binding)->binding(index); | |
if (binding) { | |
binding->removeFromObject(); | |
binding->setEnabled(false, 0); | |
} | |
if (newBinding) { | |
newBinding->addToObject(object, index); | |
newBinding->setEnabled(true, flags); | |
} | |
return binding; | |
} | |
QDeclarativeAbstractBinding * | |
QDeclarativePropertyPrivate::setBindingNoEnable(QObject *object, int coreIndex, int valueTypeIndex, | |
QDeclarativeAbstractBinding *newBinding) | |
{ | |
QDeclarativeData *data = QDeclarativeData::get(object, 0 != newBinding); | |
QDeclarativeAbstractBinding *binding = 0; | |
if (data) { | |
QDeclarativePropertyCache::Data *propertyData = | |
data->propertyCache?data->propertyCache->property(coreIndex):0; | |
if (propertyData && propertyData->flags & QDeclarativePropertyCache::Data::IsAlias) { | |
const QDeclarativeVMEMetaObject *vme = | |
static_cast<const QDeclarativeVMEMetaObject *>(metaObjectForProperty(object->metaObject(), coreIndex)); | |
QObject *aObject = 0; int aCoreIndex = -1; int aValueTypeIndex = -1; | |
if (!vme->aliasTarget(coreIndex, &aObject, &aCoreIndex, &aValueTypeIndex)) { | |
if (newBinding) newBinding->destroy(); | |
return 0; | |
} | |
// This will either be a value type sub-reference or an alias to a value-type sub-reference not both | |
Q_ASSERT(valueTypeIndex == -1 || aValueTypeIndex == -1); | |
return setBindingNoEnable(aObject, aCoreIndex, (valueTypeIndex == -1)?aValueTypeIndex:valueTypeIndex, | |
newBinding); | |
} | |
} | |
if (data && data->hasBindingBit(coreIndex)) { | |
binding = data->bindings; | |
while (binding && binding->propertyIndex() != coreIndex) | |
binding = binding->m_nextBinding; | |
} | |
int index = coreIndex; | |
if (valueTypeIndex != -1) | |
index |= (valueTypeIndex << 24); | |
if (binding && valueTypeIndex != -1 && binding->bindingType() == QDeclarativeAbstractBinding::ValueTypeProxy) | |
binding = static_cast<QDeclarativeValueTypeProxyBinding *>(binding)->binding(index); | |
if (binding) | |
binding->removeFromObject(); | |
if (newBinding) | |
newBinding->addToObject(object, index); | |
return binding; | |
} | |
/*! | |
Returns the expression associated with this signal property, or 0 if no | |
signal expression exists. | |
*/ | |
QDeclarativeExpression * | |
QDeclarativePropertyPrivate::signalExpression(const QDeclarativeProperty &that) | |
{ | |
if (!(that.type() & QDeclarativeProperty::SignalProperty)) | |
return 0; | |
const QObjectList &children = that.d->object->children(); | |
for (int ii = 0; ii < children.count(); ++ii) { | |
QObject *child = children.at(ii); | |
QDeclarativeBoundSignal *signal = QDeclarativeBoundSignal::cast(child); | |
if (signal && signal->index() == that.index()) | |
return signal->expression(); | |
} | |
return 0; | |
} | |
/*! | |
Set the signal expression associated with this signal property to \a expr. | |
Returns the existing signal expression (if any), otherwise 0. | |
Ownership of \a expr transfers to QML. Ownership of the return value is | |
assumed by the caller. | |
*/ | |
QDeclarativeExpression * | |
QDeclarativePropertyPrivate::setSignalExpression(const QDeclarativeProperty &that, | |
QDeclarativeExpression *expr) | |
{ | |
if (!(that.type() & QDeclarativeProperty::SignalProperty)) { | |
delete expr; | |
return 0; | |
} | |
const QObjectList &children = that.d->object->children(); | |
for (int ii = 0; ii < children.count(); ++ii) { | |
QObject *child = children.at(ii); | |
QDeclarativeBoundSignal *signal = QDeclarativeBoundSignal::cast(child); | |
if (signal && signal->index() == that.index()) | |
return signal->setExpression(expr); | |
} | |
if (expr) { | |
QDeclarativeBoundSignal *signal = new QDeclarativeBoundSignal(that.d->object, that.method(), that.d->object); | |
return signal->setExpression(expr); | |
} else { | |
return 0; | |
} | |
} | |
/*! | |
Returns the property value. | |
*/ | |
QVariant QDeclarativeProperty::read() const | |
{ | |
if (!d) | |
return QVariant(); | |
if (!d->object) | |
return QVariant(); | |
if (type() & SignalProperty) { | |
return QVariant(); | |
} else if (type() & Property) { | |
return d->readValueProperty(); | |
} | |
return QVariant(); | |
} | |
/*! | |
Return the \a name property value of \a object. This method is equivalent to: | |
\code | |
QDeclarativeProperty p(object, name); | |
p.read(); | |
\endcode | |
*/ | |
QVariant QDeclarativeProperty::read(QObject *object, const QString &name) | |
{ | |
QDeclarativeProperty p(object, name); | |
return p.read(); | |
} | |
/*! | |
Return the \a name property value of \a object using the | |
\l{QDeclarativeContext} {context} \a ctxt. This method is | |
equivalent to: | |
\code | |
QDeclarativeProperty p(object, name, context); | |
p.read(); | |
\endcode | |
*/ | |
QVariant QDeclarativeProperty::read(QObject *object, const QString &name, QDeclarativeContext *ctxt) | |
{ | |
QDeclarativeProperty p(object, name, ctxt); | |
return p.read(); | |
} | |
/*! | |
Return the \a name property value of \a object using the environment | |
for instantiating QML components that is provided by \a engine. . | |
This method is equivalent to: | |
\code | |
QDeclarativeProperty p(object, name, engine); | |
p.read(); | |
\endcode | |
*/ | |
QVariant QDeclarativeProperty::read(QObject *object, const QString &name, QDeclarativeEngine *engine) | |
{ | |
QDeclarativeProperty p(object, name, engine); | |
return p.read(); | |
} | |
QVariant QDeclarativePropertyPrivate::readValueProperty() | |
{ | |
if (isValueType()) { | |
QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context); | |
QDeclarativeValueType *valueType = 0; | |
if (ep) valueType = ep->valueTypes[core.propType]; | |
else valueType = QDeclarativeValueTypeFactory::valueType(core.propType); | |
Q_ASSERT(valueType); | |
valueType->read(object, core.coreIndex); | |
QVariant rv = | |
valueType->metaObject()->property(this->valueType.valueTypeCoreIdx).read(valueType); | |
if (!ep) delete valueType; | |
return rv; | |
} else if (core.flags & QDeclarativePropertyCache::Data::IsQList) { | |
QDeclarativeListProperty<QObject> prop; | |
void *args[] = { &prop, 0 }; | |
QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args); | |
return QVariant::fromValue(QDeclarativeListReferencePrivate::init(prop, core.propType, engine)); | |
} else if (core.flags & QDeclarativePropertyCache::Data::IsQObjectDerived) { | |
QObject *rv = 0; | |
void *args[] = { &rv, 0 }; | |
QMetaObject::metacall(object, QMetaObject::ReadProperty, core.coreIndex, args); | |
return QVariant::fromValue(rv); | |
} else { | |
return object->metaObject()->property(core.coreIndex).read(object.data()); | |
} | |
} | |
//writeEnumProperty MIRRORS the relelvant bit of QMetaProperty::write AND MUST BE KEPT IN SYNC! | |
bool QDeclarativePropertyPrivate::writeEnumProperty(const QMetaProperty &prop, int idx, QObject *object, const QVariant &value, int flags) | |
{ | |
if (!object || !prop.isWritable()) | |
return false; | |
QVariant v = value; | |
if (prop.isEnumType()) { | |
QMetaEnum menum = prop.enumerator(); | |
if (v.userType() == QVariant::String | |
#ifdef QT3_SUPPORT | |
|| v.userType() == QVariant::CString | |
#endif | |
) { | |
if (prop.isFlagType()) | |
v = QVariant(menum.keysToValue(value.toByteArray())); | |
else | |
v = QVariant(menum.keyToValue(value.toByteArray())); | |
} else if (v.userType() != QVariant::Int && v.userType() != QVariant::UInt) { | |
int enumMetaTypeId = QMetaType::type(QByteArray(menum.scope()) + "::" + menum.name()); | |
if ((enumMetaTypeId == 0) || (v.userType() != enumMetaTypeId) || !v.constData()) | |
return false; | |
v = QVariant(*reinterpret_cast<const int *>(v.constData())); | |
} | |
v.convert(QVariant::Int); | |
} | |
// the status variable is changed by qt_metacall to indicate what it did | |
// this feature is currently only used by QtDBus and should not be depended | |
// upon. Don't change it without looking into QDBusAbstractInterface first | |
// -1 (unchanged): normal qt_metacall, result stored in argv[0] | |
// changed: result stored directly in value, return the value of status | |
int status = -1; | |
void *argv[] = { v.data(), &v, &status, &flags }; | |
QMetaObject::metacall(object, QMetaObject::WriteProperty, idx, argv); | |
return status; | |
} | |
bool QDeclarativePropertyPrivate::writeValueProperty(const QVariant &value, WriteFlags flags) | |
{ | |
// Remove any existing bindings on this property | |
if (!(flags & DontRemoveBinding) && | |
(type() & QDeclarativeProperty::Property) && object) { | |
QDeclarativeAbstractBinding *binding = setBinding(object, core.coreIndex, | |
valueType.valueTypeCoreIdx, 0, flags); | |
if (binding) binding->destroy(); | |
} | |
bool rv = false; | |
if (isValueType()) { | |
QDeclarativeEnginePrivate *ep = QDeclarativeEnginePrivate::get(context); | |
QDeclarativeValueType *writeBack = 0; | |
if (ep) { | |
writeBack = ep->valueTypes[core.propType]; | |
} else { | |
writeBack = QDeclarativeValueTypeFactory::valueType(core.propType); | |
} | |
writeBack->read(object, core.coreIndex); | |
QDeclarativePropertyCache::Data data = core; | |
data.flags = valueType.flags; | |
data.coreIndex = valueType.valueTypeCoreIdx; | |
data.propType = valueType.valueTypePropType; | |
rv = write(writeBack, data, value, context, flags); | |
writeBack->write(object, core.coreIndex, flags); | |
if (!ep) delete writeBack; | |
} else { | |
rv = write(object, core, value, context, flags); | |
} | |
return rv; | |
} | |
bool QDeclarativePropertyPrivate::write(QObject *object, const QDeclarativePropertyCache::Data &property, | |
const QVariant &value, QDeclarativeContextData *context, | |
WriteFlags flags) | |
{ | |
int coreIdx = property.coreIndex; | |
int status = -1; //for dbus | |
if (property.flags & QDeclarativePropertyCache::Data::IsEnumType) { | |
QMetaProperty prop = object->metaObject()->property(property.coreIndex); | |
QVariant v = value; | |
// Enum values come through the script engine as doubles | |
if (value.userType() == QVariant::Double) { | |
double integral; | |
double fractional = modf(value.toDouble(), &integral); | |
if (qFuzzyIsNull(fractional)) | |
v.convert(QVariant::Int); | |
} | |
return writeEnumProperty(prop, coreIdx, object, v, flags); | |
} | |
int propertyType = property.propType; | |
int variantType = value.userType(); | |
QDeclarativeEnginePrivate *enginePriv = QDeclarativeEnginePrivate::get(context); | |
if (propertyType == QVariant::Url) { | |
QUrl u; | |
bool found = false; | |
if (variantType == QVariant::Url) { | |
u = value.toUrl(); | |
found = true; | |
} else if (variantType == QVariant::ByteArray) { | |
u = QUrl(QString::fromUtf8(value.toByteArray())); | |
found = true; | |
} else if (variantType == QVariant::String) { | |
u = QUrl(value.toString()); | |
found = true; | |
} | |
if (!found) | |
return false; | |
if (context && u.isRelative() && !u.isEmpty()) | |
u = context->resolvedUrl(u); | |
int status = -1; | |
void *argv[] = { &u, 0, &status, &flags }; | |
QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, argv); | |
} else if (variantType == propertyType) { | |
void *a[] = { (void *)value.constData(), 0, &status, &flags }; | |
QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); | |
} else if (qMetaTypeId<QVariant>() == propertyType) { | |
void *a[] = { (void *)&value, 0, &status, &flags }; | |
QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); | |
} else if (property.flags & QDeclarativePropertyCache::Data::IsQObjectDerived) { | |
const QMetaObject *valMo = rawMetaObjectForType(enginePriv, value.userType()); | |
if (!valMo) | |
return false; | |
QObject *o = *(QObject **)value.constData(); | |
const QMetaObject *propMo = rawMetaObjectForType(enginePriv, propertyType); | |
if (o) valMo = o->metaObject(); | |
if (canConvert(valMo, propMo)) { | |
void *args[] = { &o, 0, &status, &flags }; | |
QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, | |
args); | |
} else if (!o && canConvert(propMo, valMo)) { | |
// In the case of a null QObject, we assign the null if there is | |
// any change that the null variant type could be up or down cast to | |
// the property type. | |
void *args[] = { &o, 0, &status, &flags }; | |
QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, | |
args); | |
} else { | |
return false; | |
} | |
} else if (property.flags & QDeclarativePropertyCache::Data::IsQList) { | |
const QMetaObject *listType = 0; | |
if (enginePriv) { | |
listType = enginePriv->rawMetaObjectForType(enginePriv->listType(property.propType)); | |
} else { | |
QDeclarativeType *type = QDeclarativeMetaType::qmlType(QDeclarativeMetaType::listType(property.propType)); | |
if (!type) return false; | |
listType = type->baseMetaObject(); | |
} | |
if (!listType) return false; | |
QDeclarativeListProperty<void> prop; | |
void *args[] = { &prop, 0 }; | |
QMetaObject::metacall(object, QMetaObject::ReadProperty, coreIdx, args); | |
if (!prop.clear) return false; | |
prop.clear(&prop); | |
if (value.userType() == qMetaTypeId<QList<QObject *> >()) { | |
const QList<QObject *> &list = qvariant_cast<QList<QObject *> >(value); | |
for (int ii = 0; ii < list.count(); ++ii) { | |
QObject *o = list.at(ii); | |
if (o && !canConvert(o->metaObject(), listType)) | |
o = 0; | |
prop.append(&prop, (void *)o); | |
} | |
} else { | |
QObject *o = enginePriv?enginePriv->toQObject(value):QDeclarativeMetaType::toQObject(value); | |
if (o && !canConvert(o->metaObject(), listType)) | |
o = 0; | |
prop.append(&prop, (void *)o); | |
} | |
} else { | |
Q_ASSERT(variantType != propertyType); | |
bool ok = false; | |
QVariant v; | |
if (variantType == QVariant::String) | |
v = QDeclarativeStringConverters::variantFromString(value.toString(), propertyType, &ok); | |
if (!ok) { | |
v = value; | |
if (v.convert((QVariant::Type)propertyType)) { | |
ok = true; | |
} else if ((uint)propertyType >= QVariant::UserType && variantType == QVariant::String) { | |
QDeclarativeMetaType::StringConverter con = QDeclarativeMetaType::customStringConverter(propertyType); | |
if (con) { | |
v = con(value.toString()); | |
if (v.userType() == propertyType) | |
ok = true; | |
} | |
} | |
} | |
if (ok) { | |
void *a[] = { (void *)v.constData(), 0, &status, &flags}; | |
QMetaObject::metacall(object, QMetaObject::WriteProperty, coreIdx, a); | |
} else { | |
return false; | |
} | |
} | |
return true; | |
} | |
const QMetaObject *QDeclarativePropertyPrivate::rawMetaObjectForType(QDeclarativeEnginePrivate *engine, int userType) | |
{ | |
if (engine) { | |
return engine->rawMetaObjectForType(userType); | |
} else { | |
QDeclarativeType *type = QDeclarativeMetaType::qmlType(userType); | |
return type?type->baseMetaObject():0; | |
} | |
} | |
/*! | |
Sets the property value to \a value and returns true. | |
Returns false if the property can't be set because the | |
\a value is the wrong type, for example. | |
*/ | |
bool QDeclarativeProperty::write(const QVariant &value) const | |
{ | |
return QDeclarativePropertyPrivate::write(*this, value, 0); | |
} | |
/*! | |
Writes \a value to the \a name property of \a object. This method | |
is equivalent to: | |
\code | |
QDeclarativeProperty p(object, name); | |
p.write(value); | |
\endcode | |
*/ | |
bool QDeclarativeProperty::write(QObject *object, const QString &name, const QVariant &value) | |
{ | |
QDeclarativeProperty p(object, name); | |
return p.write(value); | |
} | |
/*! | |
Writes \a value to the \a name property of \a object using the | |
\l{QDeclarativeContext} {context} \a ctxt. This method is | |
equivalent to: | |
\code | |
QDeclarativeProperty p(object, name, ctxt); | |
p.write(value); | |
\endcode | |
*/ | |
bool QDeclarativeProperty::write(QObject *object, | |
const QString &name, | |
const QVariant &value, | |
QDeclarativeContext *ctxt) | |
{ | |
QDeclarativeProperty p(object, name, ctxt); | |
return p.write(value); | |
} | |
/*! | |
Writes \a value to the \a name property of \a object using the | |
environment for instantiating QML components that is provided by | |
\a engine. This method is equivalent to: | |
\code | |
QDeclarativeProperty p(object, name, engine); | |
p.write(value); | |
\endcode | |
*/ | |
bool QDeclarativeProperty::write(QObject *object, const QString &name, const QVariant &value, | |
QDeclarativeEngine *engine) | |
{ | |
QDeclarativeProperty p(object, name, engine); | |
return p.write(value); | |
} | |
/*! | |
Resets the property and returns true if the property is | |
resettable. If the property is not resettable, nothing happens | |
and false is returned. | |
*/ | |
bool QDeclarativeProperty::reset() const | |
{ | |
if (isResettable()) { | |
void *args[] = { 0 }; | |
QMetaObject::metacall(d->object, QMetaObject::ResetProperty, d->core.coreIndex, args); | |
return true; | |
} else { | |
return false; | |
} | |
} | |
bool QDeclarativePropertyPrivate::write(const QDeclarativeProperty &that, | |
const QVariant &value, WriteFlags flags) | |
{ | |
if (!that.d) | |
return false; | |
if (that.d->object && that.type() & QDeclarativeProperty::Property && | |
that.d->core.isValid() && that.isWritable()) | |
return that.d->writeValueProperty(value, flags); | |
else | |
return false; | |
} | |
/*! | |
Returns true if the property has a change notifier signal, otherwise false. | |
*/ | |
bool QDeclarativeProperty::hasNotifySignal() const | |
{ | |
if (type() & Property && d->object) { | |
return d->object->metaObject()->property(d->core.coreIndex).hasNotifySignal(); | |
} | |
return false; | |
} | |
/*! | |
Returns true if the property needs a change notifier signal for bindings | |
to remain upto date, false otherwise. | |
Some properties, such as attached properties or those whose value never | |
changes, do not require a change notifier. | |
*/ | |
bool QDeclarativeProperty::needsNotifySignal() const | |
{ | |
return type() & Property && !property().isConstant(); | |
} | |
/*! | |
Connects the property's change notifier signal to the | |
specified \a method of the \a dest object and returns | |
true. Returns false if this metaproperty does not | |
represent a regular Qt property or if it has no | |
change notifier signal, or if the \a dest object does | |
not have the specified \a method. | |
*/ | |
bool QDeclarativeProperty::connectNotifySignal(QObject *dest, int method) const | |
{ | |
if (!(type() & Property) || !d->object) | |
return false; | |
QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex); | |
if (prop.hasNotifySignal()) { | |
return QDeclarativePropertyPrivate::connect(d->object, prop.notifySignalIndex(), dest, method, Qt::DirectConnection); | |
} else { | |
return false; | |
} | |
} | |
/*! | |
Connects the property's change notifier signal to the | |
specified \a slot of the \a dest object and returns | |
true. Returns false if this metaproperty does not | |
represent a regular Qt property or if it has no | |
change notifier signal, or if the \a dest object does | |
not have the specified \a slot. | |
*/ | |
bool QDeclarativeProperty::connectNotifySignal(QObject *dest, const char *slot) const | |
{ | |
if (!(type() & Property) || !d->object) | |
return false; | |
QMetaProperty prop = d->object->metaObject()->property(d->core.coreIndex); | |
if (prop.hasNotifySignal()) { | |
QByteArray signal(QByteArray("2") + prop.notifySignal().signature()); | |
return QObject::connect(d->object, signal.constData(), dest, slot); | |
} else { | |
return false; | |
} | |
} | |
/*! | |
Return the Qt metaobject index of the property. | |
*/ | |
int QDeclarativeProperty::index() const | |
{ | |
return d ? d->core.coreIndex : -1; | |
} | |
int QDeclarativePropertyPrivate::valueTypeCoreIndex(const QDeclarativeProperty &that) | |
{ | |
return that.d ? that.d->valueType.valueTypeCoreIdx : -1; | |
} | |
/*! | |
Returns the "property index" for use in bindings. The top 8 bits are the value type | |
offset, and 0 otherwise. The bottom 24-bits are the regular property index. | |
*/ | |
int QDeclarativePropertyPrivate::bindingIndex(const QDeclarativeProperty &that) | |
{ | |
if (!that.d) | |
return -1; | |
int rv = that.d->core.coreIndex; | |
if (rv != -1 && that.d->valueType.valueTypeCoreIdx != -1) | |
rv = rv | (that.d->valueType.valueTypeCoreIdx << 24); | |
return rv; | |
} | |
struct SerializedData { | |
bool isValueType; | |
QDeclarativePropertyCache::Data core; | |
}; | |
struct ValueTypeSerializedData : public SerializedData { | |
QDeclarativePropertyCache::ValueTypeData valueType; | |
}; | |
QByteArray QDeclarativePropertyPrivate::saveValueType(const QMetaObject *metaObject, int index, | |
const QMetaObject *subObject, int subIndex) | |
{ | |
QMetaProperty prop = metaObject->property(index); | |
QMetaProperty subProp = subObject->property(subIndex); | |
ValueTypeSerializedData sd; | |
memset(&sd, 0, sizeof(sd)); | |
sd.isValueType = true; | |
sd.core.load(metaObject->property(index)); | |
sd.valueType.flags = QDeclarativePropertyCache::Data::flagsForProperty(subProp); | |
sd.valueType.valueTypeCoreIdx = subIndex; | |
sd.valueType.valueTypePropType = subProp.userType(); | |
QByteArray rv((const char *)&sd, sizeof(sd)); | |
return rv; | |
} | |
QByteArray QDeclarativePropertyPrivate::saveProperty(const QMetaObject *metaObject, int index) | |
{ | |
SerializedData sd; | |
memset(&sd, 0, sizeof(sd)); | |
sd.isValueType = false; | |
sd.core.load(metaObject->property(index)); | |
QByteArray rv((const char *)&sd, sizeof(sd)); | |
return rv; | |
} | |
QDeclarativeProperty | |
QDeclarativePropertyPrivate::restore(const QByteArray &data, QObject *object, QDeclarativeContextData *ctxt) | |
{ | |
QDeclarativeProperty prop; | |
if (data.isEmpty()) | |
return prop; | |
const SerializedData *sd = (const SerializedData *)data.constData(); | |
if (sd->isValueType) { | |
const ValueTypeSerializedData *vt = (const ValueTypeSerializedData *)sd; | |
return restore(vt->core, vt->valueType, object, ctxt); | |
} else { | |
QDeclarativePropertyCache::ValueTypeData data; | |
return restore(sd->core, data, object, ctxt); | |
} | |
} | |
QDeclarativeProperty | |
QDeclarativePropertyPrivate::restore(const QDeclarativePropertyCache::Data &data, const QDeclarativePropertyCache::ValueTypeData &valueType, QObject *object, QDeclarativeContextData *ctxt) | |
{ | |
QDeclarativeProperty prop; | |
prop.d = new QDeclarativePropertyPrivate; | |
prop.d->object = object; | |
prop.d->context = ctxt; | |
prop.d->engine = ctxt->engine; | |
prop.d->core = data; | |
prop.d->valueType = valueType; | |
return prop; | |
} | |
/*! | |
Returns true if lhs and rhs refer to the same metaobject data | |
*/ | |
bool QDeclarativePropertyPrivate::equal(const QMetaObject *lhs, const QMetaObject *rhs) | |
{ | |
return lhs == rhs || (1 && lhs && rhs && lhs->d.stringdata == rhs->d.stringdata); | |
} | |
/*! | |
Returns true if from inherits to. | |
*/ | |
bool QDeclarativePropertyPrivate::canConvert(const QMetaObject *from, const QMetaObject *to) | |
{ | |
if (from && to == &QObject::staticMetaObject) | |
return true; | |
while (from) { | |
if (equal(from, to)) | |
return true; | |
from = from->superClass(); | |
} | |
return false; | |
} | |
/*! | |
Return the signal corresponding to \a name | |
*/ | |
QMetaMethod QDeclarativePropertyPrivate::findSignalByName(const QMetaObject *mo, const QByteArray &name) | |
{ | |
Q_ASSERT(mo); | |
int methods = mo->methodCount(); | |
for (int ii = methods - 1; ii >= 2; --ii) { // >= 2 to block the destroyed signal | |
QMetaMethod method = mo->method(ii); | |
QByteArray methodName = method.signature(); | |
int idx = methodName.indexOf('('); | |
methodName = methodName.left(idx); | |
if (methodName == name) | |
return method; | |
} | |
// If no signal is found, but the signal is of the form "onBlahChanged", | |
// return the notify signal for the property "Blah" | |
if (name.endsWith("Changed")) { | |
QByteArray propName = name.mid(0, name.length() - 7); | |
int propIdx = mo->indexOfProperty(propName.constData()); | |
if (propIdx >= 0) { | |
QMetaProperty prop = mo->property(propIdx); | |
if (prop.hasNotifySignal()) | |
return prop.notifySignal(); | |
} | |
} | |
return QMetaMethod(); | |
} | |
static inline int QMetaObject_methods(const QMetaObject *metaObject) | |
{ | |
struct Private | |
{ | |
int revision; | |
int className; | |
int classInfoCount, classInfoData; | |
int methodCount, methodData; | |
int propertyCount, propertyData; | |
}; | |
return reinterpret_cast<const Private *>(metaObject->d.data)->methodCount; | |
} | |
static inline int QMetaObject_properties(const QMetaObject *metaObject) | |
{ | |
struct Private | |
{ | |
int revision; | |
int className; | |
int classInfoCount, classInfoData; | |
int methodCount, methodData; | |
int propertyCount, propertyData; | |
}; | |
return reinterpret_cast<const Private *>(metaObject->d.data)->propertyCount; | |
} | |
static inline void flush_vme_signal(const QObject *object, int index) | |
{ | |
QDeclarativeData *data = static_cast<QDeclarativeData *>(QObjectPrivate::get(const_cast<QObject *>(object))->declarativeData); | |
if (data && data->propertyCache) { | |
QDeclarativePropertyCache::Data *property = data->propertyCache->method(index); | |
if (property && property->flags & QDeclarativePropertyCache::Data::IsVMESignal) { | |
const QMetaObject *metaObject = object->metaObject(); | |
int methodOffset = metaObject->methodOffset(); | |
while (methodOffset > index) { | |
metaObject = metaObject->d.superdata; | |
methodOffset -= QMetaObject_methods(metaObject); | |
} | |
QDeclarativeVMEMetaObject *vme = | |
static_cast<QDeclarativeVMEMetaObject *>(const_cast<QMetaObject *>(metaObject)); | |
vme->connectAliasSignal(index); | |
} | |
} | |
} | |
/*! | |
Connect \a sender \a signal_index to \a receiver \a method_index with the specified | |
\a type and \a types. This behaves identically to QMetaObject::connect() except that | |
it connects any lazy "proxy" signal connections set up by QML. | |
It is possible that this logic should be moved to QMetaObject::connect(). | |
*/ | |
bool QDeclarativePropertyPrivate::connect(const QObject *sender, int signal_index, | |
const QObject *receiver, int method_index, | |
int type, int *types) | |
{ | |
flush_vme_signal(sender, signal_index); | |
flush_vme_signal(receiver, method_index); | |
return QMetaObject::connect(sender, signal_index, receiver, method_index, type, types); | |
} | |
/*! | |
Return \a metaObject's [super] meta object that provides data for \a property. | |
*/ | |
const QMetaObject *QDeclarativePropertyPrivate::metaObjectForProperty(const QMetaObject *metaObject, int property) | |
{ | |
int propertyOffset = metaObject->propertyOffset(); | |
while (propertyOffset > property) { | |
metaObject = metaObject->d.superdata; | |
propertyOffset -= QMetaObject_properties(metaObject); | |
} | |
return metaObject; | |
} | |
QT_END_NAMESPACE |