1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtCore module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
44 #ifndef QT_NO_STATEMACHINE
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"
60 \brief The QState class provides a general-purpose state for QStateMachine.
65 QState objects can have child states, and can have transitions to other
66 states. QState is part of \l{The State Machine Framework}.
68 The addTransition() function adds a transition. The removeTransition()
69 function removes a transition. The transitions() function returns the
70 state's outgoing transitions.
72 The assignProperty() function is used for defining property assignments that
73 should be performed when a state is entered.
75 Top-level states must be passed a QStateMachine object as their parent
76 state, or added to a state machine using QStateMachine::addState().
78 \section1 States with Child States
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.
86 The state emits the QState::finished() signal when a final child state
87 (QFinalState) is entered.
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).
96 \property QState::initialState
98 \brief the initial state of this state (one of its child states)
102 \property QState::errorState
104 \brief the error state of this state
108 \property QState::childMode
110 \brief the child mode of this state
112 The default value of this property is QState::ExclusiveStates.
116 \enum QState::ChildMode
118 This enum specifies how a state's child states are treated.
120 \value ExclusiveStates The child states are mutually exclusive and an
121 initial state must be set by calling QState::setInitialState().
123 \value ParallelStates The child states are parallel. When the parent state
124 is entered, all its child states are entered in parallel.
127 QStatePrivate::QStatePrivate()
128 : QAbstractStatePrivate(StandardState),
129 errorState(0), initialState(0), childMode(QState::ExclusiveStates),
130 childStatesListNeedsRefresh(true), transitionsListNeedsRefresh(true)
134 QStatePrivate::~QStatePrivate()
138 void QStatePrivate::emitFinished()
144 void QStatePrivate::emitPropertiesAssigned()
147 emit q->propertiesAssigned();
151 Constructs a new state with the given \a parent state.
153 QState::QState(QState *parent)
154 : QAbstractState(*new QStatePrivate, parent)
159 Constructs a new state with the given \a childMode and the given \a parent
162 QState::QState(ChildMode childMode, QState *parent)
163 : QAbstractState(*new QStatePrivate, parent)
166 d->childMode = childMode;
172 QState::QState(QStatePrivate &dd, QState *parent)
173 : QAbstractState(dd, parent)
184 QList<QAbstractState*> QStatePrivate::childStates() const
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))
193 childStatesList.append(s);
195 childStatesListNeedsRefresh = false;
197 return childStatesList;
200 QList<QHistoryState*> QStatePrivate::historyStates() const
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);
212 QList<QAbstractTransition*> QStatePrivate::transitions() const
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);
220 transitionsList.append(t);
222 transitionsListNeedsRefresh = false;
224 return transitionsList;
227 #ifndef QT_NO_PROPERTIES
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.
233 \sa propertiesAssigned()
235 void QState::assignProperty(QObject *object, const char *name,
236 const QVariant &value)
240 qWarning("QState::assignProperty: cannot assign property '%s' of null object", name);
243 for (int i = 0; i < d->propertyAssignments.size(); ++i) {
244 QPropertyAssignment &assn = d->propertyAssignments[i];
245 if ((assn.object == object) && (assn.propertyName == name)) {
250 d->propertyAssignments.append(QPropertyAssignment(object, name, value));
253 #endif // QT_NO_PROPERTIES
256 Returns this state's error state.
258 \sa QStateMachine::error()
260 QAbstractState *QState::errorState() const
263 return d->errorState;
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.
273 void QState::setErrorState(QAbstractState *state)
276 if (state != 0 && qobject_cast<QStateMachine*>(state)) {
277 qWarning("QStateMachine::setErrorState: root state cannot be error state");
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");
286 d->errorState = state;
290 Adds the given \a transition. The transition has this state as the source.
291 This state takes ownership of the transition.
293 void QState::addTransition(QAbstractTransition *transition)
297 qWarning("QState::addTransition: cannot add null transition");
301 transition->setParent(this);
302 const QList<QPointer<QAbstractState> > &targets = QAbstractTransitionPrivate::get(transition)->targetStates;
303 for (int i = 0; i < targets.size(); ++i) {
304 QAbstractState *t = targets.at(i).data();
306 qWarning("QState::addTransition: cannot add transition to null state");
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");
316 if (machine() != 0 && machine()->configuration().contains(this))
317 QStateMachinePrivate::get(machine())->registerTransitions(this);
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.
325 QSignalTransition *QState::addTransition(QObject *sender, const char *signal,
326 QAbstractState *target)
329 qWarning("QState::addTransition: sender cannot be null");
333 qWarning("QState::addTransition: signal cannot be null");
337 qWarning("QState::addTransition: cannot add transition to null state");
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);
349 QSignalTransition *trans = new QSignalTransition(sender, signal);
350 trans->setTargetState(target);
351 addTransition(trans);
358 class UnconditionalTransition : public QAbstractTransition
361 UnconditionalTransition(QAbstractState *target)
362 : QAbstractTransition()
363 { setTargetState(target); }
365 void onTransition(QEvent *) {}
366 bool eventTest(QEvent *) { return true; }
372 Adds an unconditional transition from this state to the given \a target
373 state, and returns then new transition object.
375 QAbstractTransition *QState::addTransition(QAbstractState *target)
378 qWarning("QState::addTransition: cannot add transition to null state");
381 UnconditionalTransition *trans = new UnconditionalTransition(target);
382 addTransition(trans);
387 Removes the given \a transition from this state. The state releases
388 ownership of the transition.
392 void QState::removeTransition(QAbstractTransition *transition)
396 qWarning("QState::removeTransition: cannot remove null transition");
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);
405 QStateMachinePrivate *mach = QStateMachinePrivate::get(d->machine());
407 mach->unregisterTransition(transition);
408 transition->setParent(0);
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.
420 QList<QAbstractTransition*> QState::transitions() const
423 return d->transitions();
429 void QState::onEntry(QEvent *event)
437 void QState::onExit(QEvent *event)
443 Returns this state's initial state, or 0 if the state has no initial state.
445 QAbstractState *QState::initialState() const
448 return d->initialState;
452 Sets this state's initial state to be the given \a state.
453 \a state has to be a child of this state.
455 void QState::setInitialState(QAbstractState *state)
458 if (d->childMode == QState::ParallelStates) {
459 qWarning("QState::setInitialState: ignoring attempt to set initial state "
460 "of parallel state group %p", this);
463 if (state && (state->parentState() != this)) {
464 qWarning("QState::setInitialState: state %p is not a child of this state (%p)",
468 d->initialState = state;
472 Returns the child mode of this state.
474 QState::ChildMode QState::childMode() const
481 Sets the child \a mode of this state.
483 void QState::setChildMode(ChildMode mode)
492 bool QState::event(QEvent *e)
495 if ((e->type() == QEvent::ChildAdded) || (e->type() == QEvent::ChildRemoved)) {
496 d->childStatesListNeedsRefresh = true;
497 d->transitionsListNeedsRefresh = true;
499 return QAbstractState::event(e);
503 \fn QState::finished()
505 This signal is emitted when a final child state of this state is entered.
511 \fn QState::propertiesAssigned()
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.
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.
521 \sa QState::assignProperty(), QAbstractTransition::addAnimation()
526 #endif //QT_NO_STATEMACHINE