diff options
Diffstat (limited to 'src/declarative/util/qdeclarativepropertychanges.cpp')
-rw-r--r-- | src/declarative/util/qdeclarativepropertychanges.cpp | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/src/declarative/util/qdeclarativepropertychanges.cpp b/src/declarative/util/qdeclarativepropertychanges.cpp new file mode 100644 index 0000000..5907ddb --- /dev/null +++ b/src/declarative/util/qdeclarativepropertychanges.cpp @@ -0,0 +1,456 @@ +/**************************************************************************** +** +** Copyright (C) 2010 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$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativepropertychanges_p.h" + +#include "qdeclarativeopenmetaobject_p.h" + +#include <qdeclarativeinfo.h> +#include <qdeclarativecustomparser_p.h> +#include <qdeclarativeparser_p.h> +#include <qdeclarativeexpression.h> +#include <qdeclarativebinding_p.h> +#include <qdeclarativecontext.h> +#include <qdeclarativeguard_p.h> + +#include <QtCore/qdebug.h> + +#include <private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +/*! + \qmlclass PropertyChanges QDeclarativePropertyChanges + \since 4.7 + \brief The PropertyChanges element describes new property values for a state. + + PropertyChanges provides a state change that modifies the properties of an item. + + Here is a property change that modifies the text and color of a Text element + when it is clicked: + + \qml + Text { + id: myText + width: 100; height: 100 + text: "Hello" + color: "blue" + + states: State { + name: "myState" + + PropertyChanges { + target: myText + text: "Goodbye" + color: "red" + } + } + + MouseArea { anchors.fill: parent; onClicked: myText.state = 'myState' } + } + \endqml + + State-specific script for signal handlers can also be specified: + + \qml + PropertyChanges { + target: myMouseArea + onClicked: doSomethingDifferent() + } + \endqml + + Changes to an Item's parent or anchors should be done using the associated change elements + (ParentChange and AnchorChanges, respectively) rather than PropertyChanges. + + \sa {qmlstate}{States} +*/ + +/*! + \internal + \class QDeclarativePropertyChanges + \brief The QDeclarativePropertyChanges class describes new property values for a state. +*/ + +/*! + \qmlproperty Object PropertyChanges::target + This property holds the object which contains the properties to be changed. +*/ + +class QDeclarativeReplaceSignalHandler : public QDeclarativeActionEvent +{ +public: + QDeclarativeReplaceSignalHandler() : expression(0), reverseExpression(0), + rewindExpression(0), ownedExpression(0) {} + ~QDeclarativeReplaceSignalHandler() { + delete ownedExpression; + } + + virtual QString typeName() const { return QLatin1String("ReplaceSignalHandler"); } + + QDeclarativeMetaProperty property; + QDeclarativeExpression *expression; + QDeclarativeExpression *reverseExpression; + QDeclarativeExpression *rewindExpression; + QDeclarativeGuard<QDeclarativeExpression> ownedExpression; + + virtual void execute() { + ownedExpression = property.setSignalExpression(expression); + } + + virtual bool isReversable() { return true; } + virtual void reverse() { + ownedExpression = property.setSignalExpression(reverseExpression); + } + + virtual void saveOriginals() { + saveCurrentValues(); + reverseExpression = rewindExpression; + } + + virtual void rewind() { + ownedExpression = property.setSignalExpression(rewindExpression); + } + virtual void saveCurrentValues() { rewindExpression = property.signalExpression(); } + + virtual bool override(QDeclarativeActionEvent*other) { + if (other == this) + return true; + if (other->typeName() != typeName()) + return false; + if (static_cast<QDeclarativeReplaceSignalHandler*>(other)->property == property) + return true; + return false; + } +}; + + +class QDeclarativePropertyChangesPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativePropertyChanges) +public: + QDeclarativePropertyChangesPrivate() : object(0), decoded(true), restore(true), + isExplicit(false) {} + + QObject *object; + QByteArray data; + + bool decoded : 1; + bool restore : 1; + bool isExplicit : 1; + + void decode(); + + QList<QPair<QByteArray, QVariant> > properties; + QList<QPair<QByteArray, QDeclarativeExpression *> > expressions; + QList<QDeclarativeReplaceSignalHandler*> signalReplacements; + + QDeclarativeMetaProperty property(const QByteArray &); +}; + +void +QDeclarativePropertyChangesParser::compileList(QList<QPair<QByteArray, QVariant> > &list, + const QByteArray &pre, + const QDeclarativeCustomParserProperty &prop) +{ + QByteArray propName = pre + prop.name(); + + QList<QVariant> values = prop.assignedValues(); + for (int ii = 0; ii < values.count(); ++ii) { + const QVariant &value = values.at(ii); + + if (value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) { + continue; + } else if(value.userType() == qMetaTypeId<QDeclarativeCustomParserProperty>()) { + + QDeclarativeCustomParserProperty prop = + qvariant_cast<QDeclarativeCustomParserProperty>(value); + QByteArray pre = propName + '.'; + compileList(list, pre, prop); + + } else { + list << qMakePair(propName, value); + } + } +} + +QByteArray +QDeclarativePropertyChangesParser::compile(const QList<QDeclarativeCustomParserProperty> &props) +{ + QList<QPair<QByteArray, QVariant> > data; + for(int ii = 0; ii < props.count(); ++ii) + compileList(data, QByteArray(), props.at(ii)); + + QByteArray rv; + QDataStream ds(&rv, QIODevice::WriteOnly); + + ds << data.count(); + for(int ii = 0; ii < data.count(); ++ii) { + QDeclarativeParser::Variant v = qvariant_cast<QDeclarativeParser::Variant>(data.at(ii).second); + QVariant var; + bool isScript = v.isScript(); + switch(v.type()) { + case QDeclarativeParser::Variant::Boolean: + var = QVariant(v.asBoolean()); + break; + case QDeclarativeParser::Variant::Number: + var = QVariant(v.asNumber()); + break; + case QDeclarativeParser::Variant::String: + var = QVariant(v.asString()); + break; + case QDeclarativeParser::Variant::Invalid: + case QDeclarativeParser::Variant::Script: + var = QVariant(v.asScript()); + break; + } + + ds << data.at(ii).first << isScript << var; + } + + return rv; +} + +void QDeclarativePropertyChangesPrivate::decode() +{ + Q_Q(QDeclarativePropertyChanges); + if (decoded) + return; + + QDataStream ds(&data, QIODevice::ReadOnly); + + int count; + ds >> count; + for (int ii = 0; ii < count; ++ii) { + QByteArray name; + bool isScript; + QVariant data; + ds >> name; + ds >> isScript; + ds >> data; + + QDeclarativeMetaProperty prop = property(name); //### better way to check for signal property? + if (prop.type() & QDeclarativeMetaProperty::SignalProperty) { + QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(q), data.toString(), object); + QDeclarativeReplaceSignalHandler *handler = new QDeclarativeReplaceSignalHandler; + handler->property = prop; + handler->expression = expression; + signalReplacements << handler; + } else if (isScript) { + QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(q), data.toString(), object); + expressions << qMakePair(name, expression); + } else { + properties << qMakePair(name, data); + } + } + + decoded = true; + data.clear(); +} + +void QDeclarativePropertyChangesParser::setCustomData(QObject *object, + const QByteArray &data) +{ + QDeclarativePropertyChangesPrivate *p = + static_cast<QDeclarativePropertyChangesPrivate *>(QObjectPrivate::get(object)); + p->data = data; + p->decoded = false; +} + +QDeclarativePropertyChanges::QDeclarativePropertyChanges() +: QDeclarativeStateOperation(*(new QDeclarativePropertyChangesPrivate)) +{ +} + +QDeclarativePropertyChanges::~QDeclarativePropertyChanges() +{ + Q_D(QDeclarativePropertyChanges); + for(int ii = 0; ii < d->expressions.count(); ++ii) + delete d->expressions.at(ii).second; + for(int ii = 0; ii < d->signalReplacements.count(); ++ii) + delete d->signalReplacements.at(ii); +} + +QObject *QDeclarativePropertyChanges::object() const +{ + Q_D(const QDeclarativePropertyChanges); + return d->object; +} + +void QDeclarativePropertyChanges::setObject(QObject *o) +{ + Q_D(QDeclarativePropertyChanges); + d->object = o; +} + +/*! + \qmlproperty bool PropertyChanges::restoreEntryValues + + Whether or not the previous values should be restored when + leaving the state. By default, restoreEntryValues is true. + + By setting restoreEntryValues to false, you can create a temporary state + that has permanent effects on property values. +*/ +bool QDeclarativePropertyChanges::restoreEntryValues() const +{ + Q_D(const QDeclarativePropertyChanges); + return d->restore; +} + +void QDeclarativePropertyChanges::setRestoreEntryValues(bool v) +{ + Q_D(QDeclarativePropertyChanges); + d->restore = v; +} + +QDeclarativeMetaProperty +QDeclarativePropertyChangesPrivate::property(const QByteArray &property) +{ + Q_Q(QDeclarativePropertyChanges); + QDeclarativeMetaProperty prop = QDeclarativeMetaProperty::createProperty(object, QString::fromUtf8(property)); + if (!prop.isValid()) { + qmlInfo(q) << QDeclarativePropertyChanges::tr("Cannot assign to non-existent property \"%1\"").arg(QString::fromUtf8(property)); + return QDeclarativeMetaProperty(); + } else if (!(prop.type() & QDeclarativeMetaProperty::SignalProperty) && !prop.isWritable()) { + qmlInfo(q) << QDeclarativePropertyChanges::tr("Cannot assign to read-only property \"%1\"").arg(QString::fromUtf8(property)); + return QDeclarativeMetaProperty(); + } + return prop; +} + +QDeclarativePropertyChanges::ActionList QDeclarativePropertyChanges::actions() +{ + Q_D(QDeclarativePropertyChanges); + + d->decode(); + + ActionList list; + + for (int ii = 0; ii < d->properties.count(); ++ii) { + + QByteArray property = d->properties.at(ii).first; + + QDeclarativeAction a(d->object, QString::fromLatin1(property), + d->properties.at(ii).second); + + if (a.property.isValid()) { + a.restore = restoreEntryValues(); + + if (a.property.propertyType() == QVariant::Url && + (a.toValue.userType() == QVariant::String || a.toValue.userType() == QVariant::ByteArray) && !a.toValue.isNull()) + a.toValue.setValue(qmlContext(this)->resolvedUrl(QUrl(a.toValue.toString()))); + + list << a; + } + } + + for (int ii = 0; ii < d->signalReplacements.count(); ++ii) { + + QDeclarativeReplaceSignalHandler *handler = d->signalReplacements.at(ii); + + if (handler->property.isValid()) { + QDeclarativeAction a; + a.event = handler; + list << a; + } + } + + for (int ii = 0; ii < d->expressions.count(); ++ii) { + + QByteArray property = d->expressions.at(ii).first; + QDeclarativeMetaProperty prop = d->property(property); + + if (prop.isValid()) { + QDeclarativeAction a; + a.restore = restoreEntryValues(); + a.property = prop; + a.fromValue = a.property.read(); + a.specifiedObject = d->object; + a.specifiedProperty = QString::fromLatin1(property); + + if (d->isExplicit) { + a.toValue = d->expressions.at(ii).second->value(); + } else { + QDeclarativeBinding *newBinding = new QDeclarativeBinding(d->expressions.at(ii).second->expression(), object(), qmlContext(this)); + newBinding->setTarget(prop); + a.toBinding = newBinding; + a.deletableToBinding = true; + } + + list << a; + } + } + + return list; +} + +/*! + \qmlproperty bool PropertyChanges::explicit + + If explicit is set to true, any potential bindings will be interpreted as + once-off assignments that occur when the state is entered. + + In the following example, the addition of explicit prevents myItem.width from + being bound to parent.width. Instead, it is assigned the value of parent.width + at the time of the state change. + \qml + PropertyChanges { + target: myItem + explicit: true + width: parent.width + } + \endqml + + By default, explicit is false. +*/ +bool QDeclarativePropertyChanges::isExplicit() const +{ + Q_D(const QDeclarativePropertyChanges); + return d->isExplicit; +} + +void QDeclarativePropertyChanges::setIsExplicit(bool e) +{ + Q_D(QDeclarativePropertyChanges); + d->isExplicit = e; +} + +QT_END_NAMESPACE |