summaryrefslogtreecommitdiffstats
path: root/src/declarative/util/qdeclarativepropertychanges.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/declarative/util/qdeclarativepropertychanges.cpp')
-rw-r--r--src/declarative/util/qdeclarativepropertychanges.cpp486
1 files changed, 486 insertions, 0 deletions
diff --git a/src/declarative/util/qdeclarativepropertychanges.cpp b/src/declarative/util/qdeclarativepropertychanges.cpp
new file mode 100644
index 0000000..9c2b1e6
--- /dev/null
+++ b/src/declarative/util/qdeclarativepropertychanges.cpp
@@ -0,0 +1,486 @@
+/****************************************************************************
+**
+** 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 <qdeclarativeproperty_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
+
+ You can reset a property in a state change by assigning \c undefined. In the following
+ example we reset \c theText's width when we enter state1. This will give the text its
+ natural width (which is the whole string on one line).
+
+ \qml
+ import Qt 4.6
+
+ Rectangle {
+ width: 640
+ height: 480
+ Text {
+ id: theText
+ width: 50
+ wrap: true
+ text: "a text string that is longer than 50 pixels"
+ }
+
+ states: State {
+ name: "state1"
+ PropertyChanges {
+ target: theText
+ width: undefined
+ }
+ }
+ }
+ \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"); }
+
+ QDeclarativeProperty property;
+ QDeclarativeExpression *expression;
+ QDeclarativeExpression *reverseExpression;
+ QDeclarativeExpression *rewindExpression;
+ QDeclarativeGuard<QDeclarativeExpression> ownedExpression;
+
+ virtual void execute() {
+ ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, expression);
+ }
+
+ virtual bool isReversable() { return true; }
+ virtual void reverse() {
+ ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, reverseExpression);
+ }
+
+ virtual void saveOriginals() {
+ saveCurrentValues();
+ reverseExpression = rewindExpression;
+ }
+
+ virtual void rewind() {
+ ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, rewindExpression);
+ }
+ virtual void saveCurrentValues() {
+ rewindExpression = QDeclarativePropertyPrivate::signalExpression(property);
+ }
+
+ 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;
+
+ QDeclarativeProperty 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;
+
+ QDeclarativeProperty prop = property(name); //### better way to check for signal property?
+ if (prop.type() & QDeclarativeProperty::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;
+}
+
+QDeclarativeProperty
+QDeclarativePropertyChangesPrivate::property(const QByteArray &property)
+{
+ Q_Q(QDeclarativePropertyChanges);
+ QDeclarativeProperty prop(object, QString::fromUtf8(property));
+ if (!prop.isValid()) {
+ qmlInfo(q) << QDeclarativePropertyChanges::tr("Cannot assign to non-existent property \"%1\"").arg(QString::fromUtf8(property));
+ return QDeclarativeProperty();
+ } else if (!(prop.type() & QDeclarativeProperty::SignalProperty) && !prop.isWritable()) {
+ qmlInfo(q) << QDeclarativePropertyChanges::tr("Cannot assign to read-only property \"%1\"").arg(QString::fromUtf8(property));
+ return QDeclarativeProperty();
+ }
+ 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;
+ QDeclarativeProperty 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