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