0230acbab6369f1bb516308536a2adadada086a6
[profile/ivi/qtbase.git] / src / corelib / statemachine / qstate.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 QtCore 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 "qstate.h"
43
44 #ifndef QT_NO_STATEMACHINE
45
46 #include "qstate_p.h"
47 #include "qhistorystate.h"
48 #include "qhistorystate_p.h"
49 #include "qabstracttransition.h"
50 #include "qabstracttransition_p.h"
51 #include "qsignaltransition.h"
52 #include "qstatemachine.h"
53 #include "qstatemachine_p.h"
54
55 QT_BEGIN_NAMESPACE
56
57 /*!
58   \class QState
59
60   \brief The QState class provides a general-purpose state for QStateMachine.
61
62   \since 4.6
63   \ingroup statemachine
64
65   QState objects can have child states, and can have transitions to other
66   states. QState is part of \l{The State Machine Framework}.
67
68   The addTransition() function adds a transition. The removeTransition()
69   function removes a transition. The transitions() function returns the
70   state's outgoing transitions.
71
72   The assignProperty() function is used for defining property assignments that
73   should be performed when a state is entered.
74
75   Top-level states must be passed a QStateMachine object as their parent
76   state, or added to a state machine using QStateMachine::addState().
77
78   \section1 States with Child States
79
80   The childMode property determines how child states are treated. For
81   non-parallel state groups, the setInitialState() function must be called to
82   set the initial state. The child states are mutually exclusive states, and
83   the state machine needs to know which child state to enter when the parent
84   state is the target of a transition.
85
86   The state emits the QState::finished() signal when a final child state
87   (QFinalState) is entered.
88
89   The setErrorState() sets the state's error state. The error state is the
90   state that the state machine will transition to if an error is detected when
91   attempting to enter the state (e.g. because no initial state has been set).
92
93 */
94
95 /*!
96     \property QState::initialState
97
98     \brief the initial state of this state (one of its child states)
99 */
100
101 /*!
102     \property QState::errorState
103
104     \brief the error state of this state
105 */
106
107 /*!
108     \property QState::childMode
109
110     \brief the child mode of this state
111
112     The default value of this property is QState::ExclusiveStates.
113 */
114
115 /*!
116   \enum QState::ChildMode
117
118   This enum specifies how a state's child states are treated.
119
120   \value ExclusiveStates The child states are mutually exclusive and an
121   initial state must be set by calling QState::setInitialState().
122
123   \value ParallelStates The child states are parallel. When the parent state
124   is entered, all its child states are entered in parallel.
125 */
126
127 QStatePrivate::QStatePrivate()
128     : QAbstractStatePrivate(StandardState),
129       errorState(0), initialState(0), childMode(QState::ExclusiveStates),
130       childStatesListNeedsRefresh(true), transitionsListNeedsRefresh(true)
131 {
132 }
133
134 QStatePrivate::~QStatePrivate()
135 {
136 }
137
138 void QStatePrivate::emitFinished()
139 {
140     Q_Q(QState);
141     emit q->finished();
142 }
143
144 void QStatePrivate::emitPropertiesAssigned()
145 {
146     Q_Q(QState);
147     emit q->propertiesAssigned();
148 }
149
150 /*!
151   Constructs a new state with the given \a parent state.
152 */
153 QState::QState(QState *parent)
154     : QAbstractState(*new QStatePrivate, parent)
155 {
156 }
157
158 /*!
159   Constructs a new state with the given \a childMode and the given \a parent
160   state.
161 */
162 QState::QState(ChildMode childMode, QState *parent)
163     : QAbstractState(*new QStatePrivate, parent)
164 {
165     Q_D(QState);
166     d->childMode = childMode;
167 }
168
169 /*!
170   \internal
171 */
172 QState::QState(QStatePrivate &dd, QState *parent)
173     : QAbstractState(dd, parent)
174 {
175 }
176
177 /*!
178   Destroys this state.
179 */
180 QState::~QState()
181 {
182 }
183
184 QList<QAbstractState*> QStatePrivate::childStates() const
185 {
186     if (childStatesListNeedsRefresh) {
187         childStatesList.clear();
188         QList<QObject*>::const_iterator it;
189         for (it = children.constBegin(); it != children.constEnd(); ++it) {
190             QAbstractState *s = qobject_cast<QAbstractState*>(*it);
191             if (!s || qobject_cast<QHistoryState*>(s))
192                 continue;
193             childStatesList.append(s);
194         }
195         childStatesListNeedsRefresh = false;
196     }
197     return childStatesList;
198 }
199
200 QList<QHistoryState*> QStatePrivate::historyStates() const
201 {
202     QList<QHistoryState*> result;
203     QList<QObject*>::const_iterator it;
204     for (it = children.constBegin(); it != children.constEnd(); ++it) {
205         QHistoryState *h = qobject_cast<QHistoryState*>(*it);
206         if (h)
207             result.append(h);
208     }
209     return result;
210 }
211
212 QList<QAbstractTransition*> QStatePrivate::transitions() const
213 {
214     if (transitionsListNeedsRefresh) {
215         transitionsList.clear();
216         QList<QObject*>::const_iterator it;
217         for (it = children.constBegin(); it != children.constEnd(); ++it) {
218             QAbstractTransition *t = qobject_cast<QAbstractTransition*>(*it);
219             if (t)
220                 transitionsList.append(t);
221         }
222         transitionsListNeedsRefresh = false;
223     }
224     return transitionsList;
225 }
226
227 #ifndef QT_NO_PROPERTIES
228
229 /*!
230   Instructs this state to set the property with the given \a name of the given
231   \a object to the given \a value when the state is entered.
232
233   \sa propertiesAssigned()
234 */
235 void QState::assignProperty(QObject *object, const char *name,
236                             const QVariant &value)
237 {
238     Q_D(QState);
239     if (!object) {
240         qWarning("QState::assignProperty: cannot assign property '%s' of null object", name);
241         return;
242     }
243     for (int i = 0; i < d->propertyAssignments.size(); ++i) {
244         QPropertyAssignment &assn = d->propertyAssignments[i];
245         if ((assn.object == object) && (assn.propertyName == name)) {
246             assn.value = value;
247             return;
248         }
249     }
250     d->propertyAssignments.append(QPropertyAssignment(object, name, value));
251 }
252
253 #endif // QT_NO_PROPERTIES
254
255 /*!
256   Returns this state's error state. 
257
258   \sa QStateMachine::error()
259 */
260 QAbstractState *QState::errorState() const
261 {
262     Q_D(const QState);
263     return d->errorState;
264 }
265
266 /*!
267   Sets this state's error state to be the given \a state. If the error state
268   is not set, or if it is set to 0, the state will inherit its parent's error
269   state recursively. If no error state is set for the state itself or any of 
270   its ancestors, an error will cause the machine to stop executing and an error
271   will be printed to the console.
272 */
273 void QState::setErrorState(QAbstractState *state)
274 {
275     Q_D(QState);
276     if (state != 0 && qobject_cast<QStateMachine*>(state)) {
277         qWarning("QStateMachine::setErrorState: root state cannot be error state");
278         return;
279     }
280     if (state != 0 && (!state->machine() || ((state->machine() != machine()) && !qobject_cast<QStateMachine*>(this)))) {
281         qWarning("QState::setErrorState: error state cannot belong "
282                  "to a different state machine");
283         return;
284     }
285
286     d->errorState = state;
287 }
288
289 /*!
290   Adds the given \a transition. The transition has this state as the source.
291   This state takes ownership of the transition. 
292 */
293 void QState::addTransition(QAbstractTransition *transition)
294 {
295     Q_D(QState);
296     if (!transition) {
297         qWarning("QState::addTransition: cannot add null transition");
298         return ;
299     }
300
301     transition->setParent(this);
302     const QList<QWeakPointer<QAbstractState> > &targets = QAbstractTransitionPrivate::get(transition)->targetStates;
303     for (int i = 0; i < targets.size(); ++i) {
304         QAbstractState *t = targets.at(i).data();
305         if (!t) {
306             qWarning("QState::addTransition: cannot add transition to null state");
307             return ;
308         }
309         if ((QAbstractStatePrivate::get(t)->machine() != d->machine())
310             && QAbstractStatePrivate::get(t)->machine() && d->machine()) {
311             qWarning("QState::addTransition: cannot add transition "
312                      "to a state in a different state machine");
313             return ;
314         }
315     }
316     if (machine() != 0 && machine()->configuration().contains(this))
317         QStateMachinePrivate::get(machine())->registerTransitions(this);
318 }
319
320 /*!
321   Adds a transition associated with the given \a signal of the given \a sender
322   object, and returns the new QSignalTransition object. The transition has
323   this state as the source, and the given \a target as the target state.
324 */
325 QSignalTransition *QState::addTransition(QObject *sender, const char *signal,
326                                          QAbstractState *target)
327 {
328     if (!sender) {
329         qWarning("QState::addTransition: sender cannot be null");
330         return 0;
331     }
332     if (!signal) {
333         qWarning("QState::addTransition: signal cannot be null");
334         return 0;
335     }
336     if (!target) {
337         qWarning("QState::addTransition: cannot add transition to null state");
338         return 0;
339     }
340     int offset = (*signal == '0'+QSIGNAL_CODE) ? 1 : 0;
341     const QMetaObject *meta = sender->metaObject();
342     if (meta->indexOfSignal(signal+offset) == -1) {
343         if (meta->indexOfSignal(QMetaObject::normalizedSignature(signal+offset)) == -1) {
344             qWarning("QState::addTransition: no such signal %s::%s",
345                      meta->className(), signal+offset);
346             return 0;
347         }
348     }
349     QSignalTransition *trans = new QSignalTransition(sender, signal);
350     trans->setTargetState(target);
351     addTransition(trans);
352     return trans;
353 }
354
355 namespace {
356
357 // ### Make public?
358 class UnconditionalTransition : public QAbstractTransition
359 {
360 public:
361     UnconditionalTransition(QAbstractState *target)
362         : QAbstractTransition()
363     { setTargetState(target); }
364 protected:
365     void onTransition(QEvent *) {}
366     bool eventTest(QEvent *) { return true; }
367 };
368
369 } // namespace
370
371 /*!
372   Adds an unconditional transition from this state to the given \a target
373   state, and returns then new transition object.
374 */
375 QAbstractTransition *QState::addTransition(QAbstractState *target)
376 {
377     if (!target) {
378         qWarning("QState::addTransition: cannot add transition to null state");
379         return 0;
380     }
381     UnconditionalTransition *trans = new UnconditionalTransition(target);
382     addTransition(trans);
383     return trans;
384 }
385
386 /*!
387   Removes the given \a transition from this state.  The state releases
388   ownership of the transition.
389
390   \sa addTransition()
391 */
392 void QState::removeTransition(QAbstractTransition *transition)
393 {
394     Q_D(QState);
395     if (!transition) {
396         qWarning("QState::removeTransition: cannot remove null transition");
397         return;
398     }
399     if (transition->sourceState() != this) {
400         qWarning("QState::removeTransition: transition %p's source state (%p)"
401                  " is different from this state (%p)",
402                  transition, transition->sourceState(), this);
403         return;
404     }
405     QStateMachinePrivate *mach = QStateMachinePrivate::get(d->machine());
406     if (mach)
407         mach->unregisterTransition(transition);
408     transition->setParent(0);
409 }
410
411 /*!
412   \since 4.7
413
414   Returns this state's outgoing transitions (i.e. transitions where
415   this state is the \l{QAbstractTransition::sourceState()}{source
416   state}), or an empty list if this state has no outgoing transitions.
417
418   \sa addTransition()
419 */
420 QList<QAbstractTransition*> QState::transitions() const
421 {
422     Q_D(const QState);
423     return d->transitions();
424 }
425
426 /*!
427   \reimp
428 */
429 void QState::onEntry(QEvent *event)
430 {
431     Q_UNUSED(event);
432 }
433
434 /*!
435   \reimp
436 */
437 void QState::onExit(QEvent *event)
438 {
439     Q_UNUSED(event);
440 }
441
442 /*!
443   Returns this state's initial state, or 0 if the state has no initial state.
444 */
445 QAbstractState *QState::initialState() const
446 {
447     Q_D(const QState);
448     return d->initialState;
449 }
450
451 /*!
452   Sets this state's initial state to be the given \a state.
453   \a state has to be a child of this state.
454 */
455 void QState::setInitialState(QAbstractState *state)
456 {
457     Q_D(QState);
458     if (d->childMode == QState::ParallelStates) {
459         qWarning("QState::setInitialState: ignoring attempt to set initial state "
460                  "of parallel state group %p", this);
461         return;
462     }
463     if (state && (state->parentState() != this)) {
464         qWarning("QState::setInitialState: state %p is not a child of this state (%p)",
465                  state, this);
466         return;
467     }
468     d->initialState = state;
469 }
470
471 /*!
472   Returns the child mode of this state.
473 */
474 QState::ChildMode QState::childMode() const
475 {
476     Q_D(const QState);
477     return d->childMode;
478 }
479
480 /*!
481   Sets the child \a mode of this state.
482 */
483 void QState::setChildMode(ChildMode mode)
484 {
485     Q_D(QState);
486     d->childMode = mode;
487 }
488
489 /*!
490   \reimp
491 */
492 bool QState::event(QEvent *e)
493 {
494     Q_D(QState);
495     if ((e->type() == QEvent::ChildAdded) || (e->type() == QEvent::ChildRemoved)) {
496         d->childStatesListNeedsRefresh = true;
497         d->transitionsListNeedsRefresh = true;
498     }
499     return QAbstractState::event(e);
500 }
501
502 /*!
503   \fn QState::finished()
504
505   This signal is emitted when a final child state of this state is entered.
506
507   \sa QFinalState
508 */
509
510 /*!
511   \fn QState::propertiesAssigned()
512
513   This signal is emitted when all properties have been assigned their final value. If the state
514   assigns a value to one or more properties for which an animation exists (either set on the
515   transition or as a default animation on the state machine), then the signal will not be emitted
516   until all such animations have finished playing.
517
518   If there are no relevant animations, or no property assignments defined for the state, then
519   the signal will be emitted immediately before the state is entered.
520
521   \sa QState::assignProperty(), QAbstractTransition::addAnimation()
522 */
523
524 QT_END_NAMESPACE
525
526 #endif //QT_NO_STATEMACHINE