1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtDeclarative module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "private/qdeclarativepropertychanges_p.h"
43 
44 #include "private/qdeclarativeopenmetaobject_p.h"
45 #include "private/qdeclarativerewrite_p.h"
46 #include "private/qdeclarativeengine_p.h"
47 #include "private/qdeclarativecompiler_p.h"
48 
49 #include <qdeclarativeinfo.h>
50 #include <qdeclarativecustomparser_p.h>
51 #include <qdeclarativeparser_p.h>
52 #include <qdeclarativeexpression.h>
53 #include <qdeclarativebinding_p.h>
54 #include <qdeclarativecontext.h>
55 #include <qdeclarativeguard_p.h>
56 #include <qdeclarativeproperty_p.h>
57 #include <qdeclarativecontext_p.h>
58 #include <qdeclarativestate_p_p.h>
59 
60 #include <QtCore/qdebug.h>
61 
62 #include <private/qobject_p.h>
63 
64 QT_BEGIN_NAMESPACE
65 
66 /*!
67     \qmlclass PropertyChanges QDeclarativePropertyChanges
68     \ingroup qml-state-elements
69     \since 4.7
70     \brief The PropertyChanges element describes new property bindings or values for a state.
71 
72     PropertyChanges is used to define the property values or bindings in a
73     \l State. This enables an item's property values to be changed when it
74     \l {QML States}{changes between states}.
75 
76     To create a PropertyChanges object, specify the \l target item whose
77     properties are to be modified, and define the new property values or
78     bindings. For example:
79 
80     \snippet doc/src/snippets/declarative/propertychanges.qml import
81     \codeline
82     \snippet doc/src/snippets/declarative/propertychanges.qml 0
83 
84     When the mouse is pressed, the \l Rectangle changes to the \e resized
85     state. In this state, the PropertyChanges object sets the rectangle's
86     color to blue and the \c height value to that of \c container.height.
87 
88     Note this automatically binds \c rect.height to \c container.height
89     in the \e resized state. If a property binding should not be
90     established, and the height should just be set to the value of
91     \c container.height at the time of the state change, set the \l explicit
92     property to \c true.
93 
94     A PropertyChanges object can also override the default signal handler
95     for an object to implement a signal handler specific to the new state:
96 
97     \qml
98     PropertyChanges {
99         target: myMouseArea
100         onClicked: doSomethingDifferent()
101     }
102     \endqml
103 
104     \note PropertyChanges can be used to change anchor margins, but not other anchor
105     values; use AnchorChanges for this instead. Similarly, to change an \l Item's
106     \l {Item::}{parent} value, use ParentChanges instead.
107 
108 
109     \section2 Resetting property values
110 
111     The \c undefined value can be used to reset the property value for a state.
112     In the following example, when \c theText changes to the \e widerText
113     state, its \c width property is reset, giving the text its natural width
114     and displaying the whole string on a single line.
115 
116     \snippet doc/src/snippets/declarative/propertychanges.qml reset
117 
118 
119     \section2 Immediate property changes in transitions
120 
121     When \l{QML Animation and Transitions}{Transitions} are used to animate
122     state changes, they animate properties from their values in the current
123     state to those defined in the new state (as defined by PropertyChanges
124     objects). However, it is sometimes desirable to set a property value
125     \e immediately during a \l Transition, without animation; in these cases,
126     the PropertyAction element can be used to force an immediate property
127     change.
128 
129     See the PropertyAction documentation for more details.
130 
131     \sa {declarative/animation/states}{states example}, {qmlstate}{States}, QtDeclarative
132 */
133 
134 /*!
135     \qmlproperty Object PropertyChanges::target
136     This property holds the object which contains the properties to be changed.
137 */
138 
139 class QDeclarativeReplaceSignalHandler : public QDeclarativeActionEvent
140 {
141 public:
QDeclarativeReplaceSignalHandler()142     QDeclarativeReplaceSignalHandler() : expression(0), reverseExpression(0),
143                                 rewindExpression(0), ownedExpression(0) {}
~QDeclarativeReplaceSignalHandler()144     ~QDeclarativeReplaceSignalHandler() {
145         delete ownedExpression;
146     }
147 
typeName() const148     virtual QString typeName() const { return QLatin1String("ReplaceSignalHandler"); }
149 
150     QDeclarativeProperty property;
151     QDeclarativeExpression *expression;
152     QDeclarativeExpression *reverseExpression;
153     QDeclarativeExpression *rewindExpression;
154     QDeclarativeGuard<QDeclarativeExpression> ownedExpression;
155 
execute(Reason)156     virtual void execute(Reason) {
157         ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, expression);
158         if (ownedExpression == expression)
159             ownedExpression = 0;
160     }
161 
isReversable()162     virtual bool isReversable() { return true; }
reverse(Reason)163     virtual void reverse(Reason) {
164         ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, reverseExpression);
165         if (ownedExpression == reverseExpression)
166             ownedExpression = 0;
167     }
168 
saveOriginals()169     virtual void saveOriginals() {
170         saveCurrentValues();
171         reverseExpression = rewindExpression;
172     }
173 
needsCopy()174     virtual bool needsCopy() { return true; }
copyOriginals(QDeclarativeActionEvent * other)175     virtual void copyOriginals(QDeclarativeActionEvent *other)
176     {
177         QDeclarativeReplaceSignalHandler *rsh = static_cast<QDeclarativeReplaceSignalHandler*>(other);
178         saveCurrentValues();
179         if (rsh == this)
180             return;
181         reverseExpression = rsh->reverseExpression;
182         if (rsh->ownedExpression == reverseExpression) {
183             ownedExpression = rsh->ownedExpression;
184             rsh->ownedExpression = 0;
185         }
186     }
187 
rewind()188     virtual void rewind() {
189         ownedExpression = QDeclarativePropertyPrivate::setSignalExpression(property, rewindExpression);
190         if (ownedExpression == rewindExpression)
191             ownedExpression = 0;
192     }
saveCurrentValues()193     virtual void saveCurrentValues() {
194         rewindExpression = QDeclarativePropertyPrivate::signalExpression(property);
195     }
196 
override(QDeclarativeActionEvent * other)197     virtual bool override(QDeclarativeActionEvent*other) {
198         if (other == this)
199             return true;
200         if (other->typeName() != typeName())
201             return false;
202         if (static_cast<QDeclarativeReplaceSignalHandler*>(other)->property == property)
203             return true;
204         return false;
205     }
206 };
207 
208 
209 class QDeclarativePropertyChangesPrivate : public QDeclarativeStateOperationPrivate
210 {
211     Q_DECLARE_PUBLIC(QDeclarativePropertyChanges)
212 public:
QDeclarativePropertyChangesPrivate()213     QDeclarativePropertyChangesPrivate() : decoded(true), restore(true),
214                                 isExplicit(false) {}
215 
216     QDeclarativeGuard<QObject> object;
217     QByteArray data;
218 
219     bool decoded : 1;
220     bool restore : 1;
221     bool isExplicit : 1;
222 
223     void decode();
224 
225     class ExpressionChange {
226     public:
ExpressionChange(const QString & _name,QDeclarativeBinding::Identifier _id,QDeclarativeExpression * _expr)227         ExpressionChange(const QString &_name,
228                          QDeclarativeBinding::Identifier _id,
229                          QDeclarativeExpression *_expr)
230             : name(_name), id(_id), expression(_expr) {}
231         QString name;
232         QDeclarativeBinding::Identifier id;
233         QDeclarativeExpression *expression;
234     };
235 
236     QList<QPair<QString, QVariant> > properties;
237     QList<ExpressionChange> expressions;
238     QList<QDeclarativeReplaceSignalHandler*> signalReplacements;
239 
240     QDeclarativeProperty property(const QString &);
241 };
242 
243 void
compileList(QList<QPair<QByteArray,QVariant>> & list,const QByteArray & pre,const QDeclarativeCustomParserProperty & prop)244 QDeclarativePropertyChangesParser::compileList(QList<QPair<QByteArray, QVariant> > &list,
245                                      const QByteArray &pre,
246                                      const QDeclarativeCustomParserProperty &prop)
247 {
248     QByteArray propName = pre + prop.name();
249 
250     QList<QVariant> values = prop.assignedValues();
251     for (int ii = 0; ii < values.count(); ++ii) {
252         const QVariant &value = values.at(ii);
253 
254         if (value.userType() == qMetaTypeId<QDeclarativeCustomParserNode>()) {
255             error(qvariant_cast<QDeclarativeCustomParserNode>(value),
256                   QDeclarativePropertyChanges::tr("PropertyChanges does not support creating state-specific objects."));
257             continue;
258         } else if(value.userType() == qMetaTypeId<QDeclarativeCustomParserProperty>()) {
259 
260             QDeclarativeCustomParserProperty prop =
261                 qvariant_cast<QDeclarativeCustomParserProperty>(value);
262             QByteArray pre = propName + '.';
263             compileList(list, pre, prop);
264 
265         } else {
266             list << qMakePair(propName, value);
267         }
268     }
269 }
270 
271 QByteArray
compile(const QList<QDeclarativeCustomParserProperty> & props)272 QDeclarativePropertyChangesParser::compile(const QList<QDeclarativeCustomParserProperty> &props)
273 {
274     QList<QPair<QByteArray, QVariant> > data;
275     for(int ii = 0; ii < props.count(); ++ii)
276         compileList(data, QByteArray(), props.at(ii));
277 
278     QByteArray rv;
279     QDataStream ds(&rv, QIODevice::WriteOnly);
280 
281     ds << data.count();
282     for(int ii = 0; ii < data.count(); ++ii) {
283         QDeclarativeParser::Variant v = qvariant_cast<QDeclarativeParser::Variant>(data.at(ii).second);
284         QVariant var;
285         bool isScript = v.isScript();
286         QDeclarativeBinding::Identifier id = 0;
287         switch(v.type()) {
288         case QDeclarativeParser::Variant::Boolean:
289             var = QVariant(v.asBoolean());
290             break;
291         case QDeclarativeParser::Variant::Number:
292             var = QVariant(v.asNumber());
293             break;
294         case QDeclarativeParser::Variant::String:
295             var = QVariant(v.asString());
296             break;
297         case QDeclarativeParser::Variant::Invalid:
298         case QDeclarativeParser::Variant::Script:
299             var = QVariant(v.asScript());
300             {
301                 // Pre-rewrite the expression
302                 QString expression = v.asScript();
303                 id = rewriteBinding(expression, data.at(ii).first); //### recreates the AST, which is slow
304             }
305             break;
306         }
307 
308         ds << QString::fromUtf8(data.at(ii).first) << isScript << var;
309         if (isScript)
310             ds << id;
311     }
312 
313     return rv;
314 }
315 
decode()316 void QDeclarativePropertyChangesPrivate::decode()
317 {
318     Q_Q(QDeclarativePropertyChanges);
319     if (decoded)
320         return;
321 
322     QDataStream ds(&data, QIODevice::ReadOnly);
323 
324     int count;
325     ds >> count;
326     for (int ii = 0; ii < count; ++ii) {
327         QString name;
328         bool isScript;
329         QVariant data;
330         QDeclarativeBinding::Identifier id = QDeclarativeBinding::Invalid;
331         ds >> name;
332         ds >> isScript;
333         ds >> data;
334         if (isScript)
335             ds >> id;
336 
337         QDeclarativeProperty prop = property(name);      //### better way to check for signal property?
338         if (prop.type() & QDeclarativeProperty::SignalProperty) {
339             QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(q), object, data.toString());
340             QDeclarativeData *ddata = QDeclarativeData::get(q);
341             if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty())
342                 expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber);
343             QDeclarativeReplaceSignalHandler *handler = new QDeclarativeReplaceSignalHandler;
344             handler->property = prop;
345             handler->expression = expression;
346             signalReplacements << handler;
347         } else if (isScript) {
348             QDeclarativeExpression *expression = new QDeclarativeExpression(qmlContext(q), object, data.toString());
349             QDeclarativeData *ddata = QDeclarativeData::get(q);
350             if (ddata && ddata->outerContext && !ddata->outerContext->url.isEmpty())
351                 expression->setSourceLocation(ddata->outerContext->url.toString(), ddata->lineNumber);
352             expressions << ExpressionChange(name, id, expression);
353         } else {
354             properties << qMakePair(name, data);
355         }
356     }
357 
358     decoded = true;
359     data.clear();
360 }
361 
setCustomData(QObject * object,const QByteArray & data)362 void QDeclarativePropertyChangesParser::setCustomData(QObject *object,
363                                             const QByteArray &data)
364 {
365     QDeclarativePropertyChangesPrivate *p =
366         static_cast<QDeclarativePropertyChangesPrivate *>(QObjectPrivate::get(object));
367     p->data = data;
368     p->decoded = false;
369 }
370 
QDeclarativePropertyChanges()371 QDeclarativePropertyChanges::QDeclarativePropertyChanges()
372 : QDeclarativeStateOperation(*(new QDeclarativePropertyChangesPrivate))
373 {
374 }
375 
~QDeclarativePropertyChanges()376 QDeclarativePropertyChanges::~QDeclarativePropertyChanges()
377 {
378     Q_D(QDeclarativePropertyChanges);
379     for(int ii = 0; ii < d->expressions.count(); ++ii)
380         delete d->expressions.at(ii).expression;
381     for(int ii = 0; ii < d->signalReplacements.count(); ++ii)
382         delete d->signalReplacements.at(ii);
383 }
384 
object() const385 QObject *QDeclarativePropertyChanges::object() const
386 {
387     Q_D(const QDeclarativePropertyChanges);
388     return d->object;
389 }
390 
setObject(QObject * o)391 void QDeclarativePropertyChanges::setObject(QObject *o)
392 {
393     Q_D(QDeclarativePropertyChanges);
394     d->object = o;
395 }
396 
397 /*!
398     \qmlproperty bool PropertyChanges::restoreEntryValues
399 
400     This property holds whether the previous values should be restored when
401     leaving the state.
402 
403     The default value is \c true. Setting this value to \c false creates a
404     temporary state that has permanent effects on property values.
405 */
restoreEntryValues() const406 bool QDeclarativePropertyChanges::restoreEntryValues() const
407 {
408     Q_D(const QDeclarativePropertyChanges);
409     return d->restore;
410 }
411 
setRestoreEntryValues(bool v)412 void QDeclarativePropertyChanges::setRestoreEntryValues(bool v)
413 {
414     Q_D(QDeclarativePropertyChanges);
415     d->restore = v;
416 }
417 
418 QDeclarativeProperty
property(const QString & property)419 QDeclarativePropertyChangesPrivate::property(const QString &property)
420 {
421     Q_Q(QDeclarativePropertyChanges);
422     QDeclarativeProperty prop(object, property, qmlContext(q));
423     if (!prop.isValid()) {
424         qmlInfo(q) << QDeclarativePropertyChanges::tr("Cannot assign to non-existent property \"%1\"").arg(property);
425         return QDeclarativeProperty();
426     } else if (!(prop.type() & QDeclarativeProperty::SignalProperty) && !prop.isWritable()) {
427         qmlInfo(q) << QDeclarativePropertyChanges::tr("Cannot assign to read-only property \"%1\"").arg(property);
428         return QDeclarativeProperty();
429     }
430     return prop;
431 }
432 
actions()433 QDeclarativePropertyChanges::ActionList QDeclarativePropertyChanges::actions()
434 {
435     Q_D(QDeclarativePropertyChanges);
436 
437     d->decode();
438 
439     ActionList list;
440 
441     for (int ii = 0; ii < d->properties.count(); ++ii) {
442 
443         QDeclarativeAction a(d->object, d->properties.at(ii).first,
444                  qmlContext(this), d->properties.at(ii).second);
445 
446         if (a.property.isValid()) {
447             a.restore = restoreEntryValues();
448             list << a;
449         }
450     }
451 
452     for (int ii = 0; ii < d->signalReplacements.count(); ++ii) {
453 
454         QDeclarativeReplaceSignalHandler *handler = d->signalReplacements.at(ii);
455 
456         if (handler->property.isValid()) {
457             QDeclarativeAction a;
458             a.event = handler;
459             list << a;
460         }
461     }
462 
463     for (int ii = 0; ii < d->expressions.count(); ++ii) {
464 
465         const QString &property = d->expressions.at(ii).name;
466         QDeclarativeProperty prop = d->property(property);
467 
468         if (prop.isValid()) {
469             QDeclarativeAction a;
470             a.restore = restoreEntryValues();
471             a.property = prop;
472             a.fromValue = a.property.read();
473             a.specifiedObject = d->object;
474             a.specifiedProperty = property;
475 
476             if (d->isExplicit) {
477                 a.toValue = d->expressions.at(ii).expression->evaluate();
478             } else {
479                 QDeclarativeExpression *e = d->expressions.at(ii).expression;
480 
481                 QDeclarativeBinding::Identifier id = d->expressions.at(ii).id;
482                 QDeclarativeBinding *newBinding = id != QDeclarativeBinding::Invalid ? QDeclarativeBinding::createBinding(id, object(), qmlContext(this), e->sourceFile(), e->lineNumber()) : 0;
483                 if (!newBinding) {
484                     newBinding = new QDeclarativeBinding(e->expression(), object(), qmlContext(this));
485                     newBinding->setSourceLocation(e->sourceFile(), e->lineNumber());
486                 }
487                 newBinding->setTarget(prop);
488                 a.toBinding = newBinding;
489                 a.deletableToBinding = true;
490             }
491 
492             list << a;
493         }
494     }
495 
496     return list;
497 }
498 
499 /*!
500     \qmlproperty bool PropertyChanges::explicit
501 
502     If explicit is set to true, any potential bindings will be interpreted as
503     once-off assignments that occur when the state is entered.
504 
505     In the following example, the addition of explicit prevents \c myItem.width from
506     being bound to \c parent.width. Instead, it is assigned the value of \c parent.width
507     at the time of the state change.
508     \qml
509     PropertyChanges {
510         target: myItem
511         explicit: true
512         width: parent.width
513     }
514     \endqml
515 
516     By default, explicit is false.
517 */
isExplicit() const518 bool QDeclarativePropertyChanges::isExplicit() const
519 {
520     Q_D(const QDeclarativePropertyChanges);
521     return d->isExplicit;
522 }
523 
setIsExplicit(bool e)524 void QDeclarativePropertyChanges::setIsExplicit(bool e)
525 {
526     Q_D(QDeclarativePropertyChanges);
527     d->isExplicit = e;
528 }
529 
containsValue(const QString & name) const530 bool QDeclarativePropertyChanges::containsValue(const QString &name) const
531 {
532     Q_D(const QDeclarativePropertyChanges);
533     typedef QPair<QString, QVariant> PropertyEntry;
534 
535     QListIterator<PropertyEntry> propertyIterator(d->properties);
536     while (propertyIterator.hasNext()) {
537         const PropertyEntry &entry = propertyIterator.next();
538         if (entry.first == name) {
539             return true;
540         }
541     }
542 
543     return false;
544 }
545 
containsExpression(const QString & name) const546 bool QDeclarativePropertyChanges::containsExpression(const QString &name) const
547 {
548     Q_D(const QDeclarativePropertyChanges);
549     typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry;
550 
551     QListIterator<ExpressionEntry> expressionIterator(d->expressions);
552     while (expressionIterator.hasNext()) {
553         const ExpressionEntry &entry = expressionIterator.next();
554         if (entry.name == name) {
555             return true;
556         }
557     }
558 
559     return false;
560 }
561 
containsProperty(const QString & name) const562 bool QDeclarativePropertyChanges::containsProperty(const QString &name) const
563 {
564     return containsValue(name) || containsExpression(name);
565 }
566 
changeValue(const QString & name,const QVariant & value)567 void QDeclarativePropertyChanges::changeValue(const QString &name, const QVariant &value)
568 {
569     Q_D(QDeclarativePropertyChanges);
570     typedef QPair<QString, QVariant> PropertyEntry;
571     typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry;
572 
573     QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions);
574     while (expressionIterator.hasNext()) {
575         const ExpressionEntry &entry = expressionIterator.next();
576         if (entry.name == name) {
577             expressionIterator.remove();
578             if (state() && state()->isStateActive()) {
579                 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name));
580                 if (oldBinding) {
581                     QDeclarativePropertyPrivate::setBinding(d->property(name), 0);
582                     oldBinding->destroy();
583                 }
584                 d->property(name).write(value);
585             }
586 
587             d->properties.append(PropertyEntry(name, value));
588             return;
589         }
590     }
591 
592     QMutableListIterator<PropertyEntry> propertyIterator(d->properties);
593     while (propertyIterator.hasNext()) {
594         PropertyEntry &entry = propertyIterator.next();
595         if (entry.first == name) {
596             entry.second = value;
597             if (state() && state()->isStateActive())
598                 d->property(name).write(value);
599             return;
600         }
601     }
602 
603     QDeclarativeAction action;
604     action.restore = restoreEntryValues();
605     action.property = d->property(name);
606     action.fromValue = action.property.read();
607     action.specifiedObject = object();
608     action.specifiedProperty = name;
609     action.toValue = value;
610 
611     propertyIterator.insert(PropertyEntry(name, value));
612     if (state() && state()->isStateActive()) {
613         state()->addEntryToRevertList(action);
614         QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(action.property);
615         if (oldBinding)
616             oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor);
617         d->property(name).write(value);
618     }
619 }
620 
changeExpression(const QString & name,const QString & expression)621 void QDeclarativePropertyChanges::changeExpression(const QString &name, const QString &expression)
622 {
623     Q_D(QDeclarativePropertyChanges);
624     typedef QPair<QString, QVariant> PropertyEntry;
625     typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry;
626 
627     bool hadValue = false;
628 
629     QMutableListIterator<PropertyEntry> propertyIterator(d->properties);
630     while (propertyIterator.hasNext()) {
631         PropertyEntry &entry = propertyIterator.next();
632         if (entry.first == name) {
633             propertyIterator.remove();
634             hadValue = true;
635             break;
636         }
637     }
638 
639     QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions);
640     while (expressionIterator.hasNext()) {
641         const ExpressionEntry &entry = expressionIterator.next();
642         if (entry.name == name) {
643             entry.expression->setExpression(expression);
644             if (state() && state()->isStateActive()) {
645                 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name));
646                 if (oldBinding) {
647                        QDeclarativePropertyPrivate::setBinding(d->property(name), 0);
648                        oldBinding->destroy();
649                 }
650 
651                 QDeclarativeBinding *newBinding = new QDeclarativeBinding(expression, object(), qmlContext(this));
652                 newBinding->setTarget(d->property(name));
653                 QDeclarativePropertyPrivate::setBinding(d->property(name), newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor);
654             }
655             return;
656         }
657     }
658 
659     QDeclarativeExpression *newExpression = new QDeclarativeExpression(qmlContext(this), d->object, expression);
660     expressionIterator.insert(ExpressionEntry(name, QDeclarativeBinding::Invalid, newExpression));
661 
662     if (state() && state()->isStateActive()) {
663         if (hadValue) {
664             QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(d->property(name));
665             if (oldBinding) {
666                 oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor);
667                 state()->changeBindingInRevertList(object(), name, oldBinding);
668             }
669 
670             QDeclarativeBinding *newBinding = new QDeclarativeBinding(expression, object(), qmlContext(this));
671             newBinding->setTarget(d->property(name));
672             QDeclarativePropertyPrivate::setBinding(d->property(name), newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor);
673         } else {
674             QDeclarativeAction action;
675             action.restore = restoreEntryValues();
676             action.property = d->property(name);
677             action.fromValue = action.property.read();
678             action.specifiedObject = object();
679             action.specifiedProperty = name;
680 
681 
682             if (d->isExplicit) {
683                 action.toValue = newExpression->evaluate();
684             } else {
685                 QDeclarativeBinding *newBinding = new QDeclarativeBinding(newExpression->expression(), object(), qmlContext(this));
686                 newBinding->setTarget(d->property(name));
687                 action.toBinding = newBinding;
688                 action.deletableToBinding = true;
689 
690                 state()->addEntryToRevertList(action);
691                 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(action.property);
692                 if (oldBinding)
693                     oldBinding->setEnabled(false, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor);
694 
695                 QDeclarativePropertyPrivate::setBinding(action.property, newBinding, QDeclarativePropertyPrivate::DontRemoveBinding | QDeclarativePropertyPrivate::BypassInterceptor);
696             }
697         }
698     }
699     // what about the signal handler?
700 }
701 
property(const QString & name) const702 QVariant QDeclarativePropertyChanges::property(const QString &name) const
703 {
704     Q_D(const QDeclarativePropertyChanges);
705     typedef QPair<QString, QVariant> PropertyEntry;
706     typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry;
707 
708     QListIterator<PropertyEntry> propertyIterator(d->properties);
709     while (propertyIterator.hasNext()) {
710         const PropertyEntry &entry = propertyIterator.next();
711         if (entry.first == name) {
712             return entry.second;
713         }
714     }
715 
716     QListIterator<ExpressionEntry> expressionIterator(d->expressions);
717     while (expressionIterator.hasNext()) {
718         const ExpressionEntry &entry = expressionIterator.next();
719         if (entry.name == name) {
720             return QVariant(entry.expression->expression());
721         }
722     }
723 
724     return QVariant();
725 }
726 
removeProperty(const QString & name)727 void QDeclarativePropertyChanges::removeProperty(const QString &name)
728 {
729     Q_D(QDeclarativePropertyChanges);
730     typedef QPair<QString, QVariant> PropertyEntry;
731     typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry;
732 
733     QMutableListIterator<ExpressionEntry> expressionIterator(d->expressions);
734     while (expressionIterator.hasNext()) {
735         const ExpressionEntry &entry = expressionIterator.next();
736         if (entry.name == name) {
737             expressionIterator.remove();
738             state()->removeEntryFromRevertList(object(), name);
739             return;
740         }
741     }
742 
743     QMutableListIterator<PropertyEntry> propertyIterator(d->properties);
744     while (propertyIterator.hasNext()) {
745         const PropertyEntry &entry = propertyIterator.next();
746         if (entry.first == name) {
747             propertyIterator.remove();
748             state()->removeEntryFromRevertList(object(), name);
749             return;
750         }
751     }
752 }
753 
value(const QString & name) const754 QVariant QDeclarativePropertyChanges::value(const QString &name) const
755 {
756     Q_D(const QDeclarativePropertyChanges);
757     typedef QPair<QString, QVariant> PropertyEntry;
758 
759     QListIterator<PropertyEntry> propertyIterator(d->properties);
760     while (propertyIterator.hasNext()) {
761         const PropertyEntry &entry = propertyIterator.next();
762         if (entry.first == name) {
763             return entry.second;
764         }
765     }
766 
767     return QVariant();
768 }
769 
expression(const QString & name) const770 QString QDeclarativePropertyChanges::expression(const QString &name) const
771 {
772     Q_D(const QDeclarativePropertyChanges);
773     typedef QDeclarativePropertyChangesPrivate::ExpressionChange ExpressionEntry;
774 
775     QListIterator<ExpressionEntry> expressionIterator(d->expressions);
776     while (expressionIterator.hasNext()) {
777         const ExpressionEntry &entry = expressionIterator.next();
778         if (entry.name == name) {
779             return entry.expression->expression();
780         }
781     }
782 
783     return QString();
784 }
785 
detachFromState()786 void QDeclarativePropertyChanges::detachFromState()
787 {
788     if (state())
789         state()->removeAllEntriesFromRevertList(object());
790 }
791 
attachToState()792 void QDeclarativePropertyChanges::attachToState()
793 {
794     if (state())
795         state()->addEntriesToRevertList(actions());
796 }
797 
798 QT_END_NAMESPACE
799