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