src/declarative/util/qdeclarativepropertychanges.cpp
changeset 30 5dc02b23752f
child 33 3e2da88830cd
equal deleted inserted replaced
29:b72c6db6890b 30:5dc02b23752f
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "private/qdeclarativepropertychanges_p.h"
       
    43 
       
    44 #include "private/qdeclarativeopenmetaobject_p.h"
       
    45 
       
    46 #include <qdeclarativeinfo.h>
       
    47 #include <qdeclarativecustomparser_p.h>
       
    48 #include <qdeclarativeparser_p.h>
       
    49 #include <qdeclarativeexpression.h>
       
    50 #include <qdeclarativebinding_p.h>
       
    51 #include <qdeclarativecontext.h>
       
    52 #include <qdeclarativeguard_p.h>
       
    53 #include <qdeclarativeproperty_p.h>
       
    54 #include <qdeclarativecontext_p.h>
       
    55 
       
    56 #include <QtCore/qdebug.h>
       
    57 
       
    58 #include <private/qobject_p.h>
       
    59 
       
    60 QT_BEGIN_NAMESPACE
       
    61 
       
    62 /*!
       
    63     \qmlclass PropertyChanges QDeclarativePropertyChanges
       
    64     \since 4.7
       
    65     \brief The PropertyChanges element describes new property values for a state.
       
    66 
       
    67     PropertyChanges provides a state change that modifies the properties of an item.
       
    68 
       
    69     Here is a property change that modifies the text and color of a Text element
       
    70     when it is clicked:
       
    71     
       
    72     \qml
       
    73     Text {
       
    74         id: myText
       
    75         width: 100; height: 100
       
    76         text: "Hello"
       
    77         color: "blue"
       
    78 
       
    79         states: State {
       
    80             name: "myState"
       
    81 
       
    82             PropertyChanges {
       
    83                 target: myText
       
    84                 text: "Goodbye"
       
    85                 color: "red"
       
    86             }
       
    87         }
       
    88 
       
    89         MouseArea { anchors.fill: parent; onClicked: myText.state = 'myState' }
       
    90     }
       
    91     \endqml
       
    92     
       
    93     State-specific script for signal handlers can also be specified:
       
    94 
       
    95     \qml
       
    96     PropertyChanges {
       
    97         target: myMouseArea
       
    98         onClicked: doSomethingDifferent()
       
    99     }
       
   100     \endqml
       
   101 
       
   102     You can reset a property in a state change by assigning \c undefined. In the following
       
   103     example we reset \c theText's width when we enter state1. This will give the text its
       
   104     natural width (which is the whole string on one line).
       
   105 
       
   106     \qml
       
   107     import Qt 4.7
       
   108 
       
   109     Rectangle {
       
   110         width: 640
       
   111         height: 480
       
   112         Text {
       
   113             id: theText
       
   114             width: 50
       
   115             wrapMode: Text.WordWrap
       
   116             text: "a text string that is longer than 50 pixels"
       
   117         }
       
   118 
       
   119         states: State {
       
   120             name: "state1"
       
   121             PropertyChanges {
       
   122                 target: theText
       
   123                 width: undefined
       
   124             }
       
   125         }
       
   126     }
       
   127     \endqml
       
   128 
       
   129     Anchor margins should be changed with PropertyChanges, but other anchor changes or changes to
       
   130     an Item's parent should be done using the associated change elements
       
   131     (ParentChange and AnchorChanges, respectively).
       
   132 
       
   133     \sa {qmlstate}{States}, QtDeclarative
       
   134 */
       
   135 
       
   136 /*!
       
   137     \internal
       
   138     \class QDeclarativePropertyChanges
       
   139     \brief The QDeclarativePropertyChanges class describes new property values for a state.
       
   140 */
       
   141 
       
   142 /*!
       
   143     \qmlproperty Object PropertyChanges::target
       
   144     This property holds the object which contains the properties to be changed.
       
   145 */
       
   146 
       
   147 class QDeclarativeReplaceSignalHandler : public QDeclarativeActionEvent
       
   148 {
       
   149 public:
       
   150     QDeclarativeReplaceSignalHandler() : expression(0), reverseExpression(0),
       
   151                                 rewindExpression(0), ownedExpression(0) {}
       
   152     ~QDeclarativeReplaceSignalHandler() {
       
   153         delete ownedExpression;
       
   154     }
       
   155 
       
   156     virtual QString typeName() const { return QLatin1String("ReplaceSignalHandler"); }
       
   157 
       
   158     QDeclarativeProperty property;
       
   159     QDeclarativeExpression *expression;
       
   160     QDeclarativeExpression *reverseExpression;
       
   161     QDeclarativeExpression *rewindExpression;
       
   162     QDeclarativeGuard<QDeclarativeExpression> ownedExpression;
       
   163 
       
   164     virtual void execute(Reason) {
       
   165         ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, expression);
       
   166         if (ownedExpression == expression)
       
   167             ownedExpression = 0;
       
   168     }
       
   169 
       
   170     virtual bool isReversable() { return true; }
       
   171     virtual void reverse(Reason) {
       
   172         ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, reverseExpression);
       
   173         if (ownedExpression == reverseExpression)
       
   174             ownedExpression = 0;
       
   175     }
       
   176 
       
   177     virtual void saveOriginals() {
       
   178         saveCurrentValues();
       
   179         reverseExpression = rewindExpression;
       
   180     }
       
   181 
       
   182     /*virtual void copyOriginals(QDeclarativeActionEvent *other)
       
   183     {
       
   184         QDeclarativeReplaceSignalHandler *rsh = static_cast<QDeclarativeReplaceSignalHandler*>(other);
       
   185         saveCurrentValues();
       
   186         if (rsh == this)
       
   187             return;
       
   188         reverseExpression = rsh->reverseExpression;
       
   189         if (rsh->ownedExpression == reverseExpression) {
       
   190             ownedExpression = rsh->ownedExpression;
       
   191             rsh->ownedExpression = 0;
       
   192         }
       
   193     }*/
       
   194 
       
   195     virtual void rewind() {
       
   196         ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, rewindExpression);
       
   197         if (ownedExpression == rewindExpression)
       
   198             ownedExpression = 0;
       
   199     }
       
   200     virtual void saveCurrentValues() { 
       
   201         rewindExpression = QDeclarativePropertyPrivate::signalExpression(property);
       
   202     }
       
   203 
       
   204     virtual bool override(QDeclarativeActionEvent*other) {
       
   205         if (other == this)
       
   206             return true;
       
   207         if (other->typeName() != typeName())
       
   208             return false;
       
   209         if (static_cast<QDeclarativeReplaceSignalHandler*>(other)->property == property)
       
   210             return true;
       
   211         return false;
       
   212     }
       
   213 };
       
   214 
       
   215 
       
   216 class QDeclarativePropertyChangesPrivate : public QObjectPrivate
       
   217 {
       
   218     Q_DECLARE_PUBLIC(QDeclarativePropertyChanges)
       
   219 public:
       
   220     QDeclarativePropertyChangesPrivate() : object(0), decoded(true), restore(true),
       
   221                                 isExplicit(false) {}
       
   222 
       
   223     QObject *object;
       
   224     QByteArray data;
       
   225 
       
   226     bool decoded : 1;
       
   227     bool restore : 1;
       
   228     bool isExplicit : 1;
       
   229 
       
   230     void decode();
       
   231 
       
   232     QList<QPair<QByteArray, QVariant> > properties;
       
   233     QList<QPair<QByteArray, QDeclarativeExpression *> > expressions;
       
   234     QList<QDeclarativeReplaceSignalHandler*> signalReplacements;
       
   235 
       
   236     QDeclarativeProperty property(const QByteArray &);
       
   237 };
       
   238 
       
   239 void
       
   240 QDeclarativePropertyChangesParser::compileList(QList<QPair<QByteArray, QVariant> > &list,
       
   241                                      const QByteArray &pre,
       
   242                                      const QDeclarativeCustomParserProperty &prop)
       
   243 {
       
   244     QByteArray propName = pre + prop.name();
       
   245 
       
   246     QList<QVariant> values = prop.assignedValues();
       
   247     for (int ii = 0; ii < values.count(); ++ii) {
       
   248         const QVariant &value = values.at(ii);
       
   249 
       
   250         if (value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) {
       
   251             error(qvariant_cast<QDeclarativeCustomParserNode>(value),
       
   252                   QDeclarativePropertyChanges::tr("PropertyChanges does not support creating state-specific objects."));
       
   253             continue;
       
   254         } else if(value.userType() == qMetaTypeId<QDeclarativeCustomParserProperty>()) {
       
   255 
       
   256             QDeclarativeCustomParserProperty prop =
       
   257                 qvariant_cast<QDeclarativeCustomParserProperty>(value);
       
   258             QByteArray pre = propName + '.';
       
   259             compileList(list, pre, prop);
       
   260 
       
   261         } else {
       
   262             list << qMakePair(propName, value);
       
   263         }
       
   264     }
       
   265 }
       
   266 
       
   267 QByteArray
       
   268 QDeclarativePropertyChangesParser::compile(const QList<QDeclarativeCustomParserProperty> &props)
       
   269 {
       
   270     QList<QPair<QByteArray, QVariant> > data;
       
   271     for(int ii = 0; ii < props.count(); ++ii)
       
   272         compileList(data, QByteArray(), props.at(ii));
       
   273 
       
   274     QByteArray rv;
       
   275     QDataStream ds(&rv, QIODevice::WriteOnly);
       
   276 
       
   277     ds << data.count();
       
   278     for(int ii = 0; ii < data.count(); ++ii) {
       
   279         QDeclarativeParser::Variant v = qvariant_cast<QDeclarativeParser::Variant>(data.at(ii).second);
       
   280         QVariant var;
       
   281         bool isScript = v.isScript();
       
   282         switch(v.type()) {
       
   283         case QDeclarativeParser::Variant::Boolean:
       
   284             var = QVariant(v.asBoolean());
       
   285             break;
       
   286         case QDeclarativeParser::Variant::Number:
       
   287             var = QVariant(v.asNumber());
       
   288             break;
       
   289         case QDeclarativeParser::Variant::String:
       
   290             var = QVariant(v.asString());
       
   291             break;
       
   292         case QDeclarativeParser::Variant::Invalid:
       
   293         case QDeclarativeParser::Variant::Script:
       
   294             var = QVariant(v.asScript());
       
   295             break;
       
   296         }
       
   297 
       
   298         ds << data.at(ii).first << isScript << var;
       
   299     }
       
   300 
       
   301     return rv;
       
   302 }
       
   303 
       
   304 void QDeclarativePropertyChangesPrivate::decode()
       
   305 {
       
   306     Q_Q(QDeclarativePropertyChanges);
       
   307     if (decoded)
       
   308         return;
       
   309 
       
   310     QDataStream ds(&data, QIODevice::ReadOnly);
       
   311 
       
   312     int count;
       
   313     ds >> count;
       
   314     for (int ii = 0; ii < count; ++ii) {
       
   315         QByteArray name;
       
   316         bool isScript;
       
   317         QVariant data;
       
   318         ds >> name;
       
   319         ds >> isScript;
       
   320         ds >> data;
       
   321 
       
   322         QDeclarativeProperty prop = property(name);      //### better way to check for signal property?
       
   323         if (prop.type() & QDeclarativeProperty::SignalProperty) {
       
   324             QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(q), object, data.toString());
       
   325             QDeclarativeData *ddata = QDeclarativeData::get(q);
       
   326             if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty())
       
   327                 expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber);
       
   328             QDeclarativeReplaceSignalHandler *handler = new QDeclarativeReplaceSignalHandler;
       
   329             handler->property = prop;
       
   330             handler->expression = expression;
       
   331             signalReplacements << handler;
       
   332         } else if (isScript) {
       
   333             QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(q), object, data.toString());
       
   334             QDeclarativeData *ddata = QDeclarativeData::get(q);
       
   335             if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty())
       
   336                 expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber);
       
   337             expressions << qMakePair(name, expression);
       
   338         } else {
       
   339             properties << qMakePair(name, data);
       
   340         }
       
   341     }
       
   342 
       
   343     decoded = true;
       
   344     data.clear();
       
   345 }
       
   346 
       
   347 void QDeclarativePropertyChangesParser::setCustomData(QObject *object,
       
   348                                             const QByteArray &data)
       
   349 {
       
   350     QDeclarativePropertyChangesPrivate *p =
       
   351         static_cast<QDeclarativePropertyChangesPrivate *>(QObjectPrivate::get(object));
       
   352     p->data = data;
       
   353     p->decoded = false;
       
   354 }
       
   355 
       
   356 QDeclarativePropertyChanges::QDeclarativePropertyChanges()
       
   357 : QDeclarativeStateOperation(*(new QDeclarativePropertyChangesPrivate))
       
   358 {
       
   359 }
       
   360 
       
   361 QDeclarativePropertyChanges::~QDeclarativePropertyChanges()
       
   362 {
       
   363     Q_D(QDeclarativePropertyChanges);
       
   364     for(int ii = 0; ii < d->expressions.count(); ++ii)
       
   365         delete d->expressions.at(ii).second;
       
   366     for(int ii = 0; ii < d->signalReplacements.count(); ++ii)
       
   367         delete d->signalReplacements.at(ii);
       
   368 }
       
   369 
       
   370 QObject *QDeclarativePropertyChanges::object() const
       
   371 {
       
   372     Q_D(const QDeclarativePropertyChanges);
       
   373     return d->object;
       
   374 }
       
   375 
       
   376 void QDeclarativePropertyChanges::setObject(QObject *o)
       
   377 {
       
   378     Q_D(QDeclarativePropertyChanges);
       
   379     d->object = o;
       
   380 }
       
   381 
       
   382 /*!
       
   383     \qmlproperty bool PropertyChanges::restoreEntryValues
       
   384     
       
   385     Whether or not the previous values should be restored when
       
   386     leaving the state. By default, restoreEntryValues is true.
       
   387 
       
   388     By setting restoreEntryValues to false, you can create a temporary state
       
   389     that has permanent effects on property values.
       
   390 */
       
   391 bool QDeclarativePropertyChanges::restoreEntryValues() const
       
   392 {
       
   393     Q_D(const QDeclarativePropertyChanges);
       
   394     return d->restore;
       
   395 }
       
   396 
       
   397 void QDeclarativePropertyChanges::setRestoreEntryValues(bool v)
       
   398 {
       
   399     Q_D(QDeclarativePropertyChanges);
       
   400     d->restore = v;
       
   401 }
       
   402 
       
   403 QDeclarativeProperty
       
   404 QDeclarativePropertyChangesPrivate::property(const QByteArray &property)
       
   405 {
       
   406     Q_Q(QDeclarativePropertyChanges);
       
   407     QDeclarativeProperty prop(object, QString::fromUtf8(property), qmlContext(q));
       
   408     if (!prop.isValid()) {
       
   409         qmlInfo(q) << QDeclarativePropertyChanges::tr("Cannot assign to non-existent property \"%1\"").arg(QString::fromUtf8(property));
       
   410         return QDeclarativeProperty();
       
   411     } else if (!(prop.type() & QDeclarativeProperty::SignalProperty) && !prop.isWritable()) {
       
   412         qmlInfo(q) << QDeclarativePropertyChanges::tr("Cannot assign to read-only property \"%1\"").arg(QString::fromUtf8(property));
       
   413         return QDeclarativeProperty();
       
   414     }
       
   415     return prop;
       
   416 }
       
   417 
       
   418 QDeclarativePropertyChanges::ActionList QDeclarativePropertyChanges::actions()
       
   419 {
       
   420     Q_D(QDeclarativePropertyChanges);
       
   421 
       
   422     d->decode();
       
   423 
       
   424     ActionList list;
       
   425 
       
   426     for (int ii = 0; ii < d->properties.count(); ++ii) {
       
   427 
       
   428         QByteArray property = d->properties.at(ii).first;
       
   429 
       
   430         QDeclarativeAction a(d->object, QString::fromUtf8(property),
       
   431                  qmlContext(this), d->properties.at(ii).second);
       
   432 
       
   433         if (a.property.isValid()) {
       
   434             a.restore = restoreEntryValues();
       
   435             list << a;
       
   436         }
       
   437     }
       
   438 
       
   439     for (int ii = 0; ii < d->signalReplacements.count(); ++ii) {
       
   440 
       
   441         QDeclarativeReplaceSignalHandler *handler = d->signalReplacements.at(ii);
       
   442 
       
   443         if (handler->property.isValid()) {
       
   444             QDeclarativeAction a;
       
   445             a.event = handler;
       
   446             list << a;
       
   447         }
       
   448     }
       
   449 
       
   450     for (int ii = 0; ii < d->expressions.count(); ++ii) {
       
   451 
       
   452         QByteArray property = d->expressions.at(ii).first;
       
   453         QDeclarativeProperty prop = d->property(property);
       
   454 
       
   455         if (prop.isValid()) {
       
   456             QDeclarativeAction a;
       
   457             a.restore = restoreEntryValues();
       
   458             a.property = prop;
       
   459             a.fromValue = a.property.read();
       
   460             a.specifiedObject = d->object;
       
   461             a.specifiedProperty = QString::fromUtf8(property);
       
   462 
       
   463             if (d->isExplicit) {
       
   464                 a.toValue = d->expressions.at(ii).second->evaluate();
       
   465             } else {
       
   466                 QDeclarativeExpression *e = d->expressions.at(ii).second;
       
   467                 QDeclarativeBinding *newBinding = 
       
   468                     new QDeclarativeBinding(e->expression(), object(), qmlContext(this));
       
   469                 newBinding->setTarget(prop);
       
   470                 newBinding->setSourceLocation(e->sourceFile(), e->lineNumber());
       
   471                 a.toBinding = newBinding;
       
   472                 a.deletableToBinding = true;
       
   473             }
       
   474 
       
   475             list << a;
       
   476         }
       
   477     }
       
   478 
       
   479     return list;
       
   480 }
       
   481 
       
   482 /*!
       
   483     \qmlproperty bool PropertyChanges::explicit
       
   484 
       
   485     If explicit is set to true, any potential bindings will be interpreted as
       
   486     once-off assignments that occur when the state is entered.
       
   487 
       
   488     In the following example, the addition of explicit prevents myItem.width from
       
   489     being bound to parent.width. Instead, it is assigned the value of parent.width
       
   490     at the time of the state change.
       
   491     \qml
       
   492     PropertyChanges {
       
   493         target: myItem
       
   494         explicit: true
       
   495         width: parent.width
       
   496     }
       
   497     \endqml
       
   498 
       
   499     By default, explicit is false.
       
   500 */
       
   501 bool QDeclarativePropertyChanges::isExplicit() const
       
   502 {
       
   503     Q_D(const QDeclarativePropertyChanges);
       
   504     return d->isExplicit;
       
   505 }
       
   506 
       
   507 void QDeclarativePropertyChanges::setIsExplicit(bool e)
       
   508 {
       
   509     Q_D(QDeclarativePropertyChanges);
       
   510     d->isExplicit = e;
       
   511 }
       
   512 
       
   513 QT_END_NAMESPACE