Update to 5.0.0-beta1
[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     \qmltype State
124     \instantiates QQuickState
125     \inqmlmodule QtQuick 2
126     \ingroup qtquick-states
127     \brief Defines configurations of objects and properties
128
129     A \e state is a set of batched changes from the default configuration.
130
131     All items have a default state that defines the default configuration of objects
132     and property values. New states can be defined by adding State items to the \l {Item::states}{states} property to
133     allow items to switch between different configurations. These configurations
134     can, for example, be used to apply different sets of property values or execute
135     different scripts.
136
137     The following example displays a single \l Rectangle. In the default state, the rectangle
138     is colored black. In the "clicked" state, a PropertyChanges object changes the
139     rectangle's color to red. Clicking within the MouseArea toggles the rectangle's state
140     between the default state and the "clicked" state, thus toggling the color of the
141     rectangle between black and red.
142
143     \snippet qml/state.qml 0
144
145     Notice the default state is referred to using an empty string ("").
146
147     States are commonly used together with \l{Animation and Transitions in Qt Quick}{Transitions} to provide
148     animations when state changes occur.
149
150     \note Setting the state of an object from within another state of the same object is
151     not allowed.
152
153     \sa {declarative/animation/states}{states example}, {Qt Quick States}{Qt Quick States},
154     {Animation and Transitions in Qt Quick}{Transitions}, QtQml
155 */
156 QQuickState::QQuickState(QObject *parent)
157 : QObject(*(new QQuickStatePrivate), parent)
158 {
159     Q_D(QQuickState);
160     d->transitionManager.setState(this);
161 }
162
163 QQuickState::~QQuickState()
164 {
165     Q_D(QQuickState);
166     if (d->group)
167         d->group->removeState(this);
168 }
169
170 /*!
171     \qmlproperty string QtQuick2::State::name
172     This property holds the name of the state.
173
174     Each state should have a unique name within its item.
175 */
176 QString QQuickState::name() const
177 {
178     Q_D(const QQuickState);
179     return d->name;
180 }
181
182 void QQuickState::setName(const QString &n)
183 {
184     Q_D(QQuickState);
185     d->name = n;
186     d->named = true;
187 }
188
189 bool QQuickState::isNamed() const
190 {
191     Q_D(const QQuickState);
192     return d->named;
193 }
194
195 bool QQuickState::isWhenKnown() const
196 {
197     Q_D(const QQuickState);
198     return d->when != 0;
199 }
200
201 /*!
202     \qmlproperty bool QtQuick2::State::when
203     This property holds when the state should be applied.
204
205     This should be set to an expression that evaluates to \c true when you want the state to
206     be applied. For example, the following \l Rectangle changes in and out of the "hidden"
207     state when the \l MouseArea is pressed:
208
209     \snippet qml/state-when.qml 0
210
211     If multiple states in a group have \c when clauses that evaluate to \c true
212     at the same time, the first matching state will be applied. For example, in
213     the following snippet \c state1 will always be selected rather than
214     \c state2 when sharedCondition becomes \c true.
215     \qml
216     Item {
217         states: [
218             State { name: "state1"; when: sharedCondition },
219             State { name: "state2"; when: sharedCondition }
220         ]
221         // ...
222     }
223     \endqml
224 */
225 QQmlBinding *QQuickState::when() const
226 {
227     Q_D(const QQuickState);
228     return d->when;
229 }
230
231 void QQuickState::setWhen(QQmlBinding *when)
232 {
233     Q_D(QQuickState);
234     d->when = when;
235     if (d->group)
236         d->group->updateAutoState();
237 }
238
239 /*!
240     \qmlproperty string QtQuick2::State::extend
241     This property holds the state that this state extends.
242
243     When a state extends another state, it inherits all the changes of that state.
244
245     The state being extended is treated as the base state in regards to
246     the changes specified by the extending state.
247 */
248 QString QQuickState::extends() const
249 {
250     Q_D(const QQuickState);
251     return d->extends;
252 }
253
254 void QQuickState::setExtends(const QString &extends)
255 {
256     Q_D(QQuickState);
257     d->extends = extends;
258 }
259
260 /*!
261     \qmlproperty list<Change> QtQuick2::State::changes
262     This property holds the changes to apply for this state
263     \default
264
265     By default these changes are applied against the default state. If the state
266     extends another state, then the changes are applied against the state being
267     extended.
268 */
269 QQmlListProperty<QQuickStateOperation> QQuickState::changes()
270 {
271     Q_D(QQuickState);
272     return QQmlListProperty<QQuickStateOperation>(this, &d->operations, QQuickStatePrivate::operations_append,
273                                               QQuickStatePrivate::operations_count, QQuickStatePrivate::operations_at,
274                                               QQuickStatePrivate::operations_clear);
275 }
276
277 int QQuickState::operationCount() const
278 {
279     Q_D(const QQuickState);
280     return d->operations.count();
281 }
282
283 QQuickStateOperation *QQuickState::operationAt(int index) const
284 {
285     Q_D(const QQuickState);
286     return d->operations.at(index);
287 }
288
289 QQuickState &QQuickState::operator<<(QQuickStateOperation *op)
290 {
291     Q_D(QQuickState);
292     d->operations.append(QQuickStatePrivate::OperationGuard(op, &d->operations));
293     return *this;
294 }
295
296 void QQuickStatePrivate::complete()
297 {
298     Q_Q(QQuickState);
299
300     for (int ii = 0; ii < reverting.count(); ++ii) {
301         for (int jj = 0; jj < revertList.count(); ++jj) {
302             const QQuickRevertAction &revert = reverting.at(ii);
303             const QQuickSimpleAction &simple = revertList.at(jj);
304             if ((revert.event && simple.event() == revert.event) ||
305                 simple.property() == revert.property) {
306                 revertList.removeAt(jj);
307                 break;
308             }
309         }
310     }
311     reverting.clear();
312
313     if (group)
314         group->stateAboutToComplete();
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"
320 QQuickStateOperation::ActionList
321 QQuickStatePrivate::generateActionList() const
322 {
323     QQuickStateOperation::ActionList applyList;
324     if (inState)
325         return applyList;
326
327     // Prevent "extends" recursion
328     inState = true;
329
330     if (!extends.isEmpty()) {
331         QList<QQuickState *> states = group ? group->states() : QList<QQuickState *>();
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<QQuickStatePrivate*>(states.at(ii)->d_func())->generateActionList();
336             }
337     }
338
339     foreach(QQuickStateOperation *op, operations)
340         applyList << op->actions();
341
342     inState = false;
343     return applyList;
344 }
345
346 QQuickStateGroup *QQuickState::stateGroup() const
347 {
348     Q_D(const QQuickState);
349     return d->group;
350 }
351
352 void QQuickState::setStateGroup(QQuickStateGroup *group)
353 {
354     Q_D(QQuickState);
355     d->group = group;
356 }
357
358 void QQuickState::cancel()
359 {
360     Q_D(QQuickState);
361     d->transitionManager.cancel();
362 }
363
364 void QQuickAction::deleteFromBinding()
365 {
366     if (fromBinding) {
367         QQmlPropertyPrivate::setBinding(property, 0);
368         fromBinding->destroy();
369         fromBinding = 0;
370     }
371 }
372
373 bool QQuickState::containsPropertyInRevertList(QObject *target, const QString &name) const
374 {
375     Q_D(const QQuickState);
376
377     if (isStateActive()) {
378         QListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
379
380         while (revertListIterator.hasNext()) {
381             const QQuickSimpleAction &simpleAction = revertListIterator.next();
382             if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
383                 return true;
384         }
385     }
386
387     return false;
388 }
389
390 bool QQuickState::changeValueInRevertList(QObject *target, const QString &name, const QVariant &revertValue)
391 {
392     Q_D(QQuickState);
393
394     if (isStateActive()) {
395         QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
396
397         while (revertListIterator.hasNext()) {
398             QQuickSimpleAction &simpleAction = revertListIterator.next();
399             if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name) {
400                     simpleAction.setValue(revertValue);
401                     return true;
402             }
403         }
404     }
405
406     return false;
407 }
408
409 bool QQuickState::changeBindingInRevertList(QObject *target, const QString &name, QQmlAbstractBinding *binding)
410 {
411     Q_D(QQuickState);
412
413     if (isStateActive()) {
414         QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
415
416         while (revertListIterator.hasNext()) {
417             QQuickSimpleAction &simpleAction = revertListIterator.next();
418             if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == 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
431 bool QQuickState::removeEntryFromRevertList(QObject *target, const QString &name)
432 {
433     Q_D(QQuickState);
434
435     if (isStateActive()) {
436         QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
437
438         while (revertListIterator.hasNext()) {
439             QQuickSimpleAction &simpleAction = revertListIterator.next();
440             if (simpleAction.property().object() == target && simpleAction.property().name() == name) {
441                 QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(simpleAction.property());
442                 if (oldBinding) {
443                     QQmlPropertyPrivate::setBinding(simpleAction.property(), 0);
444                     oldBinding->destroy();
445                 }
446
447                 simpleAction.property().write(simpleAction.value());
448                 if (simpleAction.binding())
449                     QQmlPropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
450
451                 revertListIterator.remove();
452                 return true;
453             }
454         }
455     }
456
457     return false;
458 }
459
460 void QQuickState::addEntryToRevertList(const QQuickAction &action)
461 {
462     Q_D(QQuickState);
463
464     QQuickSimpleAction simpleAction(action);
465
466     d->revertList.append(simpleAction);
467 }
468
469 void QQuickState::removeAllEntriesFromRevertList(QObject *target)
470 {
471      Q_D(QQuickState);
472
473      if (isStateActive()) {
474          QMutableListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
475
476          while (revertListIterator.hasNext()) {
477              QQuickSimpleAction &simpleAction = revertListIterator.next();
478              if (simpleAction.property().object() == target) {
479                  QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(simpleAction.property());
480                  if (oldBinding) {
481                      QQmlPropertyPrivate::setBinding(simpleAction.property(), 0);
482                      oldBinding->destroy();
483                  }
484
485                  simpleAction.property().write(simpleAction.value());
486                  if (simpleAction.binding())
487                      QQmlPropertyPrivate::setBinding(simpleAction.property(), simpleAction.binding());
488
489                  revertListIterator.remove();
490              }
491          }
492      }
493 }
494
495 void QQuickState::addEntriesToRevertList(const QList<QQuickAction> &actionList)
496 {
497     Q_D(QQuickState);
498     if (isStateActive()) {
499         QList<QQuickSimpleAction> simpleActionList;
500
501         QListIterator<QQuickAction> actionListIterator(actionList);
502         while(actionListIterator.hasNext()) {
503             const QQuickAction &action = actionListIterator.next();
504             QQuickSimpleAction simpleAction(action);
505             action.property.write(action.toValue);
506             if (!action.toBinding.isNull()) {
507                 QQmlAbstractBinding *oldBinding = QQmlPropertyPrivate::binding(simpleAction.property());
508                 if (oldBinding)
509                     QQmlPropertyPrivate::setBinding(simpleAction.property(), 0);
510                 QQmlPropertyPrivate::setBinding(simpleAction.property(), action.toBinding.data(), QQmlPropertyPrivate::DontRemoveBinding);
511             }
512
513             simpleActionList.append(simpleAction);
514         }
515
516         d->revertList.append(simpleActionList);
517     }
518 }
519
520 QVariant QQuickState::valueInRevertList(QObject *target, const QString &name) const
521 {
522     Q_D(const QQuickState);
523
524     if (isStateActive()) {
525         QListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
526
527         while (revertListIterator.hasNext()) {
528             const QQuickSimpleAction &simpleAction = revertListIterator.next();
529             if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
530                 return simpleAction.value();
531         }
532     }
533
534     return QVariant();
535 }
536
537 QQmlAbstractBinding *QQuickState::bindingInRevertList(QObject *target, const QString &name) const
538 {
539     Q_D(const QQuickState);
540
541     if (isStateActive()) {
542         QListIterator<QQuickSimpleAction> revertListIterator(d->revertList);
543
544         while (revertListIterator.hasNext()) {
545             const QQuickSimpleAction &simpleAction = revertListIterator.next();
546             if (simpleAction.specifiedObject() == target && simpleAction.specifiedProperty() == name)
547                 return simpleAction.binding();
548         }
549     }
550
551     return 0;
552 }
553
554 bool QQuickState::isStateActive() const
555 {
556     return stateGroup() && stateGroup()->state() == name();
557 }
558
559 void QQuickState::apply(QQuickTransition *trans, QQuickState *revert)
560 {
561     Q_D(QQuickState);
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         QQuickStatePrivate *revertPrivate =
573             static_cast<QQuickStatePrivate*>(revert->d_func());
574         d->revertList = revertPrivate->revertList;
575         revertPrivate->revertList.clear();
576     }
577
578     // List of actions caused by this state
579     QQuickStateOperation::ActionList applyList = d->generateActionList();
580
581     // List of actions that need to be reverted to roll back (just) this state
582     QQuickStatePrivate::SimpleActionList additionalReverts;
583     // First add the reverse of all the applyList actions
584     for (int ii = 0; ii < applyList.count(); ++ii) {
585         QQuickAction &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                 QQuickActionEvent *event = d->revertList.at(jj).event();
593                 if (event && event->type() == action.event->type()) {
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                             QQuickSimpleAction 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                 QQuickSimpleAction r(action);
616                 additionalReverts << r;
617             }
618         } else {
619             bool found = false;
620             action.fromBinding = QQmlPropertyPrivate::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                     QQuickSimpleAction 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             QQuickActionEvent *event = d->revertList.at(ii).event();
651             if (!event->isReversable())
652                 continue;
653             for (int jj = 0; !found && jj < applyList.count(); ++jj) {
654                 const QQuickAction &action = applyList.at(jj);
655                 if (action.event && action.event->type() == event->type()) {
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 QQuickAction &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             QQmlAbstractBinding *delBinding =
670                 QQmlPropertyPrivate::setBinding(d->revertList.at(ii).property(), 0);
671             if (delBinding)
672                 delBinding->destroy();
673
674             QQuickAction a;
675             a.property = d->revertList.at(ii).property();
676             a.fromValue = cur;
677             a.toValue = d->revertList.at(ii).value();
678             a.toBinding = QQmlAbstractBinding::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             if (a.event)
688                 d->reverting << a.event;
689             else
690                 d->reverting << a.property;
691         }
692     }
693     // All the local reverts now become part of the ongoing revertList
694     d->revertList << additionalReverts;
695
696 #ifndef QT_NO_DEBUG_STREAM
697     // Output for debugging
698     if (stateChangeDebug()) {
699         foreach(const QQuickAction &action, applyList) {
700             if (action.event)
701                 qWarning() << "    QQuickAction event:" << action.event->type();
702             else
703                 qWarning() << "    QQuickAction:" << action.property.object()
704                            << action.property.name() << "From:" << action.fromValue
705                            << "To:" << action.toValue;
706         }
707     }
708 #endif
709
710     d->transitionManager.transition(applyList, trans);
711 }
712
713 QQuickStateOperation::ActionList QQuickStateOperation::actions()
714 {
715     return ActionList();
716 }
717
718 QQuickState *QQuickStateOperation::state() const
719 {
720     Q_D(const QQuickStateOperation);
721     return d->m_state;
722 }
723
724 void QQuickStateOperation::setState(QQuickState *state)
725 {
726     Q_D(QQuickStateOperation);
727     d->m_state = state;
728 }
729
730 QT_END_NAMESPACE