summaryrefslogtreecommitdiffstats
path: root/src/corelib/statemachine/qstatemachine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/statemachine/qstatemachine.cpp')
-rw-r--r--src/corelib/statemachine/qstatemachine.cpp272
1 files changed, 103 insertions, 169 deletions
diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp
index bf3ee3107..a02480bff 100644
--- a/src/corelib/statemachine/qstatemachine.cpp
+++ b/src/corelib/statemachine/qstatemachine.cpp
@@ -102,14 +102,9 @@ QT_BEGIN_NAMESPACE
Framework}{overview} gives several state graphs and the code to
build them.
- The rootState() is the parent of all top-level states in the
- machine; it is used, for instance, when the state graph is
- deleted. It is created by the machine.
-
- Use the addState() function to add a state to the state machine.
- All top-level states are added to the root state. States are
- removed with the removeState() function. Removing states while the
- machine is running is discouraged.
+ Use the addState() function to add a top-level state to the state machine.
+ States are removed with the removeState() function. Removing states while
+ the machine is running is discouraged.
Before the machine can be started, the \l{initialState}{initial
state} must be set. The initial state is the state that the
@@ -179,26 +174,6 @@ This is
*/
/*!
- \property QStateMachine::rootState
-
- \brief the root state of this state machine
-*/
-
-/*!
- \property QStateMachine::initialState
-
- \brief the initial state of this state machine
-
- The initial state must be one of the rootState()'s child states.
-*/
-
-/*!
- \property QStateMachine::errorState
-
- \brief the error state of this state machine
-*/
-
-/*!
\property QStateMachine::errorString
\brief the error string of this state machine
@@ -235,7 +210,6 @@ QStateMachinePrivate::QStateMachinePrivate()
stop = false;
error = QStateMachine::NoError;
globalRestorePolicy = QStateMachine::DoNotRestoreProperties;
- rootState = 0;
signalEventGenerator = 0;
#ifndef QT_NO_ANIMATION
animationsEnabled = true;
@@ -255,6 +229,11 @@ QStateMachinePrivate *QStateMachinePrivate::get(QStateMachine *q)
return 0;
}
+QState *QStateMachinePrivate::rootState() const
+{
+ return const_cast<QStateMachine*>(q_func());
+}
+
static QEvent *cloneEvent(QEvent *e)
{
switch (e->type()) {
@@ -302,7 +281,9 @@ bool QStateMachinePrivate::stateEntryLessThan(QAbstractState *s1, QAbstractState
} else if (isDescendantOf(s2, s1)) {
return true;
} else {
- QState *lca = findLCA(QList<QAbstractState*>() << s1 << s2);
+ Q_ASSERT(s1->machine() != 0);
+ QStateMachinePrivate *mach = QStateMachinePrivate::get(s1->machine());
+ QState *lca = mach->findLCA(QList<QAbstractState*>() << s1 << s2);
Q_ASSERT(lca != 0);
return (indexOfDescendant(lca, s1) < indexOfDescendant(lca, s2));
}
@@ -318,17 +299,19 @@ bool QStateMachinePrivate::stateExitLessThan(QAbstractState *s1, QAbstractState
} else if (isDescendantOf(s2, s1)) {
return false;
} else {
- QState *lca = findLCA(QList<QAbstractState*>() << s1 << s2);
+ Q_ASSERT(s1->machine() != 0);
+ QStateMachinePrivate *mach = QStateMachinePrivate::get(s1->machine());
+ QState *lca = mach->findLCA(QList<QAbstractState*>() << s1 << s2);
Q_ASSERT(lca != 0);
return (indexOfDescendant(lca, s1) < indexOfDescendant(lca, s2));
}
}
-QState *QStateMachinePrivate::findLCA(const QList<QAbstractState*> &states)
+QState *QStateMachinePrivate::findLCA(const QList<QAbstractState*> &states) const
{
if (states.isEmpty())
return 0;
- QList<QState*> ancestors = properAncestors(states.at(0), 0);
+ QList<QState*> ancestors = properAncestors(states.at(0), rootState()->parentState());
for (int i = 0; i < ancestors.size(); ++i) {
QState *anc = ancestors.at(i);
bool ok = true;
@@ -376,7 +359,7 @@ QSet<QAbstractTransition*> QStateMachinePrivate::selectTransitions(QEvent *event
continue;
if (isPreempted(state, enabledTransitions))
continue;
- QList<QState*> lst = properAncestors(state, 0);
+ QList<QState*> lst = properAncestors(state, rootState()->parentState());
if (QState *grp = qobject_cast<QState*>(state))
lst.prepend(grp);
bool found = false;
@@ -412,7 +395,9 @@ void QStateMachinePrivate::microstep(QEvent *event, const QList<QAbstractTransit
#endif
executeTransitionContent(event, enabledTransitions);
QList<QAbstractState*> enteredStates = enterStates(event, enabledTransitions);
+#ifndef QT_NO_PROPERTIES
applyProperties(enabledTransitions, exitedStates, enteredStates);
+#endif
#ifdef QSTATEMACHINE_DEBUG
qDebug() << q_func() << ": configuration after entering states:" << configuration;
qDebug() << q_func() << ": end microstep";
@@ -557,11 +542,13 @@ QList<QAbstractState*> QStateMachinePrivate::enterStates(QEvent *event, const QL
if (isFinal(s)) {
QState *parent = s->parentState();
if (parent) {
- QState *grandparent = parent->parentState();
+ if (parent != rootState()) {
#ifdef QSTATEMACHINE_DEBUG
- qDebug() << q << ": emitting finished signal for" << parent;
+ qDebug() << q << ": emitting finished signal for" << parent;
#endif
- QStatePrivate::get(parent)->emitFinished();
+ QStatePrivate::get(parent)->emitFinished();
+ }
+ QState *grandparent = parent->parentState();
if (grandparent && isParallel(grandparent)) {
bool allChildStatesFinal = true;
QList<QAbstractState*> childStates = QStatePrivate::get(grandparent)->childStates();
@@ -572,7 +559,7 @@ QList<QAbstractState*> QStateMachinePrivate::enterStates(QEvent *event, const QL
break;
}
}
- if (allChildStatesFinal) {
+ if (allChildStatesFinal && (grandparent != rootState())) {
#ifdef QSTATEMACHINE_DEBUG
qDebug() << q << ": emitting finished signal for" << grandparent;
#endif
@@ -585,7 +572,7 @@ QList<QAbstractState*> QStateMachinePrivate::enterStates(QEvent *event, const QL
{
QSet<QAbstractState*>::const_iterator it;
for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
- if (isFinal(*it) && (*it)->parentState() == rootState) {
+ if (isFinal(*it) && (*it)->parentState() == rootState()) {
processing = false;
stopProcessingReason = Finished;
break;
@@ -630,6 +617,11 @@ void QStateMachinePrivate::addStatesToEnter(QAbstractState *s, QState *root,
}
}
} else {
+ if (s == rootState()) {
+ // Error has already been set by exitStates().
+ Q_ASSERT(error != QStateMachine::NoError);
+ return;
+ }
statesToEnter.insert(s);
if (isParallel(s)) {
QState *grp = qobject_cast<QState*>(s);
@@ -643,6 +635,7 @@ void QStateMachinePrivate::addStatesToEnter(QAbstractState *s, QState *root,
QState *grp = qobject_cast<QState*>(s);
QAbstractState *initial = grp->initialState();
if (initial != 0) {
+ Q_ASSERT(initial->machine() == q_func());
addStatesToEnter(initial, grp, statesToEnter, statesForDefaultEntry);
} else {
setError(QStateMachine::NoInitialStateError, grp);
@@ -675,6 +668,8 @@ void QStateMachinePrivate::addStatesToEnter(QAbstractState *s, QState *root,
}
}
+#ifndef QT_NO_PROPERTIES
+
void QStateMachinePrivate::applyProperties(const QList<QAbstractTransition*> &transitionList,
const QList<QAbstractState*> &exitedStates,
const QList<QAbstractState*> &enteredStates)
@@ -862,6 +857,8 @@ void QStateMachinePrivate::applyProperties(const QList<QAbstractTransition*> &tr
}
}
+#endif // QT_NO_PROPERTIES
+
bool QStateMachinePrivate::isFinal(const QAbstractState *s)
{
return qobject_cast<const QFinalState*>(s) != 0;
@@ -873,20 +870,26 @@ bool QStateMachinePrivate::isParallel(const QAbstractState *s)
return ss && (QStatePrivate::get(ss)->childMode == QState::ParallelStates);
}
-bool QStateMachinePrivate::isCompound(const QAbstractState *s)
+bool QStateMachinePrivate::isCompound(const QAbstractState *s) const
{
const QState *group = qobject_cast<const QState*>(s);
if (!group)
return false;
+ bool isMachine = (qobject_cast<const QStateMachine*>(group) != 0);
+ // Don't treat the machine as compound if it's a sub-state of this machine
+ if (isMachine && (group != rootState()))
+ return false;
return (!isParallel(group) && !QStatePrivate::get(group)->childStates().isEmpty())
- || (qobject_cast<QStateMachine*>(group->parent()) != 0);
+ || isMachine;
}
-bool QStateMachinePrivate::isAtomic(const QAbstractState *s)
+bool QStateMachinePrivate::isAtomic(const QAbstractState *s) const
{
const QState *ss = qobject_cast<const QState*>(s);
return (ss && QStatePrivate::get(ss)->childStates().isEmpty())
- || isFinal(s);
+ || isFinal(s)
+ // Treat the machine as atomic if it's a sub-state of this machine
+ || (ss && (qobject_cast<const QStateMachine*>(ss) != 0) && (ss != rootState()));
}
@@ -935,6 +938,8 @@ bool QStateMachinePrivate::isInFinalState(QAbstractState* s) const
return false;
}
+#ifndef QT_NO_PROPERTIES
+
void QStateMachinePrivate::registerRestorable(QObject *object, const QByteArray &propertyName)
{
RestorableId id(object, propertyName);
@@ -979,6 +984,8 @@ void QStateMachinePrivate::unregisterRestorable(QObject *object, const QByteArra
registeredRestorables.remove(id);
}
+#endif // QT_NO_PROPERTIES
+
QAbstractState *QStateMachinePrivate::findErrorState(QAbstractState *context)
{
// Find error state recursively in parent hierarchy if not set explicitly for context state
@@ -1034,7 +1041,7 @@ void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractSta
if (currentContext == currentErrorState)
currentErrorState = 0;
- Q_ASSERT(currentErrorState != rootState);
+ Q_ASSERT(currentErrorState != rootState());
if (currentErrorState != 0) {
QState *lca = findLCA(QList<QAbstractState*>() << currentErrorState << currentContext);
@@ -1091,12 +1098,14 @@ void QStateMachinePrivate::_q_animationFinished()
resetAnimationEndValues.remove(anim);
}
+#ifndef QT_NO_PROPERTIES
// Set the final property value.
QPropertyAssignment assn = propertyForAnimation.take(anim);
Q_ASSERT(assn.object != 0);
assn.object->setProperty(assn.propertyName, assn.value);
if (!assn.explicitlySet)
unregisterRestorable(assn.object, assn.propertyName);
+#endif
QAbstractState *state = stateForAnimation.take(anim);
Q_ASSERT(state != 0);
@@ -1141,11 +1150,8 @@ void QStateMachinePrivate::_q_start()
{
Q_Q(QStateMachine);
Q_ASSERT(state == Starting);
- if (!rootState) {
- state = NotRunning;
- return;
- }
- QAbstractState *initial = rootState->initialState();
+ Q_ASSERT(rootState() != 0);
+ QAbstractState *initial = rootState()->initialState();
configuration.clear();
qDeleteAll(internalEventQueue);
internalEventQueue.clear();
@@ -1159,7 +1165,7 @@ void QStateMachinePrivate::_q_start()
processingScheduled = true; // we call _q_process() below
emit q->started();
- StartState *start = new StartState(rootState);
+ StartState *start = new StartState(rootState());
QAbstractTransition *initialTransition = new InitialTransition(initial);
start->addTransition(initialTransition);
QList<QAbstractTransition*> transitions;
@@ -1167,8 +1173,10 @@ void QStateMachinePrivate::_q_start()
QEvent nullEvent(QEvent::None);
executeTransitionContent(&nullEvent, transitions);
QList<QAbstractState*> enteredStates = enterStates(&nullEvent, transitions);
+#ifndef QT_NO_PROPERTIES
applyProperties(transitions, QList<QAbstractState*>() << start,
enteredStates);
+#endif
delete start;
#ifdef QSTATEMACHINE_DEBUG
@@ -1371,15 +1379,22 @@ void QStateMachinePrivate::unregisterSignalTransition(QSignalTransition *transit
void QStateMachinePrivate::unregisterAllTransitions()
{
+ Q_Q(QStateMachine);
{
- QList<QSignalTransition*> transitions = qFindChildren<QSignalTransition*>(rootState);
- for (int i = 0; i < transitions.size(); ++i)
- unregisterSignalTransition(transitions.at(i));
+ QList<QSignalTransition*> transitions = qFindChildren<QSignalTransition*>(rootState());
+ for (int i = 0; i < transitions.size(); ++i) {
+ QSignalTransition *t = transitions.at(i);
+ if (t->machine() == q)
+ unregisterSignalTransition(t);
+ }
}
{
- QList<QEventTransition*> transitions = qFindChildren<QEventTransition*>(rootState);
- for (int i = 0; i < transitions.size(); ++i)
- unregisterEventTransition(transitions.at(i));
+ QList<QEventTransition*> transitions = qFindChildren<QEventTransition*>(rootState());
+ for (int i = 0; i < transitions.size(); ++i) {
+ QEventTransition *t = transitions.at(i);
+ if (t->machine() == q)
+ unregisterEventTransition(t);
+ }
}
}
@@ -1457,16 +1472,20 @@ void QStateMachinePrivate::handleTransitionSignal(const QObject *sender, int sig
Constructs a new state machine with the given \a parent.
*/
QStateMachine::QStateMachine(QObject *parent)
- : QObject(*new QStateMachinePrivate, parent)
+ : QState(*new QStateMachinePrivate, /*parentState=*/0)
{
+ // Can't pass the parent to the QState constructor, as it expects a QState
+ // But this works as expected regardless of whether parent is a QState or not
+ setParent(parent);
}
/*!
\internal
*/
QStateMachine::QStateMachine(QStateMachinePrivate &dd, QObject *parent)
- : QObject(dd, parent)
+ : QState(dd, /*parentState=*/0)
{
+ setParent(parent);
}
/*!
@@ -1476,69 +1495,6 @@ QStateMachine::~QStateMachine()
{
}
-namespace {
-
-class RootState : public QState
-{
-public:
- RootState(QState *parent)
- : QState(parent)
- {
- }
-
- void onEntry(QEvent *) {}
- void onExit(QEvent *) {}
-};
-
-} // namespace
-
-/*!
- Returns this state machine's root state.
-*/
-QState *QStateMachine::rootState() const
-{
- Q_D(const QStateMachine);
- if (!d->rootState) {
- const_cast<QStateMachinePrivate*>(d)->rootState = new RootState(0);
- d->rootState->setParent(const_cast<QStateMachine*>(this));
- }
- return d->rootState;
-}
-
-/*!
- Returns the error state of the state machine's root state.
-
- \sa QState::errorState()
-*/
-QAbstractState *QStateMachine::errorState() const
-{
- return rootState()->errorState();
-}
-
-/*!
- Sets the error state of this state machine's root state to be \a state. When a running state
- machine encounters an error which puts it in an undefined state, it will enter an error state
- based on the context of the error that occurred. It will enter this state regardless of what
- is currently in the event queue.
-
- If the erroneous state has an error state set, this will be entered by the machine. If no error
- state has been set, the state machine will search the parent hierarchy recursively for an
- error state. The error state of the root state can thus be seen as a global error state that
- applies for all states for which a more specific error state has not been set.
-
- Before entering the error state, the state machine will set the error code returned by error() and
- error message returned by errorString().
-
- If there is no error state available for the erroneous state, the state machine will print a
- warning message on the console and stop executing.
-
- \sa QState::setErrorState(), rootState()
-*/
-void QStateMachine::setErrorState(QAbstractState *state)
-{
- rootState()->setErrorState(state);
-}
-
/*! \enum QStateMachine::Error
This enum type defines errors that can occur in the state machine at run time. When the state
@@ -1640,39 +1596,13 @@ void QStateMachine::setGlobalRestorePolicy(QStateMachine::RestorePolicy restoreP
}
/*!
- Returns this state machine's initial state, or 0 if no initial state has
- been set.
-*/
-QAbstractState *QStateMachine::initialState() const
-{
- Q_D(const QStateMachine);
- if (!d->rootState)
- return 0;
- return d->rootState->initialState();
-}
-
-/*!
- Sets this state machine's initial \a state.
-*/
-void QStateMachine::setInitialState(QAbstractState *state)
-{
- Q_D(QStateMachine);
- if (!d->rootState) {
- if (!state)
- return;
- rootState()->setInitialState(state);
- }
- d->rootState->setInitialState(state);
-}
-
-/*!
Adds the given \a state to this state machine. The state becomes a top-level
- state (i.e. a child of the rootState()).
+ state.
If the state is already in a different machine, it will first be removed
from its old machine, and then added to this machine.
- \sa removeState(), rootState(), setInitialState()
+ \sa removeState(), setInitialState()
*/
void QStateMachine::addState(QAbstractState *state)
{
@@ -1684,7 +1614,7 @@ void QStateMachine::addState(QAbstractState *state)
qWarning("QStateMachine::addState: state has already been added to this machine");
return;
}
- state->setParent(rootState());
+ state->setParent(this);
}
/*!
@@ -1730,7 +1660,7 @@ void QStateMachine::start()
{
Q_D(QStateMachine);
- if (rootState()->initialState() == 0) {
+ if (initialState() == 0) {
qWarning("QStateMachine::start: No initial state set for machine. Refusing to start.");
return;
}
@@ -1821,7 +1751,7 @@ void QStateMachine::postInternalEvent(QEvent *event)
Returns the maximal consistent set of states (including parallel and final
states) that this state machine is currently in. If a state \c s is in the
configuration, it is always the case that the parent of \c s is also in
- c. Note, however, that the rootState() is not an explicit member of the
+ c. Note, however, that the machine itself is not an explicit member of the
configuration.
*/
QSet<QAbstractState*> QStateMachine::configuration() const
@@ -1840,15 +1770,6 @@ QSet<QAbstractState*> QStateMachine::configuration() const
*/
/*!
- \fn QStateMachine::finished()
-
- This signal is emitted when the state machine has reached a top-level final
- state (QFinalState).
-
- \sa QStateMachine::started()
-*/
-
-/*!
\fn QStateMachine::stopped()
This signal is emitted when the state machine has stopped.
@@ -1872,14 +1793,6 @@ bool QStateMachine::event(QEvent *e)
d->scheduleProcess();
return true;
}
- } else if (e->type() == QEvent::ChildAdded) {
- QChildEvent *ce = static_cast<QChildEvent*>(e);
- if (QAbstractState *state = qobject_cast<QAbstractState*>(ce->child())) {
- if (state != rootState()) {
- state->setParent(rootState());
- return true;
- }
- }
}
return QObject::event(e);
}
@@ -1949,6 +1862,24 @@ void QStateMachine::endMicrostep(QEvent *event)
Q_UNUSED(event);
}
+/*!
+ \reimp
+*/
+void QStateMachine::onEntry(QEvent *event)
+{
+ start();
+ QState::onEntry(event);
+}
+
+/*!
+ \reimp
+*/
+void QStateMachine::onExit(QEvent *event)
+{
+ stop();
+ QState::onExit(event);
+}
+
#ifndef QT_NO_ANIMATION
/*!
@@ -2155,6 +2086,8 @@ QSignalEvent::~QSignalEvent()
Constructs a new QWrappedEvent object with the given \a object
and \a event.
+
+ The QWrappedEvent object takes ownership of \a event.
*/
QWrappedEvent::QWrappedEvent(QObject *object, QEvent *event)
: QEvent(QEvent::Wrapped), m_object(object), m_event(event)
@@ -2166,6 +2099,7 @@ QWrappedEvent::QWrappedEvent(QObject *object, QEvent *event)
*/
QWrappedEvent::~QWrappedEvent()
{
+ delete m_event;
}
/*!