source: trunk/src/declarative/util/qdeclarativestate.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 24.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "private/qdeclarativestate_p_p.h"
43#include "private/qdeclarativestate_p.h"
44
45#include "private/qdeclarativetransition_p.h"
46#include "private/qdeclarativestategroup_p.h"
47#include "private/qdeclarativestateoperations_p.h"
48#include "private/qdeclarativeanimation_p.h"
49#include "private/qdeclarativeanimation_p_p.h"
50
51#include <qdeclarativebinding_p.h>
52#include <qdeclarativeglobal_p.h>
53
54#include <QtCore/qdebug.h>
55
56QT_BEGIN_NAMESPACE
57
58DEFINE_BOOL_CONFIG_OPTION(stateChangeDebug, STATECHANGE_DEBUG);
59
60QDeclarativeAction::QDeclarativeAction()
61: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false), fromBinding(0), event(0),
62 specifiedObject(0)
63{
64}
65
66QDeclarativeAction::QDeclarativeAction(QObject *target, const QString &propertyName,
67 const QVariant &value)
68: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false),
69 property(target, propertyName), toValue(value),
70 fromBinding(0), event(0),
71 specifiedObject(target), specifiedProperty(propertyName)
72{
73 if (property.isValid())
74 fromValue = property.read();
75}
76
77QDeclarativeAction::QDeclarativeAction(QObject *target, const QString &propertyName,
78 QDeclarativeContext *context, const QVariant &value)
79: restore(true), actionDone(false), reverseEvent(false), deletableToBinding(false),
80 property(target, propertyName, context), toValue(value),
81 fromBinding(0), event(0),
82 specifiedObject(target), specifiedProperty(propertyName)
83{
84 if (property.isValid())
85 fromValue = property.read();
86}
87
88
89QDeclarativeActionEvent::~QDeclarativeActionEvent()
90{
91}
92
93QString QDeclarativeActionEvent::typeName() const
94{
95 return QString();
96}
97
98void QDeclarativeActionEvent::execute(Reason)
99{
100}
101
102bool QDeclarativeActionEvent::isReversable()
103{
104 return false;
105}
106
107void QDeclarativeActionEvent::reverse(Reason)
108{
109}
110
111bool QDeclarativeActionEvent::changesBindings()
112{
113 return false;
114}
115
116void QDeclarativeActionEvent::clearBindings()
117{
118}
119
120bool QDeclarativeActionEvent::override(QDeclarativeActionEvent *other)
121{
122 Q_UNUSED(other);
123 return false;
124}
125
126QDeclarativeStateOperation::QDeclarativeStateOperation(QObjectPrivate &dd, QObject *parent)
127 : QObject(dd, parent)
128{
129}
130
131/*!
132 \qmlclass State QDeclarativeState
133 \ingroup qml-state-elements
134 \since 4.7
135 \brief The State element defines configurations of objects and properties.
136
137 A \e state is a set of batched changes from the default configuration.
138
139 All items have a default state that defines the default configuration of objects
140 and property values. New states can be defined by adding State items to the \l {Item::states}{states} property to
141 allow items to switch between different configurations. These configurations
142 can, for example, be used to apply different sets of property values or execute
143 different scripts.
144
145 The following example displays a single \l Rectangle. In the default state, the rectangle
146 is colored black. In the "clicked" state, a PropertyChanges element changes the
147 rectangle's color to red. Clicking within the MouseArea toggles the rectangle's state
148 between the default state and the "clicked" state, thus toggling the color of the
149 rectangle between black and red.
150
151 \snippet doc/src/snippets/declarative/state.qml 0
152
153 Notice the default state is referred to using an empty string ("").
154
155 States are commonly used together with \l {Transitions} to provide
156 animations when state changes occur.
157
158 \note Setting the state of an object from within another state of the same object is
159 not allowed.
160
161 \sa {declarative/animation/states}{states example}, {qmlstates}{States},
162 {qdeclarativeanimation.html#transitions}{QML Transitions}, QtDeclarative
163*/
164QDeclarativeState::QDeclarativeState(QObject *parent)
165: QObject(*(new QDeclarativeStatePrivate), parent)
166{
167 Q_D(QDeclarativeState);
168 d->transitionManager.setState(this);
169}
170
171QDeclarativeState::~QDeclarativeState()
172{
173 Q_D(QDeclarativeState);
174 if (d->group)
175 d->group->removeState(this);
176}
177
178/*!
179 \qmlproperty string State::name
180 This property holds the name of the state.
181
182 Each state should have a unique name within its item.
183*/
184QString QDeclarativeState::name() const
185{
186 Q_D(const QDeclarativeState);
187 return d->name;
188}
189
190void QDeclarativeState::setName(const QString &n)
191{
192 Q_D(QDeclarativeState);
193 d->name = n;
194 d->named = true;
195}
196
197bool QDeclarativeState::isNamed() const
198{
199 Q_D(const QDeclarativeState);
200 return d->named;
201}
202
203bool QDeclarativeState::isWhenKnown() const
204{
205 Q_D(const QDeclarativeState);
206 return d->when != 0;
207}
208
209/*!
210 \qmlproperty bool State::when
211 This property holds when the state should be applied.
212
213 This should be set to an expression that evaluates to \c true when you want the state to
214 be applied. For example, the following \l Rectangle changes in and out of the "hidden"
215 state when the \l MouseArea is pressed:
216
217 \snippet doc/src/snippets/declarative/state-when.qml 0
218
219 If multiple states in a group have \c when clauses that evaluate to \c true at the same time,
220 the first matching state will be applied. For example, in the following snippet
221 \c state1 will always be selected rather than \c state2 when sharedCondition becomes
222 \c true.
223 \qml
224 states: [
225 State { name: "state1"; when: sharedCondition },
226 State { name: "state2"; when: sharedCondition }
227 ]
228 \endqml
229*/
230QDeclarativeBinding *QDeclarativeState::when() const
231{
232 Q_D(const QDeclarativeState);
233 return d->when;
234}
235
236void QDeclarativeState::setWhen(QDeclarativeBinding *when)
237{
238 Q_D(QDeclarativeState);
239 d->when = when;
240 if (d->group)
241 d->group->updateAutoState();
242}
243
244/*!
245 \qmlproperty string State::extend
246 This property holds the state that this state extends.
247
248 When a state extends another state, it inherits all the changes of that state.
249
250 The state being extended is treated as the base state in regards to
251 the changes specified by the extending state.
252*/
253QString QDeclarativeState::extends() const
254{
255 Q_D(const QDeclarativeState);
256 return d->extends;
257}
258
259void QDeclarativeState::setExtends(const QString &extends)
260{
261 Q_D(QDeclarativeState);
262 d->extends = extends;
263}
264
265/*!
266 \qmlproperty list<Change> State::changes
267 This property holds the changes to apply for this state
268 \default
269
270 By default these changes are applied against the default state. If the state
271 extends another state, then the changes are applied against the state being
272 extended.
273*/
274QDeclarativeListProperty<QDeclarativeStateOperation> QDeclarativeState::changes()
275{
276 Q_D(QDeclarativeState);
277 return QDeclarativeListProperty<QDeclarativeStateOperation>(this, &d->operations, QDeclarativeStatePrivate::operations_append,
278 QDeclarativeStatePrivate::operations_count, QDeclarativeStatePrivate::operations_at,
279 QDeclarativeStatePrivate::operations_clear);
280}
281
282int QDeclarativeState::operationCount() const
283{
284 Q_D(const QDeclarativeState);
285 return d->operations.count();
286}
287
288QDeclarativeStateOperation *QDeclarativeState::operationAt(int index) const
289{
290 Q_D(const QDeclarativeState);
291 return d->operations.at(index);
292}
293
294QDeclarativeState &QDeclarativeState::operator<<(QDeclarativeStateOperation *op)
295{
296 Q_D(QDeclarativeState);
297 d->operations.append(QDeclarativeStatePrivate::OperationGuard(op, &d->operations));
298 return *this;
299}
300
301void QDeclarativeStatePrivate::complete()
302{
303 Q_Q(QDeclarativeState);
304
305 for (int ii = 0; ii < reverting.count(); ++ii) {
306 for (int jj = 0; jj < revertList.count(); ++jj) {
307 if (revertList.at(jj).property() == reverting.at(ii)) {
308 revertList.removeAt(jj);
309 break;
310 }
311 }
312 }
313 reverting.clear();
314
315 emit q->completed();
316}
317
318// Generate a list of actions for this state. This includes coelescing state
319// actions that this state "extends"
320QDeclarativeStateOperation::ActionList
321QDeclarativeStatePrivate::generateActionList(QDeclarativeStateGroup *group) const
322{
323 QDeclarativeStateOperation::ActionList applyList;
324 if (inState)
325 return applyList;
326
327 // Prevent "extends" recursion
328 inState = true;
329
330 if (!extends.isEmpty()) {
331 QList<QDeclarativeState *> states = group->states();
332 for (int ii = 0; ii < states.count(); ++ii)
333 if (states.at(ii)->name() == extends) {
334 qmlExecuteDeferred(states.at(ii));
335 applyList = static_cast<QDeclarativeStatePrivate*>(states.at(ii)->d_func())->generateActionList(group);
336 }
337 }
338
339 foreach(QDeclarativeStateOperation *op, operations)
340 applyList << op->actions();
341
342 inState = false;
343 return applyList;
344}
345
346QDeclarativeStateGroup *QDeclarativeState::stateGroup() const
347{
348 Q_D(const QDeclarativeState);
349 return d->group;
350}
351
352void QDeclarativeState::setStateGroup(QDeclarativeStateGroup *group)
353{
354 Q_D(QDeclarativeState);
355 d->group = group;
356}
357
358void QDeclarativeState::cancel()
359{
360 Q_D(QDeclarativeState);
361 d->transitionManager.cancel();
362}
363
364void QDeclarativeAction::deleteFromBinding()
365{
366 if (fromBinding) {
367 QDeclarativePropertyPrivate::setBinding(property, 0);
368 fromBinding->destroy();
369 fromBinding = 0;
370 }
371}
372
373bool QDeclarativeState::containsPropertyInRevertList(QObject *target, const QByteArray &name) const
374{
375 Q_D(const QDeclarativeState);
376
377 if (isStateActive()) {
378 QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
379
380 while (revertListIterator.hasNext()) {
381 const QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
382 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty().toUtf8() == name)
383 return true;
384 }
385 }
386
387 return false;
388}
389
390bool QDeclarativeState::changeValueInRevertList(QObject *target, const QByteArray &name, const QVariant &revertValue)
391{
392 Q_D(QDeclarativeState);
393
394 if (isStateActive()) {
395 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
396
397 while (revertListIterator.hasNext()) {
398 QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
399 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty().toUtf8() == name) {
400 simpleAction.setValue(revertValue);
401 return true;
402 }
403 }
404 }
405
406 return false;
407}
408
409bool QDeclarativeState::changeBindingInRevertList(QObject *target, const QByteArray &name, QDeclarativeAbstractBinding *binding)
410{
411 Q_D(QDeclarativeState);
412
413 if (isStateActive()) {
414 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
415
416 while (revertListIterator.hasNext()) {
417 QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
418 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty().toUtf8() == name) {
419 if (simpleAction.binding())
420 simpleAction.binding()->destroy();
421
422 simpleAction.setBinding(binding);
423 return true;
424 }
425 }
426 }
427
428 return false;
429}
430
431bool QDeclarativeState::removeEntryFromRevertList(QObject *target, const QByteArray &name)
432{
433 Q_D(QDeclarativeState);
434
435 if (isStateActive()) {
436 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
437
438 while (revertListIterator.hasNext()) {
439 QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
440 if (simpleAction.property().object() == target && simpleAction.property().name().toUtf8() == name) {
441 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property());
442 if (oldBinding) {
443 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0);
444 oldBinding->destroy();
445 }
446
447 simpleAction.property().write(simpleAction.value());
448 if (simpleAction.binding())
449 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
450
451 revertListIterator.remove();
452 return true;
453 }
454 }
455 }
456
457 return false;
458}
459
460void QDeclarativeState::addEntryToRevertList(const QDeclarativeAction &action)
461{
462 Q_D(QDeclarativeState);
463
464 QDeclarativeSimpleAction simpleAction(action);
465
466 d->revertList.append(simpleAction);
467}
468
469void QDeclarativeState::removeAllEntriesFromRevertList(QObject *target)
470{
471 Q_D(QDeclarativeState);
472
473 if (isStateActive()) {
474 QMutableListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
475
476 while (revertListIterator.hasNext()) {
477 QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
478 if (simpleAction.property().object() == target) {
479 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property());
480 if (oldBinding) {
481 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0);
482 oldBinding->destroy();
483 }
484
485 simpleAction.property().write(simpleAction.value());
486 if (simpleAction.binding())
487 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
488
489 revertListIterator.remove();
490 }
491 }
492 }
493}
494
495void QDeclarativeState::addEntriesToRevertList(const QList<QDeclarativeAction> &actionList)
496{
497 Q_D(QDeclarativeState);
498 if (isStateActive()) {
499 QList<QDeclarativeSimpleAction> simpleActionList;
500
501 QListIterator<QDeclarativeAction> actionListIterator(actionList);
502 while(actionListIterator.hasNext()) {
503 const QDeclarativeAction &action = actionListIterator.next();
504 QDeclarativeSimpleAction simpleAction(action);
505 action.property.write(action.toValue);
506 if (!action.toBinding.isNull()) {
507 QDeclarativeAbstractBinding *oldBinding = QDeclarativePropertyPrivate::binding(simpleAction.property());
508 if (oldBinding)
509 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), 0);
510 QDeclarativePropertyPrivate::setBinding(simpleAction.property(), action.toBinding.data(), QDeclarativePropertyPrivate::DontRemoveBinding);
511 }
512
513 simpleActionList.append(simpleAction);
514 }
515
516 d->revertList.append(simpleActionList);
517 }
518}
519
520QVariant QDeclarativeState::valueInRevertList(QObject *target, const QByteArray &name) const
521{
522 Q_D(const QDeclarativeState);
523
524 if (isStateActive()) {
525 QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
526
527 while (revertListIterator.hasNext()) {
528 const QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
529 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty().toUtf8() == name)
530 return simpleAction.value();
531 }
532 }
533
534 return QVariant();
535}
536
537QDeclarativeAbstractBinding *QDeclarativeState::bindingInRevertList(QObject *target, const QByteArray &name) const
538{
539 Q_D(const QDeclarativeState);
540
541 if (isStateActive()) {
542 QListIterator<QDeclarativeSimpleAction> revertListIterator(d->revertList);
543
544 while (revertListIterator.hasNext()) {
545 const QDeclarativeSimpleAction &simpleAction = revertListIterator.next();
546 if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty().toUtf8() == name)
547 return simpleAction.binding();
548 }
549 }
550
551 return 0;
552}
553
554bool QDeclarativeState::isStateActive() const
555{
556 return stateGroup() && stateGroup()->state() == name();
557}
558
559void QDeclarativeState::apply(QDeclarativeStateGroup *group, QDeclarativeTransition *trans, QDeclarativeState *revert)
560{
561 Q_D(QDeclarativeState);
562
563 qmlExecuteDeferred(this);
564
565 cancel();
566 if (revert)
567 revert->cancel();
568 d->revertList.clear();
569 d->reverting.clear();
570
571 if (revert) {
572 QDeclarativeStatePrivate *revertPrivate =
573 static_cast<QDeclarativeStatePrivate*>(revert->d_func());
574 d->revertList = revertPrivate->revertList;
575 revertPrivate->revertList.clear();
576 }
577
578 // List of actions caused by this state
579 QDeclarativeStateOperation::ActionList applyList = d->generateActionList(group);
580
581 // List of actions that need to be reverted to roll back (just) this state
582 QDeclarativeStatePrivate::SimpleActionList additionalReverts;
583 // First add the reverse of all the applyList actions
584 for (int ii = 0; ii < applyList.count(); ++ii) {
585 QDeclarativeAction &action = applyList[ii];
586
587 if (action.event) {
588 if (!action.event->isReversable())
589 continue;
590 bool found = false;
591 for (int jj = 0; jj < d->revertList.count(); ++jj) {
592 QDeclarativeActionEvent *event = d->revertList.at(jj).event();
593 if (event && event->typeName() == action.event->typeName()) {
594 if (action.event->override(event)) {
595 found = true;
596
597 if (action.event != d->revertList.at(jj).event() && action.event->needsCopy()) {
598 action.event->copyOriginals(d->revertList.at(jj).event());
599
600 QDeclarativeSimpleAction r(action);
601 additionalReverts << r;
602 d->revertList.removeAt(jj);
603 --jj;
604 } else if (action.event->isRewindable()) //###why needed?
605 action.event->saveCurrentValues();
606
607 break;
608 }
609 }
610 }
611 if (!found) {
612 action.event->saveOriginals();
613 // Only need to revert the applyList action if the previous
614 // state doesn't have a higher priority revert already
615 QDeclarativeSimpleAction r(action);
616 additionalReverts << r;
617 }
618 } else {
619 bool found = false;
620 action.fromBinding = QDeclarativePropertyPrivate::binding(action.property);
621
622 for (int jj = 0; jj < d->revertList.count(); ++jj) {
623 if (d->revertList.at(jj).property() == action.property) {
624 found = true;
625 if (d->revertList.at(jj).binding() != action.fromBinding) {
626 action.deleteFromBinding();
627 }
628 break;
629 }
630 }
631
632 if (!found) {
633 if (!action.restore) {
634 action.deleteFromBinding();;
635 } else {
636 // Only need to revert the applyList action if the previous
637 // state doesn't have a higher priority revert already
638 QDeclarativeSimpleAction r(action);
639 additionalReverts << r;
640 }
641 }
642 }
643 }
644
645 // Any reverts from a previous state that aren't carried forth
646 // into this state need to be translated into apply actions
647 for (int ii = 0; ii < d->revertList.count(); ++ii) {
648 bool found = false;
649 if (d->revertList.at(ii).event()) {
650 QDeclarativeActionEvent *event = d->revertList.at(ii).event();
651 if (!event->isReversable())
652 continue;
653 for (int jj = 0; !found && jj < applyList.count(); ++jj) {
654 const QDeclarativeAction &action = applyList.at(jj);
655 if (action.event && action.event->typeName() == event->typeName()) {
656 if (action.event->override(event))
657 found = true;
658 }
659 }
660 } else {
661 for (int jj = 0; !found && jj < applyList.count(); ++jj) {
662 const QDeclarativeAction &action = applyList.at(jj);
663 if (action.property == d->revertList.at(ii).property())
664 found = true;
665 }
666 }
667 if (!found) {
668 QVariant cur = d->revertList.at(ii).property().read();
669 QDeclarativeAbstractBinding *delBinding =
670 QDeclarativePropertyPrivate::setBinding(d->revertList.at(ii).property(), 0);
671 if (delBinding)
672 delBinding->destroy();
673
674 QDeclarativeAction a;
675 a.property = d->revertList.at(ii).property();
676 a.fromValue = cur;
677 a.toValue = d->revertList.at(ii).value();
678 a.toBinding = QDeclarativeAbstractBinding::getPointer(d->revertList.at(ii).binding());
679 a.specifiedObject = d->revertList.at(ii).specifiedObject();
680 a.specifiedProperty = d->revertList.at(ii).specifiedProperty();
681 a.event = d->revertList.at(ii).event();
682 a.reverseEvent = d->revertList.at(ii).reverseEvent();
683 if (a.event && a.event->isRewindable())
684 a.event->saveCurrentValues();
685 applyList << a;
686 // Store these special reverts in the reverting list
687 d->reverting << d->revertList.at(ii).property();
688 }
689 }
690 // All the local reverts now become part of the ongoing revertList
691 d->revertList << additionalReverts;
692
693#ifndef QT_NO_DEBUG_STREAM
694 // Output for debugging
695 if (stateChangeDebug()) {
696 foreach(const QDeclarativeAction &action, applyList) {
697 if (action.event)
698 qWarning() << " QDeclarativeAction event:" << action.event->typeName();
699 else
700 qWarning() << " QDeclarativeAction:" << action.property.object()
701 << action.property.name() << "From:" << action.fromValue
702 << "To:" << action.toValue;
703 }
704 }
705#endif
706
707 d->transitionManager.transition(applyList, trans);
708}
709
710QDeclarativeStateOperation::ActionList QDeclarativeStateOperation::actions()
711{
712 return ActionList();
713}
714
715QDeclarativeState *QDeclarativeStateOperation::state() const
716{
717 Q_D(const QDeclarativeStateOperation);
718 return d->m_state;
719}
720
721void QDeclarativeStateOperation::setState(QDeclarativeState *state)
722{
723 Q_D(QDeclarativeStateOperation);
724 d->m_state = state;
725}
726
727QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.