summaryrefslogtreecommitdiffstats
path: root/src/declarative/util/qmlpropertychanges.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/declarative/util/qmlpropertychanges.cpp')
-rw-r--r--src/declarative/util/qmlpropertychanges.cpp468
1 files changed, 468 insertions, 0 deletions
diff --git a/src/declarative/util/qmlpropertychanges.cpp b/src/declarative/util/qmlpropertychanges.cpp
new file mode 100644
index 0000000..68fc5cc
--- /dev/null
+++ b/src/declarative/util/qmlpropertychanges.cpp
@@ -0,0 +1,468 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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 "qmlpropertychanges_p.h"
+
+#include "qmlopenmetaobject_p.h"
+
+#include <qmlinfo.h>
+#include <qmlcustomparser_p.h>
+#include <qmlparser_p.h>
+#include <qmlexpression.h>
+#include <qmlbinding.h>
+#include <qmlcontext.h>
+#include <qmlguard_p.h>
+
+#include <QtCore/qdebug.h>
+
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlclass PropertyChanges QmlPropertyChanges
+ \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"
+ }
+ }
+
+ MouseRegion { anchors.fill: parent; onClicked: myText.state = 'myState' }
+ }
+ \endqml
+
+ State-specific script for signal handlers can also be specified:
+
+ \qml
+ PropertyChanges {
+ target: myMouseRegion
+ 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 QmlPropertyChanges
+ \brief The QmlPropertyChanges 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 QmlReplaceSignalHandler : public QmlActionEvent
+{
+public:
+ QmlReplaceSignalHandler() : expression(0), reverseExpression(0),
+ rewindExpression(0), ownedExpression(0) {}
+ ~QmlReplaceSignalHandler() {
+ delete ownedExpression;
+ }
+
+ virtual QString typeName() const { return QLatin1String("ReplaceSignalHandler"); }
+
+ QmlMetaProperty property;
+ QmlExpression *expression;
+ QmlExpression *reverseExpression;
+ QmlExpression *rewindExpression;
+ QmlGuard<QmlExpression> 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(QmlActionEvent*other) {
+ if (other == this)
+ return true;
+ if (other->typeName() != typeName())
+ return false;
+ if (static_cast<QmlReplaceSignalHandler*>(other)->property == property)
+ return true;
+ return false;
+ }
+};
+
+
+class QmlPropertyChangesPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QmlPropertyChanges)
+public:
+ QmlPropertyChangesPrivate() : 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, QmlExpression *> > expressions;
+ QList<QmlReplaceSignalHandler*> signalReplacements;
+
+ QmlMetaProperty property(const QByteArray &);
+};
+
+class QmlPropertyChangesParser : public QmlCustomParser
+{
+public:
+ void compileList(QList<QPair<QByteArray, QVariant> > &list, const QByteArray &pre, const QmlCustomParserProperty &prop);
+
+ virtual QByteArray compile(const QList<QmlCustomParserProperty> &);
+ virtual void setCustomData(QObject *, const QByteArray &);
+};
+
+void
+QmlPropertyChangesParser::compileList(QList<QPair<QByteArray, QVariant> > &list,
+ const QByteArray &pre,
+ const QmlCustomParserProperty &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<QmlCustomParserNode>()) {
+ continue;
+ } else if(value.userType() == qMetaTypeId<QmlCustomParserProperty>()) {
+
+ QmlCustomParserProperty prop =
+ qvariant_cast<QmlCustomParserProperty>(value);
+ QByteArray pre = propName + '.';
+ compileList(list, pre, prop);
+
+ } else {
+ list << qMakePair(propName, value);
+ }
+ }
+}
+
+QByteArray
+QmlPropertyChangesParser::compile(const QList<QmlCustomParserProperty> &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) {
+ QmlParser::Variant v = qvariant_cast<QmlParser::Variant>(data.at(ii).second);
+ QVariant var;
+ bool isScript = v.isScript();
+ switch(v.type()) {
+ case QmlParser::Variant::Boolean:
+ var = QVariant(v.asBoolean());
+ break;
+ case QmlParser::Variant::Number:
+ var = QVariant(v.asNumber());
+ break;
+ case QmlParser::Variant::String:
+ var = QVariant(v.asString());
+ break;
+ case QmlParser::Variant::Invalid:
+ case QmlParser::Variant::Script:
+ var = QVariant(v.asScript());
+ break;
+ }
+
+ ds << data.at(ii).first << isScript << var;
+ }
+
+ return rv;
+}
+
+void QmlPropertyChangesPrivate::decode()
+{
+ Q_Q(QmlPropertyChanges);
+ 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;
+
+ QmlMetaProperty prop = property(name); //### better way to check for signal property?
+ if (prop.type() & QmlMetaProperty::SignalProperty) {
+ QmlExpression *expression = new QmlExpression(qmlContext(q), data.toString(), object);
+ expression->setTrackChange(false);
+ QmlReplaceSignalHandler *handler = new QmlReplaceSignalHandler;
+ handler->property = prop;
+ handler->expression = expression;
+ signalReplacements << handler;
+ } else if (isScript) {
+ QmlExpression *expression = new QmlExpression(qmlContext(q), data.toString(), object);
+ expression->setTrackChange(false);
+ expressions << qMakePair(name, expression);
+ } else {
+ properties << qMakePair(name, data);
+ }
+ }
+
+ decoded = true;
+ data.clear();
+}
+
+void QmlPropertyChangesParser::setCustomData(QObject *object,
+ const QByteArray &data)
+{
+ QmlPropertyChangesPrivate *p =
+ static_cast<QmlPropertyChangesPrivate *>(QObjectPrivate::get(object));
+ p->data = data;
+ p->decoded = false;
+}
+
+QmlPropertyChanges::QmlPropertyChanges()
+: QmlStateOperation(*(new QmlPropertyChangesPrivate))
+{
+}
+
+QmlPropertyChanges::~QmlPropertyChanges()
+{
+ Q_D(QmlPropertyChanges);
+ 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 *QmlPropertyChanges::object() const
+{
+ Q_D(const QmlPropertyChanges);
+ return d->object;
+}
+
+void QmlPropertyChanges::setObject(QObject *o)
+{
+ Q_D(QmlPropertyChanges);
+ 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 QmlPropertyChanges::restoreEntryValues() const
+{
+ Q_D(const QmlPropertyChanges);
+ return d->restore;
+}
+
+void QmlPropertyChanges::setRestoreEntryValues(bool v)
+{
+ Q_D(QmlPropertyChanges);
+ d->restore = v;
+}
+
+QmlMetaProperty
+QmlPropertyChangesPrivate::property(const QByteArray &property)
+{
+ Q_Q(QmlPropertyChanges);
+ QmlMetaProperty prop = QmlMetaProperty::createProperty(object, QString::fromUtf8(property));
+ if (!prop.isValid()) {
+ qmlInfo(q) << QmlPropertyChanges::tr("Cannot assign to non-existant property \"%1\"").arg(QString::fromUtf8(property));
+ return QmlMetaProperty();
+ } else if (!(prop.type() & QmlMetaProperty::SignalProperty) && !prop.isWritable()) {
+ qmlInfo(q) << QmlPropertyChanges::tr("Cannot assign to read-only property \"%1\"").arg(QString::fromUtf8(property));
+ return QmlMetaProperty();
+ }
+ return prop;
+}
+
+QmlPropertyChanges::ActionList QmlPropertyChanges::actions()
+{
+ Q_D(QmlPropertyChanges);
+
+ d->decode();
+
+ ActionList list;
+
+ for (int ii = 0; ii < d->properties.count(); ++ii) {
+
+ QByteArray property = d->properties.at(ii).first;
+
+ QmlAction 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) {
+
+ QmlReplaceSignalHandler *handler = d->signalReplacements.at(ii);
+
+ if (handler->property.isValid()) {
+ QmlAction a;
+ a.event = handler;
+ list << a;
+ }
+ }
+
+ for (int ii = 0; ii < d->expressions.count(); ++ii) {
+
+ QByteArray property = d->expressions.at(ii).first;
+ QmlMetaProperty prop = d->property(property);
+
+ if (prop.isValid()) {
+ QmlAction 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 {
+ QmlBinding *newBinding = new QmlBinding(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 QmlPropertyChanges::isExplicit() const
+{
+ Q_D(const QmlPropertyChanges);
+ return d->isExplicit;
+}
+
+void QmlPropertyChanges::setIsExplicit(bool e)
+{
+ Q_D(QmlPropertyChanges);
+ d->isExplicit = e;
+}
+
+QML_DEFINE_CUSTOM_TYPE(Qt, 4,6, PropertyChanges, QmlPropertyChanges, QmlPropertyChangesParser)
+
+QT_END_NAMESPACE