summaryrefslogtreecommitdiffstats
path: root/src/corelib/statemachine
diff options
context:
space:
mode:
authorQt by Nokia <qt-info@nokia.com>2011-04-27 12:05:43 +0200
committeraxis <qt-info@nokia.com>2011-04-27 12:05:43 +0200
commit38be0d13830efd2d98281c645c3a60afe05ffece (patch)
tree6ea73f3ec77f7d153333779883e8120f82820abe /src/corelib/statemachine
Initial import from the monolithic Qt.
This is the beginning of revision history for this module. If you want to look at revision history older than this, please refer to the Qt Git wiki for how to use Git history grafting. At the time of writing, this wiki is located here: http://qt.gitorious.org/qt/pages/GitIntroductionWithQt If you have already performed the grafting and you don't see any history beyond this commit, try running "git log" with the "--follow" argument. Branched from the monolithic repo, Qt master branch, at commit 896db169ea224deb96c59ce8af800d019de63f12
Diffstat (limited to 'src/corelib/statemachine')
-rw-r--r--src/corelib/statemachine/qabstractstate.cpp215
-rw-r--r--src/corelib/statemachine/qabstractstate.h97
-rw-r--r--src/corelib/statemachine/qabstractstate_p.h95
-rw-r--r--src/corelib/statemachine/qabstracttransition.cpp335
-rw-r--r--src/corelib/statemachine/qabstracttransition.h118
-rw-r--r--src/corelib/statemachine/qabstracttransition_p.h92
-rw-r--r--src/corelib/statemachine/qeventtransition.cpp260
-rw-r--r--src/corelib/statemachine/qeventtransition.h95
-rw-r--r--src/corelib/statemachine/qeventtransition_p.h79
-rw-r--r--src/corelib/statemachine/qfinalstate.cpp140
-rw-r--r--src/corelib/statemachine/qfinalstate.h80
-rw-r--r--src/corelib/statemachine/qhistorystate.cpp230
-rw-r--r--src/corelib/statemachine/qhistorystate.h95
-rw-r--r--src/corelib/statemachine/qhistorystate_p.h79
-rw-r--r--src/corelib/statemachine/qsignaleventgenerator_p.h78
-rw-r--r--src/corelib/statemachine/qsignaltransition.cpp264
-rw-r--r--src/corelib/statemachine/qsignaltransition.h90
-rw-r--r--src/corelib/statemachine/qsignaltransition_p.h82
-rw-r--r--src/corelib/statemachine/qstate.cpp526
-rw-r--r--src/corelib/statemachine/qstate.h122
-rw-r--r--src/corelib/statemachine/qstate_p.h112
-rw-r--r--src/corelib/statemachine/qstatemachine.cpp2401
-rw-r--r--src/corelib/statemachine/qstatemachine.h196
-rw-r--r--src/corelib/statemachine/qstatemachine_p.h250
-rw-r--r--src/corelib/statemachine/statemachine.pri28
25 files changed, 6159 insertions, 0 deletions
diff --git a/src/corelib/statemachine/qabstractstate.cpp b/src/corelib/statemachine/qabstractstate.cpp
new file mode 100644
index 0000000000..9b40695f8e
--- /dev/null
+++ b/src/corelib/statemachine/qabstractstate.cpp
@@ -0,0 +1,215 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qabstractstate.h"
+
+#ifndef QT_NO_STATEMACHINE
+
+#include "qabstractstate_p.h"
+#include "qstate.h"
+#include "qstate_p.h"
+#include "qstatemachine.h"
+#include "qstatemachine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAbstractState
+
+ \brief The QAbstractState class is the base class of states of a QStateMachine.
+
+ \since 4.6
+ \ingroup statemachine
+
+ The QAbstractState class is the abstract base class of states that are part
+ of a QStateMachine. It defines the interface that all state objects have in
+ common. QAbstractState is part of \l{The State Machine Framework}.
+
+ The entered() signal is emitted when the state has been entered. The
+ exited() signal is emitted when the state has been exited.
+
+ The parentState() function returns the state's parent state. The machine()
+ function returns the state machine that the state is part of.
+
+ \section1 Subclassing
+
+ The onEntry() function is called when the state is entered; reimplement this
+ function to perform custom processing when the state is entered.
+
+ The onExit() function is called when the state is exited; reimplement this
+ function to perform custom processing when the state is exited.
+*/
+
+QAbstractStatePrivate::QAbstractStatePrivate(StateType type)
+ : stateType(type), isMachine(false), parentState(0)
+{
+}
+
+QAbstractStatePrivate *QAbstractStatePrivate::get(QAbstractState *q)
+{
+ return q->d_func();
+}
+
+const QAbstractStatePrivate *QAbstractStatePrivate::get(const QAbstractState *q)
+{
+ return q->d_func();
+}
+
+QStateMachine *QAbstractStatePrivate::machine() const
+{
+ QObject *par = parent;
+ while (par != 0) {
+ if (QStateMachine *mach = qobject_cast<QStateMachine*>(par))
+ return mach;
+ par = par->parent();
+ }
+ return 0;
+}
+
+void QAbstractStatePrivate::callOnEntry(QEvent *e)
+{
+ Q_Q(QAbstractState);
+ q->onEntry(e);
+}
+
+void QAbstractStatePrivate::callOnExit(QEvent *e)
+{
+ Q_Q(QAbstractState);
+ q->onExit(e);
+}
+
+void QAbstractStatePrivate::emitEntered()
+{
+ Q_Q(QAbstractState);
+ emit q->entered();
+}
+
+void QAbstractStatePrivate::emitExited()
+{
+ Q_Q(QAbstractState);
+ emit q->exited();
+}
+
+/*!
+ Constructs a new state with the given \a parent state.
+*/
+QAbstractState::QAbstractState(QState *parent)
+ : QObject(*new QAbstractStatePrivate(QAbstractStatePrivate::AbstractState), parent)
+{
+}
+
+/*!
+ \internal
+*/
+QAbstractState::QAbstractState(QAbstractStatePrivate &dd, QState *parent)
+ : QObject(dd, parent)
+{
+}
+
+/*!
+ Destroys this state.
+*/
+QAbstractState::~QAbstractState()
+{
+}
+
+/*!
+ Returns this state's parent state, or 0 if the state has no parent state.
+*/
+QState *QAbstractState::parentState() const
+{
+ Q_D(const QAbstractState);
+ if (d->parentState != parent())
+ d->parentState = qobject_cast<QState*>(parent());
+ return d->parentState;
+}
+
+/*!
+ Returns the state machine that this state is part of, or 0 if the state is
+ not part of a state machine.
+*/
+QStateMachine *QAbstractState::machine() const
+{
+ Q_D(const QAbstractState);
+ return d->machine();
+}
+
+/*!
+ \fn QAbstractState::onExit(QEvent *event)
+
+ This function is called when the state is exited. The given \a event is what
+ caused the state to be exited. Reimplement this function to perform custom
+ processing when the state is exited.
+*/
+
+/*!
+ \fn QAbstractState::onEntry(QEvent *event)
+
+ This function is called when the state is entered. The given \a event is
+ what caused the state to be entered. Reimplement this function to perform
+ custom processing when the state is entered.
+*/
+
+/*!
+ \fn QAbstractState::entered()
+
+ This signal is emitted when the state has been entered (after onEntry() has
+ been called).
+*/
+
+/*!
+ \fn QAbstractState::exited()
+
+ This signal is emitted when the state has been exited (after onExit() has
+ been called).
+*/
+
+/*!
+ \reimp
+*/
+bool QAbstractState::event(QEvent *e)
+{
+ return QObject::event(e);
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_STATEMACHINE
diff --git a/src/corelib/statemachine/qabstractstate.h b/src/corelib/statemachine/qabstractstate.h
new file mode 100644
index 0000000000..4931df38f1
--- /dev/null
+++ b/src/corelib/statemachine/qabstractstate.h
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTSTATE_H
+#define QABSTRACTSTATE_H
+
+#include <QtCore/qobject.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_STATEMACHINE
+
+class QState;
+class QStateMachine;
+
+class QAbstractStatePrivate;
+class Q_CORE_EXPORT QAbstractState : public QObject
+{
+ Q_OBJECT
+public:
+ ~QAbstractState();
+
+ QState *parentState() const;
+ QStateMachine *machine() const;
+
+Q_SIGNALS:
+#if !defined(Q_MOC_RUN) && !defined(qdoc)
+private: // can only be emitted by QAbstractState
+#endif
+ void entered();
+ void exited();
+
+protected:
+ QAbstractState(QState *parent = 0);
+
+ virtual void onEntry(QEvent *event) = 0;
+ virtual void onExit(QEvent *event) = 0;
+
+ bool event(QEvent *e);
+
+protected:
+ QAbstractState(QAbstractStatePrivate &dd, QState *parent);
+
+private:
+ Q_DISABLE_COPY(QAbstractState)
+ Q_DECLARE_PRIVATE(QAbstractState)
+};
+
+#endif //QT_NO_STATEMACHINE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/corelib/statemachine/qabstractstate_p.h b/src/corelib/statemachine/qabstractstate_p.h
new file mode 100644
index 0000000000..950dc1a8eb
--- /dev/null
+++ b/src/corelib/statemachine/qabstractstate_p.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTSTATE_P_H
+#define QABSTRACTSTATE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QStateMachine;
+
+class QAbstractState;
+class QAbstractStatePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractState)
+
+public:
+ enum StateType {
+ AbstractState,
+ StandardState,
+ FinalState,
+ HistoryState
+ };
+
+ QAbstractStatePrivate(StateType type);
+
+ static QAbstractStatePrivate *get(QAbstractState *q);
+ static const QAbstractStatePrivate *get(const QAbstractState *q);
+
+ QStateMachine *machine() const;
+
+ void callOnEntry(QEvent *e);
+ void callOnExit(QEvent *e);
+
+ void emitEntered();
+ void emitExited();
+
+ uint stateType:31;
+ uint isMachine:1;
+ mutable QState *parentState;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/corelib/statemachine/qabstracttransition.cpp b/src/corelib/statemachine/qabstracttransition.cpp
new file mode 100644
index 0000000000..d8a551f5bd
--- /dev/null
+++ b/src/corelib/statemachine/qabstracttransition.cpp
@@ -0,0 +1,335 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qabstracttransition.h"
+
+#ifndef QT_NO_STATEMACHINE
+
+#include "qabstracttransition_p.h"
+#include "qabstractstate.h"
+#include "qstate.h"
+#include "qstatemachine.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAbstractTransition
+
+ \brief The QAbstractTransition class is the base class of transitions between QAbstractState objects.
+
+ \since 4.6
+ \ingroup statemachine
+
+ The QAbstractTransition class is the abstract base class of transitions
+ between states (QAbstractState objects) of a
+ QStateMachine. QAbstractTransition is part of \l{The State Machine
+ Framework}.
+
+ The sourceState() function returns the source of the transition. The
+ targetStates() function returns the targets of the transition. The machine()
+ function returns the state machine that the transition is part of.
+
+ The triggered() signal is emitted when the transition has been triggered.
+
+ Transitions can cause animations to be played. Use the addAnimation()
+ function to add an animation to the transition.
+
+ \section1 Subclassing
+
+ The eventTest() function is called by the state machine to determine whether
+ an event should trigger the transition. In your reimplementation you
+ typically check the event type and cast the event object to the proper type,
+ and check that one or more properties of the event meet your criteria.
+
+ The onTransition() function is called when the transition is triggered;
+ reimplement this function to perform custom processing for the transition.
+*/
+
+/*!
+ \property QAbstractTransition::sourceState
+
+ \brief the source state (parent) of this transition
+*/
+
+/*!
+ \property QAbstractTransition::targetState
+
+ \brief the target state of this transition
+
+ If a transition has no target state, the transition may still be
+ triggered, but this will not cause the state machine's configuration to
+ change (i.e. the current state will not be exited and re-entered).
+*/
+
+/*!
+ \property QAbstractTransition::targetStates
+
+ \brief the target states of this transition
+
+ If multiple states are specified, all must be descendants of the same
+ parallel group state.
+*/
+
+QAbstractTransitionPrivate::QAbstractTransitionPrivate()
+{
+}
+
+QAbstractTransitionPrivate *QAbstractTransitionPrivate::get(QAbstractTransition *q)
+{
+ return q->d_func();
+}
+
+QStateMachine *QAbstractTransitionPrivate::machine() const
+{
+ QState *source = sourceState();
+ if (!source)
+ return 0;
+ return source->machine();
+}
+
+bool QAbstractTransitionPrivate::callEventTest(QEvent *e)
+{
+ Q_Q(QAbstractTransition);
+ return q->eventTest(e);
+}
+
+void QAbstractTransitionPrivate::callOnTransition(QEvent *e)
+{
+ Q_Q(QAbstractTransition);
+ q->onTransition(e);
+}
+
+QState *QAbstractTransitionPrivate::sourceState() const
+{
+ return qobject_cast<QState*>(parent);
+}
+
+void QAbstractTransitionPrivate::emitTriggered()
+{
+ Q_Q(QAbstractTransition);
+ emit q->triggered();
+}
+
+/*!
+ Constructs a new QAbstractTransition object with the given \a sourceState.
+*/
+QAbstractTransition::QAbstractTransition(QState *sourceState)
+ : QObject(*new QAbstractTransitionPrivate, sourceState)
+{
+}
+
+/*!
+ \internal
+*/
+QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd,
+ QState *parent)
+ : QObject(dd, parent)
+{
+}
+
+/*!
+ Destroys this transition.
+*/
+QAbstractTransition::~QAbstractTransition()
+{
+}
+
+/*!
+ Returns the source state of this transition, or 0 if this transition has no
+ source state.
+*/
+QState *QAbstractTransition::sourceState() const
+{
+ Q_D(const QAbstractTransition);
+ return d->sourceState();
+}
+
+/*!
+ Returns the target state of this transition, or 0 if the transition has no
+ target.
+*/
+QAbstractState *QAbstractTransition::targetState() const
+{
+ Q_D(const QAbstractTransition);
+ if (d->targetStates.isEmpty())
+ return 0;
+ return d->targetStates.first().data();
+}
+
+/*!
+ Sets the \a target state of this transition.
+*/
+void QAbstractTransition::setTargetState(QAbstractState* target)
+{
+ Q_D(QAbstractTransition);
+ if (!target)
+ d->targetStates.clear();
+ else
+ setTargetStates(QList<QAbstractState*>() << target);
+}
+
+/*!
+ Returns the target states of this transition, or an empty list if this
+ transition has no target states.
+*/
+QList<QAbstractState*> QAbstractTransition::targetStates() const
+{
+ Q_D(const QAbstractTransition);
+ QList<QAbstractState*> result;
+ for (int i = 0; i < d->targetStates.size(); ++i) {
+ QAbstractState *target = d->targetStates.at(i).data();
+ if (target)
+ result.append(target);
+ }
+ return result;
+}
+
+/*!
+ Sets the target states of this transition to be the given \a targets.
+*/
+void QAbstractTransition::setTargetStates(const QList<QAbstractState*> &targets)
+{
+ Q_D(QAbstractTransition);
+
+ for (int i = 0; i < targets.size(); ++i) {
+ QAbstractState *target = targets.at(i);
+ if (!target) {
+ qWarning("QAbstractTransition::setTargetStates: target state(s) cannot be null");
+ return;
+ }
+ }
+
+ d->targetStates.clear();
+ for (int i = 0; i < targets.size(); ++i)
+ d->targetStates.append(targets.at(i));
+}
+
+/*!
+ Returns the state machine that this transition is part of, or 0 if the
+ transition is not part of a state machine.
+*/
+QStateMachine *QAbstractTransition::machine() const
+{
+ Q_D(const QAbstractTransition);
+ return d->machine();
+}
+
+#ifndef QT_NO_ANIMATION
+
+/*!
+ Adds the given \a animation to this transition.
+ The transition does not take ownership of the animation.
+
+ \sa removeAnimation(), animations()
+*/
+void QAbstractTransition::addAnimation(QAbstractAnimation *animation)
+{
+ Q_D(QAbstractTransition);
+ if (!animation) {
+ qWarning("QAbstractTransition::addAnimation: cannot add null animation");
+ return;
+ }
+ d->animations.append(animation);
+}
+
+/*!
+ Removes the given \a animation from this transition.
+
+ \sa addAnimation()
+*/
+void QAbstractTransition::removeAnimation(QAbstractAnimation *animation)
+{
+ Q_D(QAbstractTransition);
+ if (!animation) {
+ qWarning("QAbstractTransition::removeAnimation: cannot remove null animation");
+ return;
+ }
+ d->animations.removeOne(animation);
+}
+
+/*!
+ Returns the list of animations associated with this transition, or an empty
+ list if it has no animations.
+
+ \sa addAnimation()
+*/
+QList<QAbstractAnimation*> QAbstractTransition::animations() const
+{
+ Q_D(const QAbstractTransition);
+ return d->animations;
+}
+
+#endif
+
+/*!
+ \fn QAbstractTransition::eventTest(QEvent *event)
+
+ This function is called to determine whether the given \a event should cause
+ this transition to trigger. Reimplement this function and return true if the
+ event should trigger the transition, otherwise return false.
+*/
+
+/*!
+ \fn QAbstractTransition::onTransition(QEvent *event)
+
+ This function is called when the transition is triggered. The given \a event
+ is what caused the transition to trigger. Reimplement this function to
+ perform custom processing when the transition is triggered.
+*/
+
+/*!
+ \fn QAbstractTransition::triggered()
+
+ This signal is emitted when the transition has been triggered (after
+ onTransition() has been called).
+*/
+
+/*!
+ \reimp
+*/
+bool QAbstractTransition::event(QEvent *e)
+{
+ return QObject::event(e);
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_STATEMACHINE
diff --git a/src/corelib/statemachine/qabstracttransition.h b/src/corelib/statemachine/qabstracttransition.h
new file mode 100644
index 0000000000..74bb843c84
--- /dev/null
+++ b/src/corelib/statemachine/qabstracttransition.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTTRANSITION_H
+#define QABSTRACTTRANSITION_H
+
+#include <QtCore/qobject.h>
+
+#include <QtCore/qlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_STATEMACHINE
+
+class QEvent;
+class QAbstractState;
+class QState;
+class QStateMachine;
+
+#ifndef QT_NO_ANIMATION
+class QAbstractAnimation;
+#endif
+
+class QAbstractTransitionPrivate;
+class Q_CORE_EXPORT QAbstractTransition : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QState* sourceState READ sourceState)
+ Q_PROPERTY(QAbstractState* targetState READ targetState WRITE setTargetState)
+ Q_PROPERTY(QList<QAbstractState*> targetStates READ targetStates WRITE setTargetStates)
+public:
+ QAbstractTransition(QState *sourceState = 0);
+ virtual ~QAbstractTransition();
+
+ QState *sourceState() const;
+ QAbstractState *targetState() const;
+ void setTargetState(QAbstractState* target);
+ QList<QAbstractState*> targetStates() const;
+ void setTargetStates(const QList<QAbstractState*> &targets);
+
+ QStateMachine *machine() const;
+
+#ifndef QT_NO_ANIMATION
+ void addAnimation(QAbstractAnimation *animation);
+ void removeAnimation(QAbstractAnimation *animation);
+ QList<QAbstractAnimation*> animations() const;
+#endif
+
+Q_SIGNALS:
+#if !defined(Q_MOC_RUN) && !defined(qdoc)
+private: // can only be emitted by QAbstractTransition
+#endif
+ void triggered();
+
+protected:
+ virtual bool eventTest(QEvent *event) = 0;
+
+ virtual void onTransition(QEvent *event) = 0;
+
+ bool event(QEvent *e);
+
+protected:
+ QAbstractTransition(QAbstractTransitionPrivate &dd, QState *parent);
+
+private:
+ Q_DISABLE_COPY(QAbstractTransition)
+ Q_DECLARE_PRIVATE(QAbstractTransition)
+};
+
+#endif //QT_NO_STATEMACHINE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/corelib/statemachine/qabstracttransition_p.h b/src/corelib/statemachine/qabstracttransition_p.h
new file mode 100644
index 0000000000..3de8447965
--- /dev/null
+++ b/src/corelib/statemachine/qabstracttransition_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTTRANSITION_P_H
+#define QABSTRACTTRANSITION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qobject_p.h>
+
+#include <QtCore/qlist.h>
+#include <QtCore/qsharedpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractState;
+class QState;
+class QStateMachine;
+
+class QAbstractTransition;
+class Q_CORE_EXPORT QAbstractTransitionPrivate
+ : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractTransition)
+public:
+ QAbstractTransitionPrivate();
+
+ static QAbstractTransitionPrivate *get(QAbstractTransition *q);
+
+ bool callEventTest(QEvent *e);
+ virtual void callOnTransition(QEvent *e);
+ QState *sourceState() const;
+ QStateMachine *machine() const;
+ void emitTriggered();
+
+ QList<QWeakPointer<QAbstractState> > targetStates;
+
+#ifndef QT_NO_ANIMATION
+ QList<QAbstractAnimation*> animations;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/corelib/statemachine/qeventtransition.cpp b/src/corelib/statemachine/qeventtransition.cpp
new file mode 100644
index 0000000000..539c307f87
--- /dev/null
+++ b/src/corelib/statemachine/qeventtransition.cpp
@@ -0,0 +1,260 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qeventtransition.h"
+
+#ifndef QT_NO_STATEMACHINE
+
+#include "qeventtransition_p.h"
+#include "qstate.h"
+#include "qstate_p.h"
+#include "qstatemachine.h"
+#include "qstatemachine_p.h"
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QEventTransition
+
+ \brief The QEventTransition class provides a QObject-specific transition for Qt events.
+
+ \since 4.6
+ \ingroup statemachine
+
+ A QEventTransition object binds an event to a particular QObject.
+ QEventTransition is part of \l{The State Machine Framework}.
+
+ Example:
+
+ \code
+ QPushButton *button = ...;
+ QState *s1 = ...;
+ QState *s2 = ...;
+ // If in s1 and the button receives an Enter event, transition to s2
+ QEventTransition *enterTransition = new QEventTransition(button, QEvent::Enter);
+ enterTransition->setTargetState(s2);
+ s1->addTransition(enterTransition);
+ // If in s2 and the button receives an Exit event, transition back to s1
+ QEventTransition *leaveTransition = new QEventTransition(button, QEvent::Leave);
+ leaveTransition->setTargetState(s1);
+ s2->addTransition(leaveTransition);
+ \endcode
+
+ \section1 Subclassing
+
+ When reimplementing the eventTest() function, you should first call the base
+ implementation to verify that the event is a QStateMachine::WrappedEvent for
+ the proper object and event type. You may then cast the event to a
+ QStateMachine::WrappedEvent and get the original event by calling
+ QStateMachine::WrappedEvent::event(), and perform additional checks on that
+ object.
+
+ \sa QState::addTransition()
+*/
+
+/*!
+ \property QEventTransition::eventSource
+
+ \brief the event source that this event transition is associated with
+*/
+
+/*!
+ \property QEventTransition::eventType
+
+ \brief the type of event that this event transition is associated with
+*/
+QEventTransitionPrivate::QEventTransitionPrivate()
+{
+ object = 0;
+ eventType = QEvent::None;
+ registered = false;
+}
+
+QEventTransitionPrivate *QEventTransitionPrivate::get(QEventTransition *q)
+{
+ return q->d_func();
+}
+
+void QEventTransitionPrivate::unregister()
+{
+ Q_Q(QEventTransition);
+ if (!registered || !machine())
+ return;
+ QStateMachinePrivate::get(machine())->unregisterEventTransition(q);
+}
+
+void QEventTransitionPrivate::maybeRegister()
+{
+ Q_Q(QEventTransition);
+ if (!machine() || !machine()->configuration().contains(sourceState()))
+ return;
+ QStateMachinePrivate::get(machine())->registerEventTransition(q);
+}
+
+/*!
+ Constructs a new QEventTransition object with the given \a sourceState.
+*/
+QEventTransition::QEventTransition(QState *sourceState)
+ : QAbstractTransition(*new QEventTransitionPrivate, sourceState)
+{
+}
+
+/*!
+ Constructs a new QEventTransition object associated with events of the given
+ \a type for the given \a object, and with the given \a sourceState.
+*/
+QEventTransition::QEventTransition(QObject *object, QEvent::Type type,
+ QState *sourceState)
+ : QAbstractTransition(*new QEventTransitionPrivate, sourceState)
+{
+ Q_D(QEventTransition);
+ d->registered = false;
+ d->object = object;
+ d->eventType = type;
+}
+
+/*!
+ \internal
+*/
+QEventTransition::QEventTransition(QEventTransitionPrivate &dd, QState *parent)
+ : QAbstractTransition(dd, parent)
+{
+}
+
+/*!
+ \internal
+*/
+QEventTransition::QEventTransition(QEventTransitionPrivate &dd, QObject *object,
+ QEvent::Type type, QState *parent)
+ : QAbstractTransition(dd, parent)
+{
+ Q_D(QEventTransition);
+ d->registered = false;
+ d->object = object;
+ d->eventType = type;
+}
+
+/*!
+ Destroys this QObject event transition.
+*/
+QEventTransition::~QEventTransition()
+{
+}
+
+/*!
+ Returns the event type that this event transition is associated with.
+*/
+QEvent::Type QEventTransition::eventType() const
+{
+ Q_D(const QEventTransition);
+ return d->eventType;
+}
+
+/*!
+ Sets the event \a type that this event transition is associated with.
+*/
+void QEventTransition::setEventType(QEvent::Type type)
+{
+ Q_D(QEventTransition);
+ if (d->eventType == type)
+ return;
+ d->unregister();
+ d->eventType = type;
+ d->maybeRegister();
+}
+
+/*!
+ Returns the event source associated with this event transition.
+*/
+QObject *QEventTransition::eventSource() const
+{
+ Q_D(const QEventTransition);
+ return d->object;
+}
+
+/*!
+ Sets the event source associated with this event transition to be the given
+ \a object.
+*/
+void QEventTransition::setEventSource(QObject *object)
+{
+ Q_D(QEventTransition);
+ if (d->object == object)
+ return;
+ d->unregister();
+ d->object = object;
+ d->maybeRegister();
+}
+
+/*!
+ \reimp
+*/
+bool QEventTransition::eventTest(QEvent *event)
+{
+ Q_D(const QEventTransition);
+ if (event->type() == QEvent::StateMachineWrapped) {
+ QStateMachine::WrappedEvent *we = static_cast<QStateMachine::WrappedEvent*>(event);
+ return (we->object() == d->object)
+ && (we->event()->type() == d->eventType);
+ }
+ return false;
+}
+
+/*!
+ \reimp
+*/
+void QEventTransition::onTransition(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+bool QEventTransition::event(QEvent *e)
+{
+ return QAbstractTransition::event(e);
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_STATEMACHINE
diff --git a/src/corelib/statemachine/qeventtransition.h b/src/corelib/statemachine/qeventtransition.h
new file mode 100644
index 0000000000..838c12e7a9
--- /dev/null
+++ b/src/corelib/statemachine/qeventtransition.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEVENTTRANSITION_H
+#define QEVENTTRANSITION_H
+
+#include <QtCore/qabstracttransition.h>
+#include <QtCore/qcoreevent.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_STATEMACHINE
+
+class QEventTransitionPrivate;
+class Q_CORE_EXPORT QEventTransition : public QAbstractTransition
+{
+ Q_OBJECT
+ Q_PROPERTY(QObject* eventSource READ eventSource WRITE setEventSource)
+ Q_PROPERTY(QEvent::Type eventType READ eventType WRITE setEventType)
+public:
+ QEventTransition(QState *sourceState = 0);
+ QEventTransition(QObject *object, QEvent::Type type, QState *sourceState = 0);
+ ~QEventTransition();
+
+ QObject *eventSource() const;
+ void setEventSource(QObject *object);
+
+ QEvent::Type eventType() const;
+ void setEventType(QEvent::Type type);
+
+protected:
+ bool eventTest(QEvent *event);
+ void onTransition(QEvent *event);
+
+ bool event(QEvent *e);
+
+protected:
+ QEventTransition(QEventTransitionPrivate &dd, QState *parent);
+ QEventTransition(QEventTransitionPrivate &dd, QObject *object,
+ QEvent::Type type, QState *parent);
+
+private:
+ Q_DISABLE_COPY(QEventTransition)
+ Q_DECLARE_PRIVATE(QEventTransition)
+};
+
+#endif //QT_NO_STATEMACHINE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/corelib/statemachine/qeventtransition_p.h b/src/corelib/statemachine/qeventtransition_p.h
new file mode 100644
index 0000000000..b021f307b4
--- /dev/null
+++ b/src/corelib/statemachine/qeventtransition_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QEVENTTRANSITION_P_H
+#define QEVENTTRANSITION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qabstracttransition_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QEventTransition;
+class Q_CORE_EXPORT QEventTransitionPrivate : public QAbstractTransitionPrivate
+{
+ Q_DECLARE_PUBLIC(QEventTransition)
+public:
+ QEventTransitionPrivate();
+
+ static QEventTransitionPrivate *get(QEventTransition *q);
+
+ void unregister();
+ void maybeRegister();
+
+ bool registered;
+ QObject *object;
+ QEvent::Type eventType;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/corelib/statemachine/qfinalstate.cpp b/src/corelib/statemachine/qfinalstate.cpp
new file mode 100644
index 0000000000..260b3653b9
--- /dev/null
+++ b/src/corelib/statemachine/qfinalstate.cpp
@@ -0,0 +1,140 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfinalstate.h"
+
+#ifndef QT_NO_STATEMACHINE
+
+#include "qabstractstate_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QFinalState
+
+ \brief The QFinalState class provides a final state.
+
+ \since 4.6
+ \ingroup statemachine
+
+ A final state is used to communicate that (part of) a QStateMachine has
+ finished its work. When a final top-level state is entered, the state
+ machine's \l{QStateMachine::finished()}{finished}() signal is emitted. In
+ general, when a final substate (a child of a QState) is entered, the parent
+ state's \l{QState::finished()}{finished}() signal is emitted. QFinalState
+ is part of \l{The State Machine Framework}.
+
+ To use a final state, you create a QFinalState object and add a transition
+ to it from another state. Example:
+
+ \code
+ QPushButton button;
+
+ QStateMachine machine;
+ QState *s1 = new QState();
+ QFinalState *s2 = new QFinalState();
+ s1->addTransition(&button, SIGNAL(clicked()), s2);
+ machine.addState(s1);
+ machine.addState(s2);
+
+ QObject::connect(&machine, SIGNAL(finished()), QApplication::instance(), SLOT(quit()));
+ machine.setInitialState(s1);
+ machine.start();
+ \endcode
+
+ \sa QStateMachine::finished(), QState::finished()
+*/
+
+class QFinalStatePrivate : public QAbstractStatePrivate
+{
+ Q_DECLARE_PUBLIC(QFinalState)
+
+public:
+ QFinalStatePrivate();
+};
+
+QFinalStatePrivate::QFinalStatePrivate()
+ : QAbstractStatePrivate(FinalState)
+{
+}
+
+/*!
+ Constructs a new QFinalState object with the given \a parent state.
+*/
+QFinalState::QFinalState(QState *parent)
+ : QAbstractState(*new QFinalStatePrivate, parent)
+{
+}
+
+/*!
+ Destroys this final state.
+*/
+QFinalState::~QFinalState()
+{
+}
+
+/*!
+ \reimp
+*/
+void QFinalState::onEntry(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+void QFinalState::onExit(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+bool QFinalState::event(QEvent *e)
+{
+ return QAbstractState::event(e);
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_STATEMACHINE
diff --git a/src/corelib/statemachine/qfinalstate.h b/src/corelib/statemachine/qfinalstate.h
new file mode 100644
index 0000000000..27c3fa1786
--- /dev/null
+++ b/src/corelib/statemachine/qfinalstate.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QFINALSTATE_H
+#define QFINALSTATE_H
+
+#include <QtCore/qabstractstate.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_STATEMACHINE
+
+class QFinalStatePrivate;
+class Q_CORE_EXPORT QFinalState : public QAbstractState
+{
+ Q_OBJECT
+public:
+ QFinalState(QState *parent = 0);
+ ~QFinalState();
+
+protected:
+ void onEntry(QEvent *event);
+ void onExit(QEvent *event);
+
+ bool event(QEvent *e);
+
+private:
+ Q_DISABLE_COPY(QFinalState)
+ Q_DECLARE_PRIVATE(QFinalState)
+};
+
+#endif //QT_NO_STATEMACHINE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/corelib/statemachine/qhistorystate.cpp b/src/corelib/statemachine/qhistorystate.cpp
new file mode 100644
index 0000000000..7093c327ac
--- /dev/null
+++ b/src/corelib/statemachine/qhistorystate.cpp
@@ -0,0 +1,230 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhistorystate.h"
+
+#ifndef QT_NO_STATEMACHINE
+
+#include "qhistorystate_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QHistoryState
+
+ \brief The QHistoryState class provides a means of returning to a previously active substate.
+
+ \since 4.6
+ \ingroup statemachine
+
+ A history state is a pseudo-state that represents the child state that the
+ parent state was in the last time the parent state was exited. A transition
+ with a history state as its target is in fact a transition to one of the
+ other child states of the parent state. QHistoryState is part of \l{The
+ State Machine Framework}.
+
+ Use the setDefaultState() function to set the state that should be entered
+ if the parent state has never been entered. Example:
+
+ \code
+ QStateMachine machine;
+
+ QState *s1 = new QState();
+ QState *s11 = new QState(s1);
+ QState *s12 = new QState(s1);
+
+ QHistoryState *s1h = new QHistoryState(s1);
+ s1h->setDefaultState(s11);
+
+ machine.addState(s1);
+
+ QState *s2 = new QState();
+ machine.addState(s2);
+
+ QPushButton *button = new QPushButton();
+ // Clicking the button will cause the state machine to enter the child state
+ // that s1 was in the last time s1 was exited, or the history state's default
+ // state if s1 has never been entered.
+ s1->addTransition(button, SIGNAL(clicked()), s1h);
+ \endcode
+
+ By default a history state is shallow, meaning that it won't remember nested
+ states. This can be configured through the historyType property.
+*/
+
+/*!
+ \property QHistoryState::defaultState
+
+ \brief the default state of this history state
+*/
+
+/*!
+ \property QHistoryState::historyType
+
+ \brief the type of history that this history state records
+
+ The default value of this property is QHistoryState::ShallowHistory.
+*/
+
+/*!
+ \enum QHistoryState::HistoryType
+
+ This enum specifies the type of history that a QHistoryState records.
+
+ \value ShallowHistory Only the immediate child states of the parent state
+ are recorded. In this case a transition with the history state as its
+ target will end up in the immediate child state that the parent was in the
+ last time it was exited. This is the default.
+
+ \value DeepHistory Nested states are recorded. In this case a transition
+ with the history state as its target will end up in the most deeply nested
+ descendant state the parent was in the last time it was exited.
+*/
+
+QHistoryStatePrivate::QHistoryStatePrivate()
+ : QAbstractStatePrivate(HistoryState),
+ defaultState(0), historyType(QHistoryState::ShallowHistory)
+{
+}
+
+QHistoryStatePrivate *QHistoryStatePrivate::get(QHistoryState *q)
+{
+ return q->d_func();
+}
+
+/*!
+ Constructs a new shallow history state with the given \a parent state.
+*/
+QHistoryState::QHistoryState(QState *parent)
+ : QAbstractState(*new QHistoryStatePrivate, parent)
+{
+}
+/*!
+ Constructs a new history state of the given \a type, with the given \a
+ parent state.
+*/
+QHistoryState::QHistoryState(HistoryType type, QState *parent)
+ : QAbstractState(*new QHistoryStatePrivate, parent)
+{
+ Q_D(QHistoryState);
+ d->historyType = type;
+}
+
+/*!
+ Destroys this history state.
+*/
+QHistoryState::~QHistoryState()
+{
+}
+
+/*!
+ Returns this history state's default state. The default state indicates the
+ state to transition to if the parent state has never been entered before.
+*/
+QAbstractState *QHistoryState::defaultState() const
+{
+ Q_D(const QHistoryState);
+ return d->defaultState;
+}
+
+/*!
+ Sets this history state's default state to be the given \a state.
+ \a state must be a sibling of this history state.
+
+ Note that this function does not set \a state as the initial state
+ of its parent.
+*/
+void QHistoryState::setDefaultState(QAbstractState *state)
+{
+ Q_D(QHistoryState);
+ if (state && state->parentState() != parentState()) {
+ qWarning("QHistoryState::setDefaultState: state %p does not belong "
+ "to this history state's group (%p)", state, parentState());
+ return;
+ }
+ d->defaultState = state;
+}
+
+/*!
+ Returns the type of history that this history state records.
+*/
+QHistoryState::HistoryType QHistoryState::historyType() const
+{
+ Q_D(const QHistoryState);
+ return d->historyType;
+}
+
+/*!
+ Sets the \a type of history that this history state records.
+*/
+void QHistoryState::setHistoryType(HistoryType type)
+{
+ Q_D(QHistoryState);
+ d->historyType = type;
+}
+
+/*!
+ \reimp
+*/
+void QHistoryState::onEntry(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+void QHistoryState::onExit(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+bool QHistoryState::event(QEvent *e)
+{
+ return QAbstractState::event(e);
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_STATEMACHINE
diff --git a/src/corelib/statemachine/qhistorystate.h b/src/corelib/statemachine/qhistorystate.h
new file mode 100644
index 0000000000..5e52b7942e
--- /dev/null
+++ b/src/corelib/statemachine/qhistorystate.h
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHISTORYSTATE_H
+#define QHISTORYSTATE_H
+
+#include <QtCore/qabstractstate.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_STATEMACHINE
+
+class QHistoryStatePrivate;
+class Q_CORE_EXPORT QHistoryState : public QAbstractState
+{
+ Q_OBJECT
+ Q_PROPERTY(QAbstractState* defaultState READ defaultState WRITE setDefaultState)
+ Q_PROPERTY(HistoryType historyType READ historyType WRITE setHistoryType)
+ Q_ENUMS(HistoryType)
+public:
+ enum HistoryType {
+ ShallowHistory,
+ DeepHistory
+ };
+
+ QHistoryState(QState *parent = 0);
+ QHistoryState(HistoryType type, QState *parent = 0);
+ ~QHistoryState();
+
+ QAbstractState *defaultState() const;
+ void setDefaultState(QAbstractState *state);
+
+ HistoryType historyType() const;
+ void setHistoryType(HistoryType type);
+
+protected:
+ void onEntry(QEvent *event);
+ void onExit(QEvent *event);
+
+ bool event(QEvent *e);
+
+private:
+ Q_DISABLE_COPY(QHistoryState)
+ Q_DECLARE_PRIVATE(QHistoryState)
+};
+
+#endif //QT_NO_STATEMACHINE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/corelib/statemachine/qhistorystate_p.h b/src/corelib/statemachine/qhistorystate_p.h
new file mode 100644
index 0000000000..07dc6e8051
--- /dev/null
+++ b/src/corelib/statemachine/qhistorystate_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHISTORYSTATE_P_H
+#define QHISTORYSTATE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qabstractstate_p.h"
+
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class QHistoryState;
+class QHistoryStatePrivate : public QAbstractStatePrivate
+{
+ Q_DECLARE_PUBLIC(QHistoryState)
+
+public:
+ QHistoryStatePrivate();
+
+ static QHistoryStatePrivate *get(QHistoryState *q);
+
+ QAbstractState *defaultState;
+ QHistoryState::HistoryType historyType;
+ QList<QAbstractState*> configuration;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/corelib/statemachine/qsignaleventgenerator_p.h b/src/corelib/statemachine/qsignaleventgenerator_p.h
new file mode 100644
index 0000000000..9409bc9806
--- /dev/null
+++ b/src/corelib/statemachine/qsignaleventgenerator_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSIGNALEVENTGENERATOR_P_H
+#define QSIGNALEVENTGENERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+class QStateMachine;
+
+class QSignalEventGenerator : public QObject
+{
+public:
+ QSignalEventGenerator(QStateMachine *parent);
+
+ static const QMetaObject staticMetaObject;
+ virtual const QMetaObject *metaObject() const;
+ virtual void *qt_metacast(const char *);
+ virtual int qt_metacall(QMetaObject::Call, int, void **argv);
+
+private:
+ Q_DISABLE_COPY(QSignalEventGenerator)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/corelib/statemachine/qsignaltransition.cpp b/src/corelib/statemachine/qsignaltransition.cpp
new file mode 100644
index 0000000000..b0b735bb9b
--- /dev/null
+++ b/src/corelib/statemachine/qsignaltransition.cpp
@@ -0,0 +1,264 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsignaltransition.h"
+
+#ifndef QT_NO_STATEMACHINE
+
+#include "qsignaltransition_p.h"
+#include "qstate.h"
+#include "qstate_p.h"
+#include "qstatemachine.h"
+#include "qstatemachine_p.h"
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSignalTransition
+
+ \brief The QSignalTransition class provides a transition based on a Qt signal.
+
+ \since 4.6
+ \ingroup statemachine
+
+ Typically you would use the overload of QState::addTransition() that takes a
+ sender and signal as arguments, rather than creating QSignalTransition
+ objects directly. QSignalTransition is part of \l{The State Machine
+ Framework}.
+
+ You can subclass QSignalTransition and reimplement eventTest() to make a
+ signal transition conditional; the event object passed to eventTest() will
+ be a QStateMachine::SignalEvent object. Example:
+
+ \code
+ class CheckedTransition : public QSignalTransition
+ {
+ public:
+ CheckedTransition(QCheckBox *check)
+ : QSignalTransition(check, SIGNAL(stateChanged(int))) {}
+ protected:
+ bool eventTest(QEvent *e) {
+ if (!QSignalTransition::eventTest(e))
+ return false;
+ QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(e);
+ return (se->arguments().at(0).toInt() == Qt::Checked);
+ }
+ };
+
+ ...
+
+ QCheckBox *check = new QCheckBox();
+ check->setTristate(true);
+
+ QState *s1 = new QState();
+ QState *s2 = new QState();
+ CheckedTransition *t1 = new CheckedTransition(check);
+ t1->setTargetState(s2);
+ s1->addTransition(t1);
+ \endcode
+*/
+
+/*!
+ \property QSignalTransition::senderObject
+
+ \brief the sender object that this signal transition is associated with
+*/
+
+/*!
+ \property QSignalTransition::signal
+
+ \brief the signal that this signal transition is associated with
+*/
+
+QSignalTransitionPrivate::QSignalTransitionPrivate()
+{
+ sender = 0;
+ signalIndex = -1;
+}
+
+QSignalTransitionPrivate *QSignalTransitionPrivate::get(QSignalTransition *q)
+{
+ return q->d_func();
+}
+
+void QSignalTransitionPrivate::unregister()
+{
+ Q_Q(QSignalTransition);
+ if ((signalIndex == -1) || !machine())
+ return;
+ QStateMachinePrivate::get(machine())->unregisterSignalTransition(q);
+}
+
+void QSignalTransitionPrivate::maybeRegister()
+{
+ Q_Q(QSignalTransition);
+ if (!machine() || !machine()->configuration().contains(sourceState()))
+ return;
+ QStateMachinePrivate::get(machine())->registerSignalTransition(q);
+}
+
+/*!
+ Constructs a new signal transition with the given \a sourceState.
+*/
+QSignalTransition::QSignalTransition(QState *sourceState)
+ : QAbstractTransition(*new QSignalTransitionPrivate, sourceState)
+{
+}
+
+/*!
+ Constructs a new signal transition associated with the given \a signal of
+ the given \a sender, and with the given \a sourceState.
+*/
+QSignalTransition::QSignalTransition(QObject *sender, const char *signal,
+ QState *sourceState)
+ : QAbstractTransition(*new QSignalTransitionPrivate, sourceState)
+{
+ Q_D(QSignalTransition);
+ d->sender = sender;
+ d->signal = signal;
+}
+
+/*!
+ Destroys this signal transition.
+*/
+QSignalTransition::~QSignalTransition()
+{
+}
+
+/*!
+ Returns the sender object associated with this signal transition.
+*/
+QObject *QSignalTransition::senderObject() const
+{
+ Q_D(const QSignalTransition);
+ return d->sender;
+}
+
+/*!
+ Sets the \a sender object associated with this signal transition.
+*/
+void QSignalTransition::setSenderObject(QObject *sender)
+{
+ Q_D(QSignalTransition);
+ if (sender == d->sender)
+ return;
+ d->unregister();
+ d->sender = sender;
+ d->maybeRegister();
+}
+
+/*!
+ Returns the signal associated with this signal transition.
+*/
+QByteArray QSignalTransition::signal() const
+{
+ Q_D(const QSignalTransition);
+ return d->signal;
+}
+
+/*!
+ Sets the \a signal associated with this signal transition.
+*/
+void QSignalTransition::setSignal(const QByteArray &signal)
+{
+ Q_D(QSignalTransition);
+ if (signal == d->signal)
+ return;
+ d->unregister();
+ d->signal = signal;
+ d->maybeRegister();
+}
+
+/*!
+ \reimp
+
+ The default implementation returns true if the \a event is a
+ QStateMachine::SignalEvent object and the event's sender and signal index
+ match this transition, and returns false otherwise.
+*/
+bool QSignalTransition::eventTest(QEvent *event)
+{
+ Q_D(const QSignalTransition);
+ if (event->type() == QEvent::StateMachineSignal) {
+ if (d->signalIndex == -1)
+ return false;
+ QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(event);
+ return (se->sender() == d->sender)
+ && (se->signalIndex() == d->signalIndex);
+ }
+ return false;
+}
+
+/*!
+ \reimp
+*/
+void QSignalTransition::onTransition(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+bool QSignalTransition::event(QEvent *e)
+{
+ return QAbstractTransition::event(e);
+}
+
+void QSignalTransitionPrivate::callOnTransition(QEvent *e)
+{
+ Q_Q(QSignalTransition);
+
+ if (e->type() == QEvent::StateMachineSignal) {
+ QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent *>(e);
+ int savedSignalIndex = se->m_signalIndex;
+ se->m_signalIndex = originalSignalIndex;
+ q->onTransition(e);
+ se->m_signalIndex = savedSignalIndex;
+ } else {
+ q->onTransition(e);
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_STATEMACHINE
diff --git a/src/corelib/statemachine/qsignaltransition.h b/src/corelib/statemachine/qsignaltransition.h
new file mode 100644
index 0000000000..93ec73433a
--- /dev/null
+++ b/src/corelib/statemachine/qsignaltransition.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSIGNALTRANSITION_H
+#define QSIGNALTRANSITION_H
+
+#include <QtCore/qabstracttransition.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_STATEMACHINE
+
+class QSignalTransitionPrivate;
+class Q_CORE_EXPORT QSignalTransition : public QAbstractTransition
+{
+ Q_OBJECT
+ Q_PROPERTY(QObject* senderObject READ senderObject WRITE setSenderObject)
+ Q_PROPERTY(QByteArray signal READ signal WRITE setSignal)
+public:
+ QSignalTransition(QState *sourceState = 0);
+ QSignalTransition(QObject *sender, const char *signal,
+ QState *sourceState = 0);
+ ~QSignalTransition();
+
+ QObject *senderObject() const;
+ void setSenderObject(QObject *sender);
+
+ QByteArray signal() const;
+ void setSignal(const QByteArray &signal);
+
+protected:
+ bool eventTest(QEvent *event);
+ void onTransition(QEvent *event);
+
+ bool event(QEvent *e);
+
+private:
+ Q_DISABLE_COPY(QSignalTransition)
+ Q_DECLARE_PRIVATE(QSignalTransition)
+};
+
+#endif //QT_NO_STATEMACHINE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/corelib/statemachine/qsignaltransition_p.h b/src/corelib/statemachine/qsignaltransition_p.h
new file mode 100644
index 0000000000..5becfe5b13
--- /dev/null
+++ b/src/corelib/statemachine/qsignaltransition_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSIGNALTRANSITION_P_H
+#define QSIGNALTRANSITION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qabstracttransition_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSignalTransition;
+class QSignalTransitionPrivate : public QAbstractTransitionPrivate
+{
+ Q_DECLARE_PUBLIC(QSignalTransition)
+public:
+ QSignalTransitionPrivate();
+
+ static QSignalTransitionPrivate *get(QSignalTransition *q);
+
+ void unregister();
+ void maybeRegister();
+
+ virtual void callOnTransition(QEvent *e);
+
+ QObject *sender;
+ QByteArray signal;
+ int signalIndex;
+ int originalSignalIndex;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp
new file mode 100644
index 0000000000..f8f4f510b7
--- /dev/null
+++ b/src/corelib/statemachine/qstate.cpp
@@ -0,0 +1,526 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qstate.h"
+
+#ifndef QT_NO_STATEMACHINE
+
+#include "qstate_p.h"
+#include "qhistorystate.h"
+#include "qhistorystate_p.h"
+#include "qabstracttransition.h"
+#include "qabstracttransition_p.h"
+#include "qsignaltransition.h"
+#include "qstatemachine.h"
+#include "qstatemachine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QState
+
+ \brief The QState class provides a general-purpose state for QStateMachine.
+
+ \since 4.6
+ \ingroup statemachine
+
+ QState objects can have child states, and can have transitions to other
+ states. QState is part of \l{The State Machine Framework}.
+
+ The addTransition() function adds a transition. The removeTransition()
+ function removes a transition. The transitions() function returns the
+ state's outgoing transitions.
+
+ The assignProperty() function is used for defining property assignments that
+ should be performed when a state is entered.
+
+ Top-level states must be passed a QStateMachine object as their parent
+ state, or added to a state machine using QStateMachine::addState().
+
+ \section1 States with Child States
+
+ The childMode property determines how child states are treated. For
+ non-parallel state groups, the setInitialState() function must be called to
+ set the initial state. The child states are mutually exclusive states, and
+ the state machine needs to know which child state to enter when the parent
+ state is the target of a transition.
+
+ The state emits the QState::finished() signal when a final child state
+ (QFinalState) is entered.
+
+ The setErrorState() sets the state's error state. The error state is the
+ state that the state machine will transition to if an error is detected when
+ attempting to enter the state (e.g. because no initial state has been set).
+
+*/
+
+/*!
+ \property QState::initialState
+
+ \brief the initial state of this state (one of its child states)
+*/
+
+/*!
+ \property QState::errorState
+
+ \brief the error state of this state
+*/
+
+/*!
+ \property QState::childMode
+
+ \brief the child mode of this state
+
+ The default value of this property is QState::ExclusiveStates.
+*/
+
+/*!
+ \enum QState::ChildMode
+
+ This enum specifies how a state's child states are treated.
+
+ \value ExclusiveStates The child states are mutually exclusive and an
+ initial state must be set by calling QState::setInitialState().
+
+ \value ParallelStates The child states are parallel. When the parent state
+ is entered, all its child states are entered in parallel.
+*/
+
+QStatePrivate::QStatePrivate()
+ : QAbstractStatePrivate(StandardState),
+ errorState(0), initialState(0), childMode(QState::ExclusiveStates),
+ childStatesListNeedsRefresh(true), transitionsListNeedsRefresh(true)
+{
+}
+
+QStatePrivate::~QStatePrivate()
+{
+}
+
+void QStatePrivate::emitFinished()
+{
+ Q_Q(QState);
+ emit q->finished();
+}
+
+void QStatePrivate::emitPropertiesAssigned()
+{
+ Q_Q(QState);
+ emit q->propertiesAssigned();
+}
+
+/*!
+ Constructs a new state with the given \a parent state.
+*/
+QState::QState(QState *parent)
+ : QAbstractState(*new QStatePrivate, parent)
+{
+}
+
+/*!
+ Constructs a new state with the given \a childMode and the given \a parent
+ state.
+*/
+QState::QState(ChildMode childMode, QState *parent)
+ : QAbstractState(*new QStatePrivate, parent)
+{
+ Q_D(QState);
+ d->childMode = childMode;
+}
+
+/*!
+ \internal
+*/
+QState::QState(QStatePrivate &dd, QState *parent)
+ : QAbstractState(dd, parent)
+{
+}
+
+/*!
+ Destroys this state.
+*/
+QState::~QState()
+{
+}
+
+QList<QAbstractState*> QStatePrivate::childStates() const
+{
+ if (childStatesListNeedsRefresh) {
+ childStatesList.clear();
+ QList<QObject*>::const_iterator it;
+ for (it = children.constBegin(); it != children.constEnd(); ++it) {
+ QAbstractState *s = qobject_cast<QAbstractState*>(*it);
+ if (!s || qobject_cast<QHistoryState*>(s))
+ continue;
+ childStatesList.append(s);
+ }
+ childStatesListNeedsRefresh = false;
+ }
+ return childStatesList;
+}
+
+QList<QHistoryState*> QStatePrivate::historyStates() const
+{
+ QList<QHistoryState*> result;
+ QList<QObject*>::const_iterator it;
+ for (it = children.constBegin(); it != children.constEnd(); ++it) {
+ QHistoryState *h = qobject_cast<QHistoryState*>(*it);
+ if (h)
+ result.append(h);
+ }
+ return result;
+}
+
+QList<QAbstractTransition*> QStatePrivate::transitions() const
+{
+ if (transitionsListNeedsRefresh) {
+ transitionsList.clear();
+ QList<QObject*>::const_iterator it;
+ for (it = children.constBegin(); it != children.constEnd(); ++it) {
+ QAbstractTransition *t = qobject_cast<QAbstractTransition*>(*it);
+ if (t)
+ transitionsList.append(t);
+ }
+ transitionsListNeedsRefresh = false;
+ }
+ return transitionsList;
+}
+
+#ifndef QT_NO_PROPERTIES
+
+/*!
+ Instructs this state to set the property with the given \a name of the given
+ \a object to the given \a value when the state is entered.
+
+ \sa propertiesAssigned()
+*/
+void QState::assignProperty(QObject *object, const char *name,
+ const QVariant &value)
+{
+ Q_D(QState);
+ if (!object) {
+ qWarning("QState::assignProperty: cannot assign property '%s' of null object", name);
+ return;
+ }
+ for (int i = 0; i < d->propertyAssignments.size(); ++i) {
+ QPropertyAssignment &assn = d->propertyAssignments[i];
+ if ((assn.object == object) && (assn.propertyName == name)) {
+ assn.value = value;
+ return;
+ }
+ }
+ d->propertyAssignments.append(QPropertyAssignment(object, name, value));
+}
+
+#endif // QT_NO_PROPERTIES
+
+/*!
+ Returns this state's error state.
+
+ \sa QStateMachine::error()
+*/
+QAbstractState *QState::errorState() const
+{
+ Q_D(const QState);
+ return d->errorState;
+}
+
+/*!
+ Sets this state's error state to be the given \a state. If the error state
+ is not set, or if it is set to 0, the state will inherit its parent's error
+ state recursively. If no error state is set for the state itself or any of
+ its ancestors, an error will cause the machine to stop executing and an error
+ will be printed to the console.
+*/
+void QState::setErrorState(QAbstractState *state)
+{
+ Q_D(QState);
+ if (state != 0 && qobject_cast<QStateMachine*>(state)) {
+ qWarning("QStateMachine::setErrorState: root state cannot be error state");
+ return;
+ }
+ if (state != 0 && (!state->machine() || ((state->machine() != machine()) && !qobject_cast<QStateMachine*>(this)))) {
+ qWarning("QState::setErrorState: error state cannot belong "
+ "to a different state machine");
+ return;
+ }
+
+ d->errorState = state;
+}
+
+/*!
+ Adds the given \a transition. The transition has this state as the source.
+ This state takes ownership of the transition.
+*/
+void QState::addTransition(QAbstractTransition *transition)
+{
+ Q_D(QState);
+ if (!transition) {
+ qWarning("QState::addTransition: cannot add null transition");
+ return ;
+ }
+
+ transition->setParent(this);
+ const QList<QWeakPointer<QAbstractState> > &targets = QAbstractTransitionPrivate::get(transition)->targetStates;
+ for (int i = 0; i < targets.size(); ++i) {
+ QAbstractState *t = targets.at(i).data();
+ if (!t) {
+ qWarning("QState::addTransition: cannot add transition to null state");
+ return ;
+ }
+ if ((QAbstractStatePrivate::get(t)->machine() != d->machine())
+ && QAbstractStatePrivate::get(t)->machine() && d->machine()) {
+ qWarning("QState::addTransition: cannot add transition "
+ "to a state in a different state machine");
+ return ;
+ }
+ }
+ if (machine() != 0 && machine()->configuration().contains(this))
+ QStateMachinePrivate::get(machine())->registerTransitions(this);
+}
+
+/*!
+ Adds a transition associated with the given \a signal of the given \a sender
+ object, and returns the new QSignalTransition object. The transition has
+ this state as the source, and the given \a target as the target state.
+*/
+QSignalTransition *QState::addTransition(QObject *sender, const char *signal,
+ QAbstractState *target)
+{
+ if (!sender) {
+ qWarning("QState::addTransition: sender cannot be null");
+ return 0;
+ }
+ if (!signal) {
+ qWarning("QState::addTransition: signal cannot be null");
+ return 0;
+ }
+ if (!target) {
+ qWarning("QState::addTransition: cannot add transition to null state");
+ return 0;
+ }
+ int offset = (*signal == '0'+QSIGNAL_CODE) ? 1 : 0;
+ const QMetaObject *meta = sender->metaObject();
+ if (meta->indexOfSignal(signal+offset) == -1) {
+ if (meta->indexOfSignal(QMetaObject::normalizedSignature(signal+offset)) == -1) {
+ qWarning("QState::addTransition: no such signal %s::%s",
+ meta->className(), signal+offset);
+ return 0;
+ }
+ }
+ QSignalTransition *trans = new QSignalTransition(sender, signal);
+ trans->setTargetState(target);
+ addTransition(trans);
+ return trans;
+}
+
+namespace {
+
+// ### Make public?
+class UnconditionalTransition : public QAbstractTransition
+{
+public:
+ UnconditionalTransition(QAbstractState *target)
+ : QAbstractTransition()
+ { setTargetState(target); }
+protected:
+ void onTransition(QEvent *) {}
+ bool eventTest(QEvent *) { return true; }
+};
+
+} // namespace
+
+/*!
+ Adds an unconditional transition from this state to the given \a target
+ state, and returns then new transition object.
+*/
+QAbstractTransition *QState::addTransition(QAbstractState *target)
+{
+ if (!target) {
+ qWarning("QState::addTransition: cannot add transition to null state");
+ return 0;
+ }
+ UnconditionalTransition *trans = new UnconditionalTransition(target);
+ addTransition(trans);
+ return trans;
+}
+
+/*!
+ Removes the given \a transition from this state. The state releases
+ ownership of the transition.
+
+ \sa addTransition()
+*/
+void QState::removeTransition(QAbstractTransition *transition)
+{
+ Q_D(QState);
+ if (!transition) {
+ qWarning("QState::removeTransition: cannot remove null transition");
+ return;
+ }
+ if (transition->sourceState() != this) {
+ qWarning("QState::removeTransition: transition %p's source state (%p)"
+ " is different from this state (%p)",
+ transition, transition->sourceState(), this);
+ return;
+ }
+ QStateMachinePrivate *mach = QStateMachinePrivate::get(d->machine());
+ if (mach)
+ mach->unregisterTransition(transition);
+ transition->setParent(0);
+}
+
+/*!
+ \since 4.7
+
+ Returns this state's outgoing transitions (i.e. transitions where
+ this state is the \l{QAbstractTransition::sourceState()}{source
+ state}), or an empty list if this state has no outgoing transitions.
+
+ \sa addTransition()
+*/
+QList<QAbstractTransition*> QState::transitions() const
+{
+ Q_D(const QState);
+ return d->transitions();
+}
+
+/*!
+ \reimp
+*/
+void QState::onEntry(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+void QState::onExit(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ Returns this state's initial state, or 0 if the state has no initial state.
+*/
+QAbstractState *QState::initialState() const
+{
+ Q_D(const QState);
+ return d->initialState;
+}
+
+/*!
+ Sets this state's initial state to be the given \a state.
+ \a state has to be a child of this state.
+*/
+void QState::setInitialState(QAbstractState *state)
+{
+ Q_D(QState);
+ if (d->childMode == QState::ParallelStates) {
+ qWarning("QState::setInitialState: ignoring attempt to set initial state "
+ "of parallel state group %p", this);
+ return;
+ }
+ if (state && (state->parentState() != this)) {
+ qWarning("QState::setInitialState: state %p is not a child of this state (%p)",
+ state, this);
+ return;
+ }
+ d->initialState = state;
+}
+
+/*!
+ Returns the child mode of this state.
+*/
+QState::ChildMode QState::childMode() const
+{
+ Q_D(const QState);
+ return d->childMode;
+}
+
+/*!
+ Sets the child \a mode of this state.
+*/
+void QState::setChildMode(ChildMode mode)
+{
+ Q_D(QState);
+ d->childMode = mode;
+}
+
+/*!
+ \reimp
+*/
+bool QState::event(QEvent *e)
+{
+ Q_D(QState);
+ if ((e->type() == QEvent::ChildAdded) || (e->type() == QEvent::ChildRemoved)) {
+ d->childStatesListNeedsRefresh = true;
+ d->transitionsListNeedsRefresh = true;
+ }
+ return QAbstractState::event(e);
+}
+
+/*!
+ \fn QState::finished()
+
+ This signal is emitted when a final child state of this state is entered.
+
+ \sa QFinalState
+*/
+
+/*!
+ \fn QState::propertiesAssigned()
+
+ This signal is emitted when all properties have been assigned their final value. If the state
+ assigns a value to one or more properties for which an animation exists (either set on the
+ transition or as a default animation on the state machine), then the signal will not be emitted
+ until all such animations have finished playing.
+
+ If there are no relevant animations, or no property assignments defined for the state, then
+ the signal will be emitted immediately before the state is entered.
+
+ \sa QState::assignProperty(), QAbstractTransition::addAnimation()
+*/
+
+QT_END_NAMESPACE
+
+#endif //QT_NO_STATEMACHINE
diff --git a/src/corelib/statemachine/qstate.h b/src/corelib/statemachine/qstate.h
new file mode 100644
index 0000000000..654dda53b1
--- /dev/null
+++ b/src/corelib/statemachine/qstate.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTATE_H
+#define QSTATE_H
+
+#include <QtCore/qabstractstate.h>
+
+#include <QtCore/qlist.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_STATEMACHINE
+
+class QAbstractTransition;
+class QSignalTransition;
+
+class QStatePrivate;
+class Q_CORE_EXPORT QState : public QAbstractState
+{
+ Q_OBJECT
+ Q_PROPERTY(QAbstractState* initialState READ initialState WRITE setInitialState)
+ Q_PROPERTY(QAbstractState* errorState READ errorState WRITE setErrorState)
+ Q_PROPERTY(ChildMode childMode READ childMode WRITE setChildMode)
+ Q_ENUMS(ChildMode)
+public:
+ enum ChildMode {
+ ExclusiveStates,
+ ParallelStates
+ };
+
+ QState(QState *parent = 0);
+ QState(ChildMode childMode, QState *parent = 0);
+ ~QState();
+
+ QAbstractState *errorState() const;
+ void setErrorState(QAbstractState *state);
+
+ void addTransition(QAbstractTransition *transition);
+ QSignalTransition *addTransition(QObject *sender, const char *signal, QAbstractState *target);
+ QAbstractTransition *addTransition(QAbstractState *target);
+ void removeTransition(QAbstractTransition *transition);
+ QList<QAbstractTransition*> transitions() const;
+
+ QAbstractState *initialState() const;
+ void setInitialState(QAbstractState *state);
+
+ ChildMode childMode() const;
+ void setChildMode(ChildMode mode);
+
+#ifndef QT_NO_PROPERTIES
+ void assignProperty(QObject *object, const char *name,
+ const QVariant &value);
+#endif
+
+Q_SIGNALS:
+ void finished();
+ void propertiesAssigned();
+
+protected:
+ void onEntry(QEvent *event);
+ void onExit(QEvent *event);
+
+ bool event(QEvent *e);
+
+protected:
+ QState(QStatePrivate &dd, QState *parent);
+
+private:
+ Q_DISABLE_COPY(QState)
+ Q_DECLARE_PRIVATE(QState)
+};
+
+#endif //QT_NO_STATEMACHINE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/corelib/statemachine/qstate_p.h b/src/corelib/statemachine/qstate_p.h
new file mode 100644
index 0000000000..08b13979e2
--- /dev/null
+++ b/src/corelib/statemachine/qstate_p.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTATE_P_H
+#define QSTATE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qabstractstate_p.h"
+
+#include <QtCore/qlist.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+struct QPropertyAssignment
+{
+ QPropertyAssignment()
+ : object(0), explicitlySet(true) {}
+ QPropertyAssignment(QObject *o, const QByteArray &n,
+ const QVariant &v, bool es = true)
+ : object(o), propertyName(n), value(v), explicitlySet(es)
+ {}
+ QObject *object;
+ QByteArray propertyName;
+ QVariant value;
+ bool explicitlySet;
+};
+
+class QAbstractTransition;
+class QHistoryState;
+
+class QState;
+class Q_AUTOTEST_EXPORT QStatePrivate : public QAbstractStatePrivate
+{
+ Q_DECLARE_PUBLIC(QState)
+public:
+ QStatePrivate();
+ ~QStatePrivate();
+
+ static QStatePrivate *get(QState *q) { return q ? q->d_func() : 0; }
+ static const QStatePrivate *get(const QState *q) { return q? q->d_func() : 0; }
+
+ QList<QAbstractState*> childStates() const;
+ QList<QHistoryState*> historyStates() const;
+ QList<QAbstractTransition*> transitions() const;
+
+ void emitFinished();
+ void emitPropertiesAssigned();
+
+ QAbstractState *errorState;
+ QAbstractState *initialState;
+ QState::ChildMode childMode;
+ mutable bool childStatesListNeedsRefresh;
+ mutable QList<QAbstractState*> childStatesList;
+ mutable bool transitionsListNeedsRefresh;
+ mutable QList<QAbstractTransition*> transitionsList;
+
+ QList<QPropertyAssignment> propertyAssignments;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp
new file mode 100644
index 0000000000..b2af5ca961
--- /dev/null
+++ b/src/corelib/statemachine/qstatemachine.cpp
@@ -0,0 +1,2401 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qstatemachine.h"
+
+#ifndef QT_NO_STATEMACHINE
+
+#include "qstate.h"
+#include "qstate_p.h"
+#include "qstatemachine_p.h"
+#include "qabstracttransition.h"
+#include "qabstracttransition_p.h"
+#include "qsignaltransition.h"
+#include "qsignaltransition_p.h"
+#include "qsignaleventgenerator_p.h"
+#include "qabstractstate.h"
+#include "qabstractstate_p.h"
+#include "qfinalstate.h"
+#include "qhistorystate.h"
+#include "qhistorystate_p.h"
+#include "private/qobject_p.h"
+#include "private/qthread_p.h"
+
+#ifndef QT_NO_STATEMACHINE_EVENTFILTER
+#include "qeventtransition.h"
+#include "qeventtransition_p.h"
+#endif
+
+#ifndef QT_NO_ANIMATION
+#include "qpropertyanimation.h"
+#include "qanimationgroup.h"
+#include <private/qvariantanimation_p.h>
+#endif
+
+#include <QtCore/qmetaobject.h>
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QStateMachine
+ \reentrant
+
+ \brief The QStateMachine class provides a hierarchical finite state machine.
+
+ \since 4.6
+ \ingroup statemachine
+
+ QStateMachine is based on the concepts and notation of
+ \l{Statecharts: A visual formalism for complex
+ systems}{Statecharts}. QStateMachine is part of \l{The State
+ Machine Framework}.
+
+ A state machine manages a set of states (classes that inherit from
+ QAbstractState) and transitions (descendants of
+ QAbstractTransition) between those states; these states and
+ transitions define a state graph. Once a state graph has been
+ built, the state machine can execute it. QStateMachine's
+ execution algorithm is based on the \l{State Chart XML: State
+ Machine Notation for Control Abstraction}{State Chart XML (SCXML)}
+ algorithm. The framework's \l{The State Machine
+ Framework}{overview} gives several state graphs and the code to
+ build them.
+
+ 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
+ machine enters when started. You can then start() the state
+ machine. The started() signal is emitted when the initial state is
+ entered.
+
+ The machine is event driven and keeps its own event loop. Events
+ are posted to the machine through postEvent(). Note that this
+ means that it executes asynchronously, and that it will not
+ progress without a running event loop. You will normally not have
+ to post events to the machine directly as Qt's transitions, e.g.,
+ QEventTransition and its subclasses, handle this. But for custom
+ transitions triggered by events, postEvent() is useful.
+
+ The state machine processes events and takes transitions until a
+ top-level final state is entered; the state machine then emits the
+ finished() signal. You can also stop() the state machine
+ explicitly. The stopped() signal is emitted in this case.
+
+ The following snippet shows a state machine that will finish when a button
+ is clicked:
+
+ \snippet doc/src/snippets/code/src_corelib_statemachine_qstatemachine.cpp simple state machine
+
+ This code example uses QState, which inherits QAbstractState. The
+ QState class provides a state that you can use to set properties
+ and invoke methods on \l{QObject}s when the state is entered or
+ exited. It also contains convenience functions for adding
+ transitions, e.g., \l{QSignalTransition}s as in this example. See
+ the QState class description for further details.
+
+ If an error is encountered, the machine will look for an
+ \l{errorState}{error state}, and if one is available, it will
+ enter this state. The types of errors possible are described by the
+ \l{QStateMachine::}{Error} enum. After the error state is entered,
+ the type of the error can be retrieved with error(). The execution
+ of the state graph will not stop when the error state is entered. If
+ no error state applies to the erroneous state, the machine will stop
+ executing and an error message will be printed to the console.
+
+ \sa QAbstractState, QAbstractTransition, QState, {The State Machine Framework}
+*/
+
+/*!
+ \property QStateMachine::errorString
+
+ \brief the error string of this state machine
+*/
+
+/*!
+ \property QStateMachine::globalRestorePolicy
+
+ \brief the restore policy for states of this state machine.
+
+ The default value of this property is
+ QStateMachine::DontRestoreProperties.
+*/
+
+#ifndef QT_NO_ANIMATION
+/*!
+ \property QStateMachine::animated
+
+ \brief whether animations are enabled
+
+ The default value of this property is true.
+
+ \sa QAbstractTransition::addAnimation()
+*/
+#endif
+
+// #define QSTATEMACHINE_DEBUG
+
+QStateMachinePrivate::QStateMachinePrivate()
+{
+ isMachine = true;
+
+ state = NotRunning;
+ _startState = 0;
+ processing = false;
+ processingScheduled = false;
+ stop = false;
+ stopProcessingReason = EventQueueEmpty;
+ error = QStateMachine::NoError;
+ globalRestorePolicy = QStateMachine::DontRestoreProperties;
+ signalEventGenerator = 0;
+#ifndef QT_NO_ANIMATION
+ animated = true;
+#endif
+}
+
+QStateMachinePrivate::~QStateMachinePrivate()
+{
+ qDeleteAll(internalEventQueue);
+ qDeleteAll(externalEventQueue);
+}
+
+QStateMachinePrivate *QStateMachinePrivate::get(QStateMachine *q)
+{
+ if (q)
+ return q->d_func();
+ return 0;
+}
+
+QState *QStateMachinePrivate::rootState() const
+{
+ return const_cast<QStateMachine*>(q_func());
+}
+
+static QEvent *cloneEvent(QEvent *e)
+{
+ switch (e->type()) {
+ case QEvent::None:
+ return new QEvent(*e);
+ case QEvent::Timer:
+ return new QTimerEvent(*static_cast<QTimerEvent*>(e));
+ default:
+ Q_ASSERT_X(false, "cloneEvent()", "not implemented");
+ break;
+ }
+ return 0;
+}
+
+const QStateMachinePrivate::Handler qt_kernel_statemachine_handler = {
+ cloneEvent
+};
+
+const QStateMachinePrivate::Handler *QStateMachinePrivate::handler = &qt_kernel_statemachine_handler;
+
+Q_CORE_EXPORT const QStateMachinePrivate::Handler *qcoreStateMachineHandler()
+{
+ return &qt_kernel_statemachine_handler;
+}
+
+static int indexOfDescendant(QState *s, QAbstractState *desc)
+{
+ QList<QAbstractState*> childStates = QStatePrivate::get(s)->childStates();
+ for (int i = 0; i < childStates.size(); ++i) {
+ QAbstractState *c = childStates.at(i);
+ if ((c == desc) || QStateMachinePrivate::isDescendantOf(desc, c)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+bool QStateMachinePrivate::stateEntryLessThan(QAbstractState *s1, QAbstractState *s2)
+{
+ if (s1->parent() == s2->parent()) {
+ return s1->children().indexOf(s1)
+ < s2->children().indexOf(s2);
+ } else if (isDescendantOf(s1, s2)) {
+ return false;
+ } else if (isDescendantOf(s2, s1)) {
+ return true;
+ } else {
+ 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));
+ }
+}
+
+bool QStateMachinePrivate::stateExitLessThan(QAbstractState *s1, QAbstractState *s2)
+{
+ if (s1->parent() == s2->parent()) {
+ return s1->children().indexOf(s1)
+ < s2->children().indexOf(s2);
+ } else if (isDescendantOf(s1, s2)) {
+ return true;
+ } else if (isDescendantOf(s2, s1)) {
+ return false;
+ } else {
+ 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) const
+{
+ if (states.isEmpty())
+ return 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;
+ for (int j = states.size() - 1; (j > 0) && ok; --j) {
+ const QAbstractState *s = states.at(j);
+ if (!isDescendantOf(s, anc))
+ ok = false;
+ }
+ if (ok)
+ return anc;
+ }
+ return 0;
+}
+
+bool QStateMachinePrivate::isPreempted(const QAbstractState *s, const QSet<QAbstractTransition*> &transitions) const
+{
+ QSet<QAbstractTransition*>::const_iterator it;
+ for (it = transitions.constBegin(); it != transitions.constEnd(); ++it) {
+ QAbstractTransition *t = *it;
+ QList<QAbstractState*> lst = t->targetStates();
+ if (!lst.isEmpty()) {
+ lst.prepend(t->sourceState());
+ QAbstractState *lca = findLCA(lst);
+ if (isDescendantOf(s, lca)) {
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ':' << transitions << "preempts selection of a transition from"
+ << s << "because" << s << "is a descendant of" << lca;
+#endif
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+QSet<QAbstractTransition*> QStateMachinePrivate::selectTransitions(QEvent *event) const
+{
+ Q_Q(const QStateMachine);
+ QSet<QAbstractTransition*> enabledTransitions;
+ QSet<QAbstractState*>::const_iterator it;
+ const_cast<QStateMachine*>(q)->beginSelectTransitions(event);
+ for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
+ QAbstractState *state = *it;
+ if (!isAtomic(state))
+ continue;
+ if (isPreempted(state, enabledTransitions))
+ continue;
+ QList<QState*> lst = properAncestors(state, rootState()->parentState());
+ if (QState *grp = toStandardState(state))
+ lst.prepend(grp);
+ bool found = false;
+ for (int j = 0; (j < lst.size()) && !found; ++j) {
+ QState *s = lst.at(j);
+ QList<QAbstractTransition*> transitions = QStatePrivate::get(s)->transitions();
+ for (int k = 0; k < transitions.size(); ++k) {
+ QAbstractTransition *t = transitions.at(k);
+ if (QAbstractTransitionPrivate::get(t)->callEventTest(event)) {
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": selecting transition" << t;
+#endif
+ enabledTransitions.insert(t);
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+ const_cast<QStateMachine*>(q)->endSelectTransitions(event);
+ return enabledTransitions;
+}
+
+void QStateMachinePrivate::microstep(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions)
+{
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ": begin microstep( enabledTransitions:" << enabledTransitions << ')';
+ qDebug() << q_func() << ": configuration before exiting states:" << configuration;
+#endif
+ QList<QAbstractState*> exitedStates = exitStates(event, enabledTransitions);
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ": configuration after exiting states:" << configuration;
+#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";
+#endif
+}
+
+QList<QAbstractState*> QStateMachinePrivate::exitStates(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions)
+{
+// qDebug() << "exitStates(" << enabledTransitions << ')';
+ QSet<QAbstractState*> statesToExit;
+// QSet<QAbstractState*> statesToSnapshot;
+ for (int i = 0; i < enabledTransitions.size(); ++i) {
+ QAbstractTransition *t = enabledTransitions.at(i);
+ QList<QAbstractState*> lst = t->targetStates();
+ if (lst.isEmpty())
+ continue;
+ lst.prepend(t->sourceState());
+ QAbstractState *lca = findLCA(lst);
+ if (lca == 0) {
+ setError(QStateMachine::NoCommonAncestorForTransitionError, t->sourceState());
+ lst = pendingErrorStates.toList();
+ lst.prepend(t->sourceState());
+
+ lca = findLCA(lst);
+ Q_ASSERT(lca != 0);
+ }
+
+ {
+ QSet<QAbstractState*>::const_iterator it;
+ for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
+ QAbstractState *s = *it;
+ if (isDescendantOf(s, lca))
+ statesToExit.insert(s);
+ }
+ }
+ }
+ QList<QAbstractState*> statesToExit_sorted = statesToExit.toList();
+ qSort(statesToExit_sorted.begin(), statesToExit_sorted.end(), stateExitLessThan);
+ for (int i = 0; i < statesToExit_sorted.size(); ++i) {
+ QAbstractState *s = statesToExit_sorted.at(i);
+ if (QState *grp = toStandardState(s)) {
+ QList<QHistoryState*> hlst = QStatePrivate::get(grp)->historyStates();
+ for (int j = 0; j < hlst.size(); ++j) {
+ QHistoryState *h = hlst.at(j);
+ QHistoryStatePrivate::get(h)->configuration.clear();
+ QSet<QAbstractState*>::const_iterator it;
+ for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
+ QAbstractState *s0 = *it;
+ if (QHistoryStatePrivate::get(h)->historyType == QHistoryState::DeepHistory) {
+ if (isAtomic(s0) && isDescendantOf(s0, s))
+ QHistoryStatePrivate::get(h)->configuration.append(s0);
+ } else if (s0->parentState() == s) {
+ QHistoryStatePrivate::get(h)->configuration.append(s0);
+ }
+ }
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ": recorded" << ((QHistoryStatePrivate::get(h)->historyType == QHistoryState::DeepHistory) ? "deep" : "shallow")
+ << "history for" << s << "in" << h << ':' << QHistoryStatePrivate::get(h)->configuration;
+#endif
+ }
+ }
+ }
+ for (int i = 0; i < statesToExit_sorted.size(); ++i) {
+ QAbstractState *s = statesToExit_sorted.at(i);
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ": exiting" << s;
+#endif
+ QAbstractStatePrivate::get(s)->callOnExit(event);
+ configuration.remove(s);
+ QAbstractStatePrivate::get(s)->emitExited();
+ }
+ return statesToExit_sorted;
+}
+
+void QStateMachinePrivate::executeTransitionContent(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions)
+{
+ for (int i = 0; i < enabledTransitions.size(); ++i) {
+ QAbstractTransition *t = enabledTransitions.at(i);
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ": triggering" << t;
+#endif
+ QAbstractTransitionPrivate::get(t)->callOnTransition(event);
+ QAbstractTransitionPrivate::get(t)->emitTriggered();
+ }
+}
+
+QList<QAbstractState*> QStateMachinePrivate::enterStates(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions)
+{
+#ifdef QSTATEMACHINE_DEBUG
+ Q_Q(QStateMachine);
+#endif
+// qDebug() << "enterStates(" << enabledTransitions << ')';
+ QSet<QAbstractState*> statesToEnter;
+ QSet<QAbstractState*> statesForDefaultEntry;
+
+ if (pendingErrorStates.isEmpty()) {
+ for (int i = 0; i < enabledTransitions.size(); ++i) {
+ QAbstractTransition *t = enabledTransitions.at(i);
+ QList<QAbstractState*> lst = t->targetStates();
+ if (lst.isEmpty())
+ continue;
+ lst.prepend(t->sourceState());
+ QState *lca = findLCA(lst);
+ for (int j = 1; j < lst.size(); ++j) {
+ QAbstractState *s = lst.at(j);
+ addStatesToEnter(s, lca, statesToEnter, statesForDefaultEntry);
+ if (isParallel(lca)) {
+ QList<QAbstractState*> lcac = QStatePrivate::get(lca)->childStates();
+ foreach (QAbstractState* child,lcac) {
+ if (!statesToEnter.contains(child))
+ addStatesToEnter(child,lca,statesToEnter,statesForDefaultEntry);
+ }
+ }
+ }
+ }
+ }
+
+ // Did an error occur while selecting transitions? Then we enter the error state.
+ if (!pendingErrorStates.isEmpty()) {
+ statesToEnter.clear();
+ statesToEnter = pendingErrorStates;
+ statesForDefaultEntry = pendingErrorStatesForDefaultEntry;
+ pendingErrorStates.clear();
+ pendingErrorStatesForDefaultEntry.clear();
+ }
+
+ QList<QAbstractState*> statesToEnter_sorted = statesToEnter.toList();
+ qSort(statesToEnter_sorted.begin(), statesToEnter_sorted.end(), stateEntryLessThan);
+
+ for (int i = 0; i < statesToEnter_sorted.size(); ++i) {
+ QAbstractState *s = statesToEnter_sorted.at(i);
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": entering" << s;
+#endif
+ configuration.insert(s);
+ registerTransitions(s);
+ QAbstractStatePrivate::get(s)->callOnEntry(event);
+ QAbstractStatePrivate::get(s)->emitEntered();
+ if (statesForDefaultEntry.contains(s)) {
+ // ### executeContent(s.initial.transition.children())
+ }
+ if (isFinal(s)) {
+ QState *parent = s->parentState();
+ if (parent) {
+ if (parent != rootState()) {
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": emitting finished signal for" << parent;
+#endif
+ QStatePrivate::get(parent)->emitFinished();
+ }
+ QState *grandparent = parent->parentState();
+ if (grandparent && isParallel(grandparent)) {
+ bool allChildStatesFinal = true;
+ QList<QAbstractState*> childStates = QStatePrivate::get(grandparent)->childStates();
+ for (int j = 0; j < childStates.size(); ++j) {
+ QAbstractState *cs = childStates.at(j);
+ if (!isInFinalState(cs)) {
+ allChildStatesFinal = false;
+ break;
+ }
+ }
+ if (allChildStatesFinal && (grandparent != rootState())) {
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": emitting finished signal for" << grandparent;
+#endif
+ QStatePrivate::get(grandparent)->emitFinished();
+ }
+ }
+ }
+ }
+ }
+ {
+ QSet<QAbstractState*>::const_iterator it;
+ for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
+ if (isFinal(*it) && (*it)->parentState() == rootState()) {
+ processing = false;
+ stopProcessingReason = Finished;
+ break;
+ }
+ }
+ }
+// qDebug() << "configuration:" << configuration.toList();
+ return statesToEnter_sorted;
+}
+
+void QStateMachinePrivate::addStatesToEnter(QAbstractState *s, QState *root,
+ QSet<QAbstractState*> &statesToEnter,
+ QSet<QAbstractState*> &statesForDefaultEntry)
+{
+ if (QHistoryState *h = toHistoryState(s)) {
+ QList<QAbstractState*> hconf = QHistoryStatePrivate::get(h)->configuration;
+ if (!hconf.isEmpty()) {
+ for (int k = 0; k < hconf.size(); ++k) {
+ QAbstractState *s0 = hconf.at(k);
+ addStatesToEnter(s0, root, statesToEnter, statesForDefaultEntry);
+ }
+ #ifdef QSTATEMACHINE_DEBUG
+ qDebug() <<q_func() << ": restoring"
+ << ((QHistoryStatePrivate::get(h)->historyType == QHistoryState::DeepHistory) ? "deep" : "shallow")
+ << "history from" << s << ':' << hconf;
+ #endif
+ } else {
+ QList<QAbstractState*> hlst;
+ if (QHistoryStatePrivate::get(h)->defaultState)
+ hlst.append(QHistoryStatePrivate::get(h)->defaultState);
+
+ if (hlst.isEmpty()) {
+ setError(QStateMachine::NoDefaultStateInHistoryStateError, h);
+ } else {
+ for (int k = 0; k < hlst.size(); ++k) {
+ QAbstractState *s0 = hlst.at(k);
+ addStatesToEnter(s0, root, statesToEnter, statesForDefaultEntry);
+ }
+ #ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ": initial history targets for" << s << ':' << hlst;
+ #endif
+ }
+ }
+ } 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 = toStandardState(s);
+ QList<QAbstractState*> lst = QStatePrivate::get(grp)->childStates();
+ for (int i = 0; i < lst.size(); ++i) {
+ QAbstractState *child = lst.at(i);
+ addStatesToEnter(child, grp, statesToEnter, statesForDefaultEntry);
+ }
+ } else if (isCompound(s)) {
+ statesForDefaultEntry.insert(s);
+ QState *grp = toStandardState(s);
+ QAbstractState *initial = grp->initialState();
+ if (initial != 0) {
+ Q_ASSERT(initial->machine() == q_func());
+ addStatesToEnter(initial, grp, statesToEnter, statesForDefaultEntry);
+ } else {
+ setError(QStateMachine::NoInitialStateError, grp);
+ return;
+ }
+ }
+ QList<QState*> ancs = properAncestors(s, root);
+ for (int i = 0; i < ancs.size(); ++i) {
+ QState *anc = ancs.at(i);
+ if (!anc->parentState())
+ continue;
+ statesToEnter.insert(anc);
+ if (isParallel(anc)) {
+ QList<QAbstractState*> lst = QStatePrivate::get(anc)->childStates();
+ for (int j = 0; j < lst.size(); ++j) {
+ QAbstractState *child = lst.at(j);
+ bool hasDescendantInList = false;
+ QSet<QAbstractState*>::const_iterator it;
+ for (it = statesToEnter.constBegin(); it != statesToEnter.constEnd(); ++it) {
+ if (isDescendantOf(*it, child)) {
+ hasDescendantInList = true;
+ break;
+ }
+ }
+ if (!hasDescendantInList)
+ addStatesToEnter(child, anc, statesToEnter, statesForDefaultEntry);
+ }
+ }
+ }
+ }
+}
+
+#ifndef QT_NO_PROPERTIES
+
+void QStateMachinePrivate::applyProperties(const QList<QAbstractTransition*> &transitionList,
+ const QList<QAbstractState*> &exitedStates,
+ const QList<QAbstractState*> &enteredStates)
+{
+#ifdef QT_NO_ANIMATION
+ Q_UNUSED(transitionList);
+ Q_UNUSED(exitedStates);
+#else
+ Q_Q(QStateMachine);
+#endif
+ // Process the property assignments of the entered states.
+ QHash<QAbstractState*, QList<QPropertyAssignment> > propertyAssignmentsForState;
+ QHash<RestorableId, QVariant> pendingRestorables = registeredRestorables;
+ for (int i = 0; i < enteredStates.size(); ++i) {
+ QState *s = toStandardState(enteredStates.at(i));
+ if (!s)
+ continue;
+
+ QList<QPropertyAssignment> assignments = QStatePrivate::get(s)->propertyAssignments;
+ for (int j = 0; j < assignments.size(); ++j) {
+ const QPropertyAssignment &assn = assignments.at(j);
+ if (globalRestorePolicy == QStateMachine::RestoreProperties) {
+ registerRestorable(assn.object, assn.propertyName);
+ }
+ pendingRestorables.remove(RestorableId(assn.object, assn.propertyName));
+ propertyAssignmentsForState[s].append(assn);
+ }
+
+ // Remove pending restorables for all parent states to avoid restoring properties
+ // before the state that assigned them is exited. If state does not explicitly
+ // assign a property which is assigned by the parent, it inherits the parent's assignment.
+ QState *parentState = s;
+ while ((parentState = parentState->parentState()) != 0) {
+ assignments = QStatePrivate::get(parentState)->propertyAssignments;
+ for (int j=0; j<assignments.size(); ++j) {
+ const QPropertyAssignment &assn = assignments.at(j);
+ int c = pendingRestorables.remove(RestorableId(assn.object, assn.propertyName));
+ if (c > 0)
+ propertyAssignmentsForState[s].append(assn);
+ }
+ }
+ }
+ if (!pendingRestorables.isEmpty()) {
+ QAbstractState *s;
+ if (!enteredStates.isEmpty())
+ s = enteredStates.last(); // ### handle if parallel
+ else
+ s = 0;
+ propertyAssignmentsForState[s] << restorablesToPropertyList(pendingRestorables);
+ }
+
+#ifndef QT_NO_ANIMATION
+ // Gracefully terminate playing animations for states that are exited.
+ for (int i = 0; i < exitedStates.size(); ++i) {
+ QAbstractState *s = exitedStates.at(i);
+ QList<QAbstractAnimation*> animations = animationsForState.take(s);
+ for (int j = 0; j < animations.size(); ++j) {
+ QAbstractAnimation *anim = animations.at(j);
+ QObject::disconnect(anim, SIGNAL(finished()), q, SLOT(_q_animationFinished()));
+ stateForAnimation.remove(anim);
+
+ // Stop the (top-level) animation.
+ // ### Stopping nested animation has weird behavior.
+ QAbstractAnimation *topLevelAnim = anim;
+ while (QAnimationGroup *group = topLevelAnim->group())
+ topLevelAnim = group;
+ topLevelAnim->stop();
+
+ if (resetAnimationEndValues.contains(anim)) {
+ qobject_cast<QVariantAnimation*>(anim)->setEndValue(QVariant()); // ### generalize
+ resetAnimationEndValues.remove(anim);
+ }
+ QPropertyAssignment assn = propertyForAnimation.take(anim);
+ Q_ASSERT(assn.object != 0);
+ // If there is no property assignment that sets this property,
+ // set the property to its target value.
+ bool found = false;
+ QHash<QAbstractState*, QList<QPropertyAssignment> >::const_iterator it;
+ for (it = propertyAssignmentsForState.constBegin(); it != propertyAssignmentsForState.constEnd(); ++it) {
+ const QList<QPropertyAssignment> &assignments = it.value();
+ for (int k = 0; k < assignments.size(); ++k) {
+ if ((assignments.at(k).object == assn.object)
+ && (assignments.at(k).propertyName == assn.propertyName)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ assn.object->setProperty(assn.propertyName, assn.value);
+ }
+ }
+ }
+
+ // Find the animations to use for the state change.
+ QList<QAbstractAnimation*> selectedAnimations;
+ if (animated) {
+ for (int i = 0; i < transitionList.size(); ++i) {
+ QAbstractTransition *transition = transitionList.at(i);
+
+ selectedAnimations << transition->animations();
+ selectedAnimations << defaultAnimationsForSource.values(transition->sourceState());
+
+ QList<QAbstractState *> targetStates = transition->targetStates();
+ for (int j=0; j<targetStates.size(); ++j)
+ selectedAnimations << defaultAnimationsForTarget.values(targetStates.at(j));
+ }
+ selectedAnimations << defaultAnimations;
+ }
+
+ // Initialize animations from property assignments.
+ for (int i = 0; i < selectedAnimations.size(); ++i) {
+ QAbstractAnimation *anim = selectedAnimations.at(i);
+ QHash<QAbstractState*, QList<QPropertyAssignment> >::iterator it;
+ for (it = propertyAssignmentsForState.begin(); it != propertyAssignmentsForState.end(); ) {
+ QList<QPropertyAssignment>::iterator it2;
+ QAbstractState *s = it.key();
+ QList<QPropertyAssignment> &assignments = it.value();
+ for (it2 = assignments.begin(); it2 != assignments.end(); ) {
+ QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > ret;
+ ret = initializeAnimation(anim, *it2);
+ QList<QAbstractAnimation*> handlers = ret.first;
+ if (!handlers.isEmpty()) {
+ for (int j = 0; j < handlers.size(); ++j) {
+ QAbstractAnimation *a = handlers.at(j);
+ propertyForAnimation.insert(a, *it2);
+ stateForAnimation.insert(a, s);
+ animationsForState[s].append(a);
+ // ### connect to just the top-level animation?
+ QObject::connect(a, SIGNAL(finished()), q, SLOT(_q_animationFinished()), Qt::UniqueConnection);
+ }
+ it2 = assignments.erase(it2);
+ } else {
+ ++it2;
+ }
+ for (int j = 0; j < ret.second.size(); ++j)
+ resetAnimationEndValues.insert(ret.second.at(j));
+ }
+ if (assignments.isEmpty())
+ it = propertyAssignmentsForState.erase(it);
+ else
+ ++it;
+ }
+ // We require that at least one animation is valid.
+ // ### generalize
+ QList<QVariantAnimation*> variantAnims = anim->findChildren<QVariantAnimation*>();
+ if (QVariantAnimation *va = qobject_cast<QVariantAnimation*>(anim))
+ variantAnims.append(va);
+
+ bool hasValidEndValue = false;
+ for (int j = 0; j < variantAnims.size(); ++j) {
+ if (variantAnims.at(j)->endValue().isValid()) {
+ hasValidEndValue = true;
+ break;
+ }
+ }
+
+ if (hasValidEndValue) {
+ if (anim->state() == QAbstractAnimation::Running) {
+ // The animation is still running. This can happen if the
+ // animation is a group, and one of its children just finished,
+ // and that caused a state to emit its propertiesAssigned() signal, and
+ // that triggered a transition in the machine.
+ // Just stop the animation so it is correctly restarted again.
+ anim->stop();
+ }
+ anim->start();
+ }
+ }
+#endif // !QT_NO_ANIMATION
+
+ // Immediately set the properties that are not animated.
+ {
+ QHash<QAbstractState*, QList<QPropertyAssignment> >::const_iterator it;
+ for (it = propertyAssignmentsForState.constBegin(); it != propertyAssignmentsForState.constEnd(); ++it) {
+ const QList<QPropertyAssignment> &assignments = it.value();
+ for (int i = 0; i < assignments.size(); ++i) {
+ const QPropertyAssignment &assn = assignments.at(i);
+ assn.object->setProperty(assn.propertyName, assn.value);
+ }
+ }
+ }
+
+ // Emit propertiesAssigned signal for entered states that have no animated properties.
+ for (int i = 0; i < enteredStates.size(); ++i) {
+ QState *s = toStandardState(enteredStates.at(i));
+ if (s
+#ifndef QT_NO_ANIMATION
+ && !animationsForState.contains(s)
+#endif
+ )
+ QStatePrivate::get(s)->emitPropertiesAssigned();
+ }
+}
+
+#endif // QT_NO_PROPERTIES
+
+bool QStateMachinePrivate::isFinal(const QAbstractState *s)
+{
+ return s && (QAbstractStatePrivate::get(s)->stateType == QAbstractStatePrivate::FinalState);
+}
+
+bool QStateMachinePrivate::isParallel(const QAbstractState *s)
+{
+ const QState *ss = toStandardState(s);
+ return ss && (QStatePrivate::get(ss)->childMode == QState::ParallelStates);
+}
+
+bool QStateMachinePrivate::isCompound(const QAbstractState *s) const
+{
+ const QState *group = toStandardState(s);
+ if (!group)
+ return false;
+ bool isMachine = QStatePrivate::get(group)->isMachine;
+ // 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())
+ || isMachine;
+}
+
+bool QStateMachinePrivate::isAtomic(const QAbstractState *s) const
+{
+ const QState *ss = toStandardState(s);
+ return (ss && QStatePrivate::get(ss)->childStates().isEmpty())
+ || isFinal(s)
+ // Treat the machine as atomic if it's a sub-state of this machine
+ || (ss && QStatePrivate::get(ss)->isMachine && (ss != rootState()));
+}
+
+
+bool QStateMachinePrivate::isDescendantOf(const QAbstractState *state, const QAbstractState *other)
+{
+ Q_ASSERT(state != 0);
+ for (QAbstractState *s = state->parentState(); s != 0; s = s->parentState()) {
+ if (s == other)
+ return true;
+ }
+ return false;
+}
+
+QList<QState*> QStateMachinePrivate::properAncestors(const QAbstractState *state, const QState *upperBound)
+{
+ Q_ASSERT(state != 0);
+ QList<QState*> result;
+ for (QState *s = state->parentState(); s && s != upperBound; s = s->parentState()) {
+ result.append(s);
+ }
+ return result;
+}
+
+QState *QStateMachinePrivate::toStandardState(QAbstractState *state)
+{
+ if (state && (QAbstractStatePrivate::get(state)->stateType == QAbstractStatePrivate::StandardState))
+ return static_cast<QState*>(state);
+ return 0;
+}
+
+const QState *QStateMachinePrivate::toStandardState(const QAbstractState *state)
+{
+ if (state && (QAbstractStatePrivate::get(state)->stateType == QAbstractStatePrivate::StandardState))
+ return static_cast<const QState*>(state);
+ return 0;
+}
+
+QFinalState *QStateMachinePrivate::toFinalState(QAbstractState *state)
+{
+ if (state && (QAbstractStatePrivate::get(state)->stateType == QAbstractStatePrivate::FinalState))
+ return static_cast<QFinalState*>(state);
+ return 0;
+}
+
+QHistoryState *QStateMachinePrivate::toHistoryState(QAbstractState *state)
+{
+ if (state && (QAbstractStatePrivate::get(state)->stateType == QAbstractStatePrivate::HistoryState))
+ return static_cast<QHistoryState*>(state);
+ return 0;
+}
+
+bool QStateMachinePrivate::isInFinalState(QAbstractState* s) const
+{
+ if (isCompound(s)) {
+ QState *grp = toStandardState(s);
+ QList<QAbstractState*> lst = QStatePrivate::get(grp)->childStates();
+ for (int i = 0; i < lst.size(); ++i) {
+ QAbstractState *cs = lst.at(i);
+ if (isFinal(cs) && configuration.contains(cs))
+ return true;
+ }
+ return false;
+ } else if (isParallel(s)) {
+ QState *grp = toStandardState(s);
+ QList<QAbstractState*> lst = QStatePrivate::get(grp)->childStates();
+ for (int i = 0; i < lst.size(); ++i) {
+ QAbstractState *cs = lst.at(i);
+ if (!isInFinalState(cs))
+ return false;
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+#ifndef QT_NO_PROPERTIES
+
+void QStateMachinePrivate::registerRestorable(QObject *object, const QByteArray &propertyName)
+{
+ RestorableId id(object, propertyName);
+ if (!registeredRestorables.contains(id))
+ registeredRestorables.insert(id, object->property(propertyName));
+}
+
+QList<QPropertyAssignment> QStateMachinePrivate::restorablesToPropertyList(const QHash<RestorableId, QVariant> &restorables) const
+{
+ QList<QPropertyAssignment> result;
+ QHash<RestorableId, QVariant>::const_iterator it;
+ for (it = restorables.constBegin(); it != restorables.constEnd(); ++it) {
+// qDebug() << "restorable:" << it.key().first << it.key().second << it.value();
+ result.append(QPropertyAssignment(it.key().first, it.key().second, it.value(), /*explicitlySet=*/false));
+ }
+ return result;
+}
+
+/*!
+ \internal
+ Returns true if the variable with the given \a id has been registered for restoration.
+*/
+bool QStateMachinePrivate::hasRestorable(QObject *object, const QByteArray &propertyName) const
+{
+ return registeredRestorables.contains(RestorableId(object, propertyName));
+}
+
+QVariant QStateMachinePrivate::restorableValue(QObject *object, const QByteArray &propertyName) const
+{
+ return registeredRestorables.value(RestorableId(object, propertyName), QVariant());
+}
+
+
+/*!
+ \internal
+ Unregisters the variable identified by \a id
+*/
+void QStateMachinePrivate::unregisterRestorable(QObject *object, const QByteArray &propertyName)
+{
+// qDebug() << "unregisterRestorable(" << object << propertyName << ')';
+ RestorableId id(object, propertyName);
+ 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
+ QAbstractState *errorState = 0;
+ if (context != 0) {
+ QState *s = toStandardState(context);
+ if (s != 0)
+ errorState = s->errorState();
+
+ if (errorState == 0)
+ errorState = findErrorState(context->parentState());
+ }
+
+ return errorState;
+}
+
+void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractState *currentContext)
+{
+ Q_Q(QStateMachine);
+
+ error = errorCode;
+ switch (errorCode) {
+ case QStateMachine::NoInitialStateError:
+ Q_ASSERT(currentContext != 0);
+
+ errorString = QStateMachine::tr("Missing initial state in compound state '%1'")
+ .arg(currentContext->objectName());
+
+ break;
+ case QStateMachine::NoDefaultStateInHistoryStateError:
+ Q_ASSERT(currentContext != 0);
+
+ errorString = QStateMachine::tr("Missing default state in history state '%1'")
+ .arg(currentContext->objectName());
+ break;
+
+ case QStateMachine::NoCommonAncestorForTransitionError:
+ Q_ASSERT(currentContext != 0);
+
+ errorString = QStateMachine::tr("No common ancestor for targets and source of transition from state '%1'")
+ .arg(currentContext->objectName());
+ break;
+ default:
+ errorString = QStateMachine::tr("Unknown error");
+ };
+
+ pendingErrorStates.clear();
+ pendingErrorStatesForDefaultEntry.clear();
+
+ QAbstractState *currentErrorState = findErrorState(currentContext);
+
+ // Avoid infinite loop if the error state itself has an error
+ if (currentContext == currentErrorState)
+ currentErrorState = 0;
+
+ Q_ASSERT(currentErrorState != rootState());
+
+ if (currentErrorState != 0) {
+ QState *lca = findLCA(QList<QAbstractState*>() << currentErrorState << currentContext);
+ addStatesToEnter(currentErrorState, lca, pendingErrorStates, pendingErrorStatesForDefaultEntry);
+ } else {
+ qWarning("Unrecoverable error detected in running state machine: %s",
+ qPrintable(errorString));
+ q->stop();
+ }
+}
+
+#ifndef QT_NO_ANIMATION
+
+QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> >
+QStateMachinePrivate::initializeAnimation(QAbstractAnimation *abstractAnimation,
+ const QPropertyAssignment &prop)
+{
+ QList<QAbstractAnimation*> handledAnimations;
+ QList<QAbstractAnimation*> localResetEndValues;
+ QAnimationGroup *group = qobject_cast<QAnimationGroup*>(abstractAnimation);
+ if (group) {
+ for (int i = 0; i < group->animationCount(); ++i) {
+ QAbstractAnimation *animationChild = group->animationAt(i);
+ QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> > ret;
+ ret = initializeAnimation(animationChild, prop);
+ handledAnimations << ret.first;
+ localResetEndValues << ret.second;
+ }
+ } else {
+ QPropertyAnimation *animation = qobject_cast<QPropertyAnimation *>(abstractAnimation);
+ if (animation != 0
+ && prop.object == animation->targetObject()
+ && prop.propertyName == animation->propertyName()) {
+
+ // Only change end value if it is undefined
+ if (!animation->endValue().isValid()) {
+ animation->setEndValue(prop.value);
+ localResetEndValues.append(animation);
+ }
+ handledAnimations.append(animation);
+ }
+ }
+ return qMakePair(handledAnimations, localResetEndValues);
+}
+
+void QStateMachinePrivate::_q_animationFinished()
+{
+ Q_Q(QStateMachine);
+ QAbstractAnimation *anim = qobject_cast<QAbstractAnimation*>(q->sender());
+ Q_ASSERT(anim != 0);
+ QObject::disconnect(anim, SIGNAL(finished()), q, SLOT(_q_animationFinished()));
+ if (resetAnimationEndValues.contains(anim)) {
+ qobject_cast<QVariantAnimation*>(anim)->setEndValue(QVariant()); // ### generalize
+ 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);
+ QHash<QAbstractState*, QList<QAbstractAnimation*> >::iterator it;
+ it = animationsForState.find(state);
+ Q_ASSERT(it != animationsForState.end());
+ QList<QAbstractAnimation*> &animations = it.value();
+ animations.removeOne(anim);
+ if (animations.isEmpty()) {
+ animationsForState.erase(it);
+ QStatePrivate::get(toStandardState(state))->emitPropertiesAssigned();
+ }
+}
+
+#endif // !QT_NO_ANIMATION
+
+namespace {
+
+class StartState : public QState
+{
+public:
+ StartState(QState *parent)
+ : QState(parent) {}
+protected:
+ void onEntry(QEvent *) {}
+ void onExit(QEvent *) {}
+};
+
+class InitialTransition : public QAbstractTransition
+{
+public:
+ InitialTransition(QAbstractState *target)
+ : QAbstractTransition()
+ { setTargetState(target); }
+protected:
+ virtual bool eventTest(QEvent *) { return true; }
+ virtual void onTransition(QEvent *) {}
+};
+
+} // namespace
+
+QState *QStateMachinePrivate::startState()
+{
+ Q_Q(QStateMachine);
+ if (_startState == 0)
+ _startState = new StartState(q);
+ return _startState;
+}
+
+void QStateMachinePrivate::removeStartState()
+{
+ delete _startState;
+ _startState = 0;
+}
+
+void QStateMachinePrivate::clearHistory()
+{
+ Q_Q(QStateMachine);
+ QList<QHistoryState*> historyStates = q->findChildren<QHistoryState*>();
+ for (int i = 0; i < historyStates.size(); ++i) {
+ QHistoryState *h = historyStates.at(i);
+ QHistoryStatePrivate::get(h)->configuration.clear();
+ }
+}
+
+void QStateMachinePrivate::_q_start()
+{
+ Q_Q(QStateMachine);
+ Q_ASSERT(state == Starting);
+ Q_ASSERT(rootState() != 0);
+ QAbstractState *initial = rootState()->initialState();
+ configuration.clear();
+ qDeleteAll(internalEventQueue);
+ internalEventQueue.clear();
+ qDeleteAll(externalEventQueue);
+ externalEventQueue.clear();
+ clearHistory();
+
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": starting";
+#endif
+ state = Running;
+ processingScheduled = true; // we call _q_process() below
+ emit q->started();
+
+ QState *start = startState();
+ Q_ASSERT(start != 0);
+
+ QList<QAbstractTransition*> transitions = QStatePrivate::get(start)->transitions();
+
+ // If a transition has already been added, then we skip this step, as the
+ // initial transition in that case has been overridden.
+ if (transitions.isEmpty()) {
+ QAbstractTransition *initialTransition = new InitialTransition(initial);
+ start->addTransition(initialTransition);
+ transitions.append(initialTransition);
+ }
+
+ QEvent nullEvent(QEvent::None);
+ executeTransitionContent(&nullEvent, transitions);
+ QList<QAbstractState*> enteredStates = enterStates(&nullEvent, transitions);
+#ifndef QT_NO_PROPERTIES
+ applyProperties(transitions, QList<QAbstractState*>() << start,
+ enteredStates);
+#endif
+ removeStartState();
+
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": initial configuration:" << configuration;
+#endif
+ _q_process();
+}
+
+void QStateMachinePrivate::_q_process()
+{
+ Q_Q(QStateMachine);
+ Q_ASSERT(state == Running);
+ Q_ASSERT(!processing);
+ processing = true;
+ processingScheduled = false;
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": starting the event processing loop";
+#endif
+ while (processing) {
+ if (stop) {
+ processing = false;
+ break;
+ }
+ QSet<QAbstractTransition*> enabledTransitions;
+ QEvent *e = new QEvent(QEvent::None);
+ enabledTransitions = selectTransitions(e);
+ if (enabledTransitions.isEmpty()) {
+ delete e;
+ e = 0;
+ }
+ if (enabledTransitions.isEmpty() && ((e = dequeueInternalEvent()) != 0)) {
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": dequeued internal event" << e << "of type" << e->type();
+#endif
+ enabledTransitions = selectTransitions(e);
+ if (enabledTransitions.isEmpty()) {
+ delete e;
+ e = 0;
+ }
+ }
+ if (enabledTransitions.isEmpty()) {
+ if ((e = dequeueExternalEvent()) != 0) {
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": dequeued external event" << e << "of type" << e->type();
+#endif
+ enabledTransitions = selectTransitions(e);
+ if (enabledTransitions.isEmpty()) {
+ delete e;
+ e = 0;
+ }
+ } else {
+ if (isInternalEventQueueEmpty()) {
+ processing = false;
+ stopProcessingReason = EventQueueEmpty;
+ }
+ }
+ }
+ if (!enabledTransitions.isEmpty()) {
+ q->beginMicrostep(e);
+ microstep(e, enabledTransitions.toList());
+ q->endMicrostep(e);
+ }
+#ifdef QSTATEMACHINE_DEBUG
+ else {
+ qDebug() << q << ": no transitions enabled";
+ }
+#endif
+ delete e;
+ }
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": finished the event processing loop";
+#endif
+ if (stop) {
+ stop = false;
+ stopProcessingReason = Stopped;
+ }
+
+ switch (stopProcessingReason) {
+ case EventQueueEmpty:
+ break;
+ case Finished:
+ state = NotRunning;
+ cancelAllDelayedEvents();
+ unregisterAllTransitions();
+ emit q->finished();
+ break;
+ case Stopped:
+ state = NotRunning;
+ cancelAllDelayedEvents();
+ unregisterAllTransitions();
+ emit q->stopped();
+ break;
+ }
+}
+
+void QStateMachinePrivate::postInternalEvent(QEvent *e)
+{
+ QMutexLocker locker(&internalEventMutex);
+ internalEventQueue.append(e);
+}
+
+void QStateMachinePrivate::postExternalEvent(QEvent *e)
+{
+ QMutexLocker locker(&externalEventMutex);
+ externalEventQueue.append(e);
+}
+
+QEvent *QStateMachinePrivate::dequeueInternalEvent()
+{
+ QMutexLocker locker(&internalEventMutex);
+ if (internalEventQueue.isEmpty())
+ return 0;
+ return internalEventQueue.takeFirst();
+}
+
+QEvent *QStateMachinePrivate::dequeueExternalEvent()
+{
+ QMutexLocker locker(&externalEventMutex);
+ if (externalEventQueue.isEmpty())
+ return 0;
+ return externalEventQueue.takeFirst();
+}
+
+bool QStateMachinePrivate::isInternalEventQueueEmpty()
+{
+ QMutexLocker locker(&internalEventMutex);
+ return internalEventQueue.isEmpty();
+}
+
+bool QStateMachinePrivate::isExternalEventQueueEmpty()
+{
+ QMutexLocker locker(&externalEventMutex);
+ return externalEventQueue.isEmpty();
+}
+
+void QStateMachinePrivate::processEvents(EventProcessingMode processingMode)
+{
+ Q_Q(QStateMachine);
+ if ((state != Running) || processing || processingScheduled)
+ return;
+ switch (processingMode) {
+ case DirectProcessing:
+ if (QThread::currentThread() == q->thread()) {
+ _q_process();
+ break;
+ } // fallthrough -- processing must be done in the machine thread
+ case QueuedProcessing:
+ processingScheduled = true;
+ QMetaObject::invokeMethod(q, "_q_process", Qt::QueuedConnection);
+ break;
+ }
+}
+
+void QStateMachinePrivate::cancelAllDelayedEvents()
+{
+ Q_Q(QStateMachine);
+ QMutexLocker locker(&delayedEventsMutex);
+ QHash<int, QEvent*>::const_iterator it;
+ for (it = delayedEvents.constBegin(); it != delayedEvents.constEnd(); ++it) {
+ int id = it.key();
+ QEvent *e = it.value();
+ q->killTimer(id);
+ delete e;
+ }
+ delayedEvents.clear();
+}
+
+namespace _QStateMachine_Internal{
+
+class GoToStateTransition : public QAbstractTransition
+{
+public:
+ GoToStateTransition(QAbstractState *target)
+ : QAbstractTransition()
+ { setTargetState(target); }
+protected:
+ void onTransition(QEvent *) { deleteLater(); }
+ bool eventTest(QEvent *) { return true; }
+};
+
+} // namespace
+// mingw compiler tries to export QObject::findChild<GoToStateTransition>(),
+// which doesn't work if its in an anonymous namespace.
+using namespace _QStateMachine_Internal;
+/*!
+ \internal
+
+ Causes this state machine to unconditionally transition to the given
+ \a targetState.
+
+ Provides a backdoor for using the state machine "imperatively"; i.e. rather
+ than defining explicit transitions, you drive the machine's execution by
+ calling this function. It breaks the whole integrity of the
+ transition-driven model, but is provided for pragmatic reasons.
+*/
+void QStateMachinePrivate::goToState(QAbstractState *targetState)
+{
+ if (!targetState) {
+ qWarning("QStateMachine::goToState(): cannot go to null state");
+ return;
+ }
+
+ if (configuration.contains(targetState))
+ return;
+
+ QState *sourceState = 0;
+ if (state == Running) {
+ QSet<QAbstractState*>::const_iterator it;
+ for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
+ sourceState = toStandardState(*it);
+ if (sourceState != 0)
+ break;
+ }
+ } else {
+ sourceState = startState();
+ }
+
+ Q_ASSERT(sourceState != 0);
+ // Reuse previous GoToStateTransition in case of several calls to
+ // goToState() in a row.
+ GoToStateTransition *trans = sourceState->findChild<GoToStateTransition*>();
+ if (!trans) {
+ trans = new GoToStateTransition(targetState);
+ sourceState->addTransition(trans);
+ } else {
+ trans->setTargetState(targetState);
+ }
+
+ processEvents(QueuedProcessing);
+}
+
+void QStateMachinePrivate::registerTransitions(QAbstractState *state)
+{
+ QState *group = toStandardState(state);
+ if (!group)
+ return;
+ QList<QAbstractTransition*> transitions = QStatePrivate::get(group)->transitions();
+ for (int i = 0; i < transitions.size(); ++i) {
+ QAbstractTransition *t = transitions.at(i);
+ if (QSignalTransition *st = qobject_cast<QSignalTransition*>(t)) {
+ registerSignalTransition(st);
+ }
+#ifndef QT_NO_STATEMACHINE_EVENTFILTER
+ else if (QEventTransition *oet = qobject_cast<QEventTransition*>(t)) {
+ registerEventTransition(oet);
+ }
+#endif
+ }
+}
+
+void QStateMachinePrivate::unregisterTransition(QAbstractTransition *transition)
+{
+ if (QSignalTransition *st = qobject_cast<QSignalTransition*>(transition)) {
+ unregisterSignalTransition(st);
+ }
+#ifndef QT_NO_STATEMACHINE_EVENTFILTER
+ else if (QEventTransition *oet = qobject_cast<QEventTransition*>(transition)) {
+ unregisterEventTransition(oet);
+ }
+#endif
+}
+
+void QStateMachinePrivate::registerSignalTransition(QSignalTransition *transition)
+{
+ Q_Q(QStateMachine);
+ if (QSignalTransitionPrivate::get(transition)->signalIndex != -1)
+ return; // already registered
+ QObject *sender = QSignalTransitionPrivate::get(transition)->sender;
+ if (!sender)
+ return;
+ QByteArray signal = QSignalTransitionPrivate::get(transition)->signal;
+ if (signal.startsWith('0'+QSIGNAL_CODE))
+ signal.remove(0, 1);
+ const QMetaObject *meta = sender->metaObject();
+ int signalIndex = meta->indexOfSignal(signal);
+ int originalSignalIndex = signalIndex;
+ if (signalIndex == -1) {
+ signalIndex = meta->indexOfSignal(QMetaObject::normalizedSignature(signal));
+ if (signalIndex == -1) {
+ qWarning("QSignalTransition: no such signal: %s::%s",
+ meta->className(), signal.constData());
+ return;
+ }
+ }
+ // The signal index we actually want to connect to is the one
+ // that is going to be sent, i.e. the non-cloned original index.
+ while (meta->method(signalIndex).attributes() & QMetaMethod::Cloned)
+ --signalIndex;
+
+ QVector<int> &connectedSignalIndexes = connections[sender];
+ if (connectedSignalIndexes.size() <= signalIndex)
+ connectedSignalIndexes.resize(signalIndex+1);
+ if (connectedSignalIndexes.at(signalIndex) == 0) {
+ if (!signalEventGenerator)
+ signalEventGenerator = new QSignalEventGenerator(q);
+ bool ok = QMetaObject::connect(sender, signalIndex, signalEventGenerator,
+ signalEventGenerator->metaObject()->methodOffset());
+ if (!ok) {
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": FAILED to add signal transition from" << transition->sourceState()
+ << ": ( sender =" << sender << ", signal =" << signal
+ << ", targets =" << transition->targetStates() << ')';
+#endif
+ return;
+ }
+ }
+ ++connectedSignalIndexes[signalIndex];
+ QSignalTransitionPrivate::get(transition)->signalIndex = signalIndex;
+ QSignalTransitionPrivate::get(transition)->originalSignalIndex = originalSignalIndex;
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": added signal transition from" << transition->sourceState()
+ << ": ( sender =" << sender << ", signal =" << signal
+ << ", targets =" << transition->targetStates() << ')';
+#endif
+}
+
+void QStateMachinePrivate::unregisterSignalTransition(QSignalTransition *transition)
+{
+ int signalIndex = QSignalTransitionPrivate::get(transition)->signalIndex;
+ if (signalIndex == -1)
+ return; // not registered
+ QSignalTransitionPrivate::get(transition)->signalIndex = -1;
+ const QObject *sender = QSignalTransitionPrivate::get(transition)->sender;
+ QVector<int> &connectedSignalIndexes = connections[sender];
+ Q_ASSERT(connectedSignalIndexes.size() > signalIndex);
+ Q_ASSERT(connectedSignalIndexes.at(signalIndex) != 0);
+ if (--connectedSignalIndexes[signalIndex] == 0) {
+ Q_ASSERT(signalEventGenerator != 0);
+ QMetaObject::disconnect(sender, signalIndex, signalEventGenerator,
+ signalEventGenerator->metaObject()->methodOffset());
+ int sum = 0;
+ for (int i = 0; i < connectedSignalIndexes.size(); ++i)
+ sum += connectedSignalIndexes.at(i);
+ if (sum == 0)
+ connections.remove(sender);
+ }
+}
+
+void QStateMachinePrivate::unregisterAllTransitions()
+{
+ Q_Q(QStateMachine);
+ {
+ QList<QSignalTransition*> transitions = rootState()->findChildren<QSignalTransition*>();
+ for (int i = 0; i < transitions.size(); ++i) {
+ QSignalTransition *t = transitions.at(i);
+ if (t->machine() == q)
+ unregisterSignalTransition(t);
+ }
+ }
+ {
+ QList<QEventTransition*> transitions = rootState()->findChildren<QEventTransition*>();
+ for (int i = 0; i < transitions.size(); ++i) {
+ QEventTransition *t = transitions.at(i);
+ if (t->machine() == q)
+ unregisterEventTransition(t);
+ }
+ }
+}
+
+#ifndef QT_NO_STATEMACHINE_EVENTFILTER
+void QStateMachinePrivate::registerEventTransition(QEventTransition *transition)
+{
+ Q_Q(QStateMachine);
+ if (QEventTransitionPrivate::get(transition)->registered)
+ return;
+ if (transition->eventType() >= QEvent::User) {
+ qWarning("QObject event transitions are not supported for custom types");
+ return;
+ }
+ QObject *object = QEventTransitionPrivate::get(transition)->object;
+ if (!object)
+ return;
+ QObjectPrivate *od = QObjectPrivate::get(object);
+ if (!od->eventFilters.contains(q))
+ object->installEventFilter(q);
+ ++qobjectEvents[object][transition->eventType()];
+ QEventTransitionPrivate::get(transition)->registered = true;
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": added event transition from" << transition->sourceState()
+ << ": ( object =" << object << ", event =" << transition->eventType()
+ << ", targets =" << transition->targetStates() << ')';
+#endif
+}
+
+void QStateMachinePrivate::unregisterEventTransition(QEventTransition *transition)
+{
+ Q_Q(QStateMachine);
+ if (!QEventTransitionPrivate::get(transition)->registered)
+ return;
+ QObject *object = QEventTransitionPrivate::get(transition)->object;
+ QHash<QEvent::Type, int> &events = qobjectEvents[object];
+ Q_ASSERT(events.value(transition->eventType()) > 0);
+ if (--events[transition->eventType()] == 0) {
+ events.remove(transition->eventType());
+ int sum = 0;
+ QHash<QEvent::Type, int>::const_iterator it;
+ for (it = events.constBegin(); it != events.constEnd(); ++it)
+ sum += it.value();
+ if (sum == 0) {
+ qobjectEvents.remove(object);
+ object->removeEventFilter(q);
+ }
+ }
+ QEventTransitionPrivate::get(transition)->registered = false;
+}
+
+void QStateMachinePrivate::handleFilteredEvent(QObject *watched, QEvent *event)
+{
+ if (qobjectEvents.value(watched).contains(event->type())) {
+ postInternalEvent(new QStateMachine::WrappedEvent(watched, handler->cloneEvent(event)));
+ processEvents(DirectProcessing);
+ }
+}
+#endif
+
+void QStateMachinePrivate::handleTransitionSignal(QObject *sender, int signalIndex,
+ void **argv)
+{
+ Q_ASSERT(connections[sender].at(signalIndex) != 0);
+ const QMetaObject *meta = sender->metaObject();
+ QMetaMethod method = meta->method(signalIndex);
+ QList<QByteArray> parameterTypes = method.parameterTypes();
+ int argc = parameterTypes.count();
+ QList<QVariant> vargs;
+ for (int i = 0; i < argc; ++i) {
+ int type = QMetaType::type(parameterTypes.at(i));
+ vargs.append(QVariant(type, argv[i+1]));
+ }
+
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ": sending signal event ( sender =" << sender
+ << ", signal =" << sender->metaObject()->method(signalIndex).signature() << ')';
+#endif
+ postInternalEvent(new QStateMachine::SignalEvent(sender, signalIndex, vargs));
+ processEvents(DirectProcessing);
+}
+
+/*!
+ Constructs a new state machine with the given \a parent.
+*/
+QStateMachine::QStateMachine(QObject *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)
+ : QState(dd, /*parentState=*/0)
+{
+ setParent(parent);
+}
+
+/*!
+ Destroys this state machine.
+*/
+QStateMachine::~QStateMachine()
+{
+}
+
+/*!
+ \enum QStateMachine::EventPriority
+
+ This enum type specifies the priority of an event posted to the state
+ machine using postEvent().
+
+ Events of high priority are processed before events of normal priority.
+
+ \value NormalPriority The event has normal priority.
+ \value HighPriority The event has high priority.
+*/
+
+/*! \enum QStateMachine::Error
+
+ This enum type defines errors that can occur in the state machine at run time. When the state
+ machine encounters an unrecoverable error at run time, it will set the error code returned
+ by error(), the error message returned by errorString(), and enter an error state based on
+ the context of the error.
+
+ \value NoError No error has occurred.
+ \value NoInitialStateError The machine has entered a QState with children which does not have an
+ initial state set. The context of this error is the state which is missing an initial
+ state.
+ \value NoDefaultStateInHistoryStateError The machine has entered a QHistoryState which does not have
+ a default state set. The context of this error is the QHistoryState which is missing a
+ default state.
+ \value NoCommonAncestorForTransitionError The machine has selected a transition whose source
+ and targets are not part of the same tree of states, and thus are not part of the same
+ state machine. Commonly, this could mean that one of the states has not been given
+ any parent or added to any machine. The context of this error is the source state of
+ the transition.
+
+ \sa setErrorState()
+*/
+
+/*!
+ \enum QStateMachine::RestorePolicy
+
+ This enum specifies the restore policy type. The restore policy
+ takes effect when the machine enters a state which sets one or more
+ properties. If the restore policy is set to RestoreProperties,
+ the state machine will save the original value of the property before the
+ new value is set.
+
+ Later, when the machine either enters a state which does not set
+ a value for the given property, the property will automatically be restored
+ to its initial value.
+
+ Only one initial value will be saved for any given property. If a value for a property has
+ already been saved by the state machine, it will not be overwritten until the property has been
+ successfully restored.
+
+ \value DontRestoreProperties The state machine should not save the initial values of properties
+ and restore them later.
+ \value RestoreProperties The state machine should save the initial values of properties
+ and restore them later.
+
+ \sa QStateMachine::globalRestorePolicy QState::assignProperty()
+*/
+
+
+/*!
+ Returns the error code of the last error that occurred in the state machine.
+*/
+QStateMachine::Error QStateMachine::error() const
+{
+ Q_D(const QStateMachine);
+ return d->error;
+}
+
+/*!
+ Returns the error string of the last error that occurred in the state machine.
+*/
+QString QStateMachine::errorString() const
+{
+ Q_D(const QStateMachine);
+ return d->errorString;
+}
+
+/*!
+ Clears the error string and error code of the state machine.
+*/
+void QStateMachine::clearError()
+{
+ Q_D(QStateMachine);
+ d->errorString.clear();
+ d->error = NoError;
+}
+
+/*!
+ Returns the restore policy of the state machine.
+
+ \sa setGlobalRestorePolicy()
+*/
+QStateMachine::RestorePolicy QStateMachine::globalRestorePolicy() const
+{
+ Q_D(const QStateMachine);
+ return d->globalRestorePolicy;
+}
+
+/*!
+ Sets the restore policy of the state machine to \a restorePolicy. The default
+ restore policy is QAbstractState::DontRestoreProperties.
+
+ \sa globalRestorePolicy()
+*/
+void QStateMachine::setGlobalRestorePolicy(QStateMachine::RestorePolicy restorePolicy)
+{
+ Q_D(QStateMachine);
+ d->globalRestorePolicy = restorePolicy;
+}
+
+/*!
+ Adds the given \a state to this state machine. The state becomes a top-level
+ 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(), setInitialState()
+*/
+void QStateMachine::addState(QAbstractState *state)
+{
+ if (!state) {
+ qWarning("QStateMachine::addState: cannot add null state");
+ return;
+ }
+ if (QAbstractStatePrivate::get(state)->machine() == this) {
+ qWarning("QStateMachine::addState: state has already been added to this machine");
+ return;
+ }
+ state->setParent(this);
+}
+
+/*!
+ Removes the given \a state from this state machine. The state machine
+ releases ownership of the state.
+
+ \sa addState()
+*/
+void QStateMachine::removeState(QAbstractState *state)
+{
+ if (!state) {
+ qWarning("QStateMachine::removeState: cannot remove null state");
+ return;
+ }
+ if (QAbstractStatePrivate::get(state)->machine() != this) {
+ qWarning("QStateMachine::removeState: state %p's machine (%p)"
+ " is different from this machine (%p)",
+ state, QAbstractStatePrivate::get(state)->machine(), this);
+ return;
+ }
+ state->setParent(0);
+}
+
+/*!
+ Returns whether this state machine is running.
+
+ start(), stop()
+*/
+bool QStateMachine::isRunning() const
+{
+ Q_D(const QStateMachine);
+ return (d->state == QStateMachinePrivate::Running);
+}
+
+/*!
+ Starts this state machine. The machine will reset its configuration and
+ transition to the initial state. When a final top-level state (QFinalState)
+ is entered, the machine will emit the finished() signal.
+
+ \note A state machine will not run without a running event loop, such as
+ the main application event loop started with QCoreApplication::exec() or
+ QApplication::exec().
+
+ \sa started(), finished(), stop(), initialState()
+*/
+void QStateMachine::start()
+{
+ Q_D(QStateMachine);
+
+ if (initialState() == 0) {
+ qWarning("QStateMachine::start: No initial state set for machine. Refusing to start.");
+ return;
+ }
+
+ switch (d->state) {
+ case QStateMachinePrivate::NotRunning:
+ d->state = QStateMachinePrivate::Starting;
+ QMetaObject::invokeMethod(this, "_q_start", Qt::QueuedConnection);
+ break;
+ case QStateMachinePrivate::Starting:
+ break;
+ case QStateMachinePrivate::Running:
+ qWarning("QStateMachine::start(): already running");
+ break;
+ }
+}
+
+/*!
+ Stops this state machine. The state machine will stop processing events and
+ then emit the stopped() signal.
+
+ \sa stopped(), start()
+*/
+void QStateMachine::stop()
+{
+ Q_D(QStateMachine);
+ switch (d->state) {
+ case QStateMachinePrivate::NotRunning:
+ break;
+ case QStateMachinePrivate::Starting:
+ // the machine will exit as soon as it enters the event processing loop
+ d->stop = true;
+ break;
+ case QStateMachinePrivate::Running:
+ d->stop = true;
+ d->processEvents(QStateMachinePrivate::QueuedProcessing);
+ break;
+ }
+}
+
+/*!
+ \threadsafe
+
+ Posts the given \a event of the given \a priority for processing by this
+ state machine.
+
+ This function returns immediately. The event is added to the state machine's
+ event queue. Events are processed in the order posted. The state machine
+ takes ownership of the event and deletes it once it has been processed.
+
+ You can only post events when the state machine is running.
+
+ \sa postDelayedEvent()
+*/
+void QStateMachine::postEvent(QEvent *event, EventPriority priority)
+{
+ Q_D(QStateMachine);
+ if (d->state != QStateMachinePrivate::Running) {
+ qWarning("QStateMachine::postEvent: cannot post event when the state machine is not running");
+ return;
+ }
+ if (!event) {
+ qWarning("QStateMachine::postEvent: cannot post null event");
+ return;
+ }
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << this << ": posting event" << event;
+#endif
+ switch (priority) {
+ case NormalPriority:
+ d->postExternalEvent(event);
+ break;
+ case HighPriority:
+ d->postInternalEvent(event);
+ break;
+ }
+ d->processEvents(QStateMachinePrivate::QueuedProcessing);
+}
+
+/*!
+ \threadsafe
+
+ Posts the given \a event for processing by this state machine, with the
+ given \a delay in milliseconds. Returns an identifier associated with the
+ delayed event, or -1 if the event could not be posted.
+
+ This function returns immediately. When the delay has expired, the event
+ will be added to the state machine's event queue for processing. The state
+ machine takes ownership of the event and deletes it once it has been
+ processed.
+
+ You can only post events when the state machine is running.
+
+ \sa cancelDelayedEvent(), postEvent()
+*/
+int QStateMachine::postDelayedEvent(QEvent *event, int delay)
+{
+ Q_D(QStateMachine);
+ if (d->state != QStateMachinePrivate::Running) {
+ qWarning("QStateMachine::postDelayedEvent: cannot post event when the state machine is not running");
+ return -1;
+ }
+ if (!event) {
+ qWarning("QStateMachine::postDelayedEvent: cannot post null event");
+ return -1;
+ }
+ if (delay < 0) {
+ qWarning("QStateMachine::postDelayedEvent: delay cannot be negative");
+ return -1;
+ }
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << this << ": posting event" << event << "with delay" << delay;
+#endif
+ QMutexLocker locker(&d->delayedEventsMutex);
+ int tid = startTimer(delay);
+ d->delayedEvents[tid] = event;
+ return tid;
+}
+
+/*!
+ \threadsafe
+
+ Cancels the delayed event identified by the given \a id. The id should be a
+ value returned by a call to postDelayedEvent(). Returns true if the event
+ was successfully cancelled, otherwise returns false.
+
+ \sa postDelayedEvent()
+*/
+bool QStateMachine::cancelDelayedEvent(int id)
+{
+ Q_D(QStateMachine);
+ if (d->state != QStateMachinePrivate::Running) {
+ qWarning("QStateMachine::cancelDelayedEvent: the machine is not running");
+ return false;
+ }
+ QMutexLocker locker(&d->delayedEventsMutex);
+ QEvent *e = d->delayedEvents.take(id);
+ if (!e)
+ return false;
+ killTimer(id);
+ delete e;
+ return true;
+}
+
+/*!
+ 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 machine itself is not an explicit member of the
+ configuration.
+*/
+QSet<QAbstractState*> QStateMachine::configuration() const
+{
+ Q_D(const QStateMachine);
+ return d->configuration;
+}
+
+/*!
+ \fn QStateMachine::started()
+
+ This signal is emitted when the state machine has entered its initial state
+ (QStateMachine::initialState).
+
+ \sa QStateMachine::finished(), QStateMachine::start()
+*/
+
+/*!
+ \fn QStateMachine::stopped()
+
+ This signal is emitted when the state machine has stopped.
+
+ \sa QStateMachine::stop(), QStateMachine::finished()
+*/
+
+/*!
+ \reimp
+*/
+bool QStateMachine::event(QEvent *e)
+{
+ Q_D(QStateMachine);
+ if (e->type() == QEvent::Timer) {
+ QTimerEvent *te = static_cast<QTimerEvent*>(e);
+ int tid = te->timerId();
+ if (d->state != QStateMachinePrivate::Running) {
+ // This event has been cancelled already
+ QMutexLocker locker(&d->delayedEventsMutex);
+ Q_ASSERT(!d->delayedEvents.contains(tid));
+ return true;
+ }
+ d->delayedEventsMutex.lock();
+ QEvent *ee = d->delayedEvents.take(tid);
+ if (ee != 0) {
+ killTimer(tid);
+ d->delayedEventsMutex.unlock();
+ d->postExternalEvent(ee);
+ d->processEvents(QStateMachinePrivate::DirectProcessing);
+ return true;
+ } else {
+ d->delayedEventsMutex.unlock();
+ }
+ }
+ return QState::event(e);
+}
+
+#ifndef QT_NO_STATEMACHINE_EVENTFILTER
+/*!
+ \reimp
+*/
+bool QStateMachine::eventFilter(QObject *watched, QEvent *event)
+{
+ Q_D(QStateMachine);
+ d->handleFilteredEvent(watched, event);
+ return false;
+}
+#endif
+
+/*!
+ \internal
+
+ This function is called when the state machine is about to select
+ transitions based on the given \a event.
+
+ The default implementation does nothing.
+*/
+void QStateMachine::beginSelectTransitions(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \internal
+
+ This function is called when the state machine has finished selecting
+ transitions based on the given \a event.
+
+ The default implementation does nothing.
+*/
+void QStateMachine::endSelectTransitions(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \internal
+
+ This function is called when the state machine is about to do a microstep.
+
+ The default implementation does nothing.
+*/
+void QStateMachine::beginMicrostep(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \internal
+
+ This function is called when the state machine has finished doing a
+ microstep.
+
+ The default implementation does nothing.
+*/
+void QStateMachine::endMicrostep(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+ This function will call start() to start the state machine.
+*/
+void QStateMachine::onEntry(QEvent *event)
+{
+ start();
+ QState::onEntry(event);
+}
+
+/*!
+ \reimp
+ This function will call stop() to stop the state machine and
+ subsequently emit the stopped() signal.
+*/
+void QStateMachine::onExit(QEvent *event)
+{
+ stop();
+ QState::onExit(event);
+}
+
+#ifndef QT_NO_ANIMATION
+
+/*!
+ Returns whether animations are enabled for this state machine.
+*/
+bool QStateMachine::isAnimated() const
+{
+ Q_D(const QStateMachine);
+ return d->animated;
+}
+
+/*!
+ Sets whether animations are \a enabled for this state machine.
+*/
+void QStateMachine::setAnimated(bool enabled)
+{
+ Q_D(QStateMachine);
+ d->animated = enabled;
+}
+
+/*!
+ Adds a default \a animation to be considered for any transition.
+*/
+void QStateMachine::addDefaultAnimation(QAbstractAnimation *animation)
+{
+ Q_D(QStateMachine);
+ d->defaultAnimations.append(animation);
+}
+
+/*!
+ Returns the list of default animations that will be considered for any transition.
+*/
+QList<QAbstractAnimation*> QStateMachine::defaultAnimations() const
+{
+ Q_D(const QStateMachine);
+ return d->defaultAnimations;
+}
+
+/*!
+ Removes \a animation from the list of default animations.
+*/
+void QStateMachine::removeDefaultAnimation(QAbstractAnimation *animation)
+{
+ Q_D(QStateMachine);
+ d->defaultAnimations.removeAll(animation);
+}
+
+#endif // QT_NO_ANIMATION
+
+
+static const uint qt_meta_data_QSignalEventGenerator[] = {
+
+ // content:
+ 2, // revision
+ 0, // classname
+ 0, 0, // classinfo
+ 1, 12, // methods
+ 0, 0, // properties
+ 0, 0, // enums/sets
+ 0, 0, // constructors
+
+ // slots: signature, parameters, type, tag, flags
+ 23, 22, 22, 22, 0x0a,
+
+ 0 // eod
+};
+
+static const char qt_meta_stringdata_QSignalEventGenerator[] = {
+ "QSignalEventGenerator\0\0execute()\0"
+};
+
+const QMetaObject QSignalEventGenerator::staticMetaObject = {
+ { &QObject::staticMetaObject, qt_meta_stringdata_QSignalEventGenerator,
+ qt_meta_data_QSignalEventGenerator, 0 }
+};
+
+const QMetaObject *QSignalEventGenerator::metaObject() const
+{
+ return &staticMetaObject;
+}
+
+void *QSignalEventGenerator::qt_metacast(const char *_clname)
+{
+ if (!_clname) return 0;
+ if (!strcmp(_clname, qt_meta_stringdata_QSignalEventGenerator))
+ return static_cast<void*>(const_cast< QSignalEventGenerator*>(this));
+ return QObject::qt_metacast(_clname);
+}
+
+int QSignalEventGenerator::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
+{
+ _id = QObject::qt_metacall(_c, _id, _a);
+ if (_id < 0)
+ return _id;
+ if (_c == QMetaObject::InvokeMetaMethod) {
+ switch (_id) {
+ case 0: {
+// ### in Qt 4.6 we can use QObject::senderSignalIndex()
+ QObjectPrivate *d = static_cast<QObjectPrivate *>(d_ptr.data());
+ int signalIndex = -1;
+ QObject *sender = this->sender();
+ if (sender && d->currentSender)
+ signalIndex = d->currentSender->signal;
+
+ Q_ASSERT(signalIndex != -1);
+ QStateMachine *machine = qobject_cast<QStateMachine*>(parent());
+ QStateMachinePrivate::get(machine)->handleTransitionSignal(sender, signalIndex, _a);
+ break;
+ }
+ default: ;
+ }
+ _id -= 1;
+ }
+ return _id;
+}
+
+QSignalEventGenerator::QSignalEventGenerator(QStateMachine *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ \class QStateMachine::SignalEvent
+
+ \brief The SignalEvent class represents a Qt signal event.
+
+ \since 4.6
+ \ingroup statemachine
+
+ A signal event is generated by a QStateMachine in response to a Qt
+ signal. The QSignalTransition class provides a transition associated with a
+ signal event. QStateMachine::SignalEvent is part of \l{The State Machine Framework}.
+
+ The sender() function returns the object that generated the signal. The
+ signalIndex() function returns the index of the signal. The arguments()
+ function returns the arguments of the signal.
+
+ \sa QSignalTransition
+*/
+
+/*!
+ \internal
+
+ Constructs a new SignalEvent object with the given \a sender, \a
+ signalIndex and \a arguments.
+*/
+QStateMachine::SignalEvent::SignalEvent(QObject *sender, int signalIndex,
+ const QList<QVariant> &arguments)
+ : QEvent(QEvent::StateMachineSignal), m_sender(sender),
+ m_signalIndex(signalIndex), m_arguments(arguments)
+{
+}
+
+/*!
+ Destroys this SignalEvent.
+*/
+QStateMachine::SignalEvent::~SignalEvent()
+{
+}
+
+/*!
+ \fn QStateMachine::SignalEvent::sender() const
+
+ Returns the object that emitted the signal.
+
+ \sa QObject::sender()
+*/
+
+/*!
+ \fn QStateMachine::SignalEvent::signalIndex() const
+
+ Returns the index of the signal.
+
+ \sa QMetaObject::indexOfSignal(), QMetaObject::method()
+*/
+
+/*!
+ \fn QStateMachine::SignalEvent::arguments() const
+
+ Returns the arguments of the signal.
+*/
+
+
+/*!
+ \class QStateMachine::WrappedEvent
+
+ \brief The WrappedEvent class holds a clone of an event associated with a QObject.
+
+ \since 4.6
+ \ingroup statemachine
+
+ A wrapped event is generated by a QStateMachine in response to a Qt
+ event. The QEventTransition class provides a transition associated with a
+ such an event. QStateMachine::WrappedEvent is part of \l{The State Machine
+ Framework}.
+
+ The object() function returns the object that generated the event. The
+ event() function returns a clone of the original event.
+
+ \sa QEventTransition
+*/
+
+/*!
+ \internal
+
+ Constructs a new WrappedEvent object with the given \a object
+ and \a event.
+
+ The WrappedEvent object takes ownership of \a event.
+*/
+QStateMachine::WrappedEvent::WrappedEvent(QObject *object, QEvent *event)
+ : QEvent(QEvent::StateMachineWrapped), m_object(object), m_event(event)
+{
+}
+
+/*!
+ Destroys this WrappedEvent.
+*/
+QStateMachine::WrappedEvent::~WrappedEvent()
+{
+ delete m_event;
+}
+
+/*!
+ \fn QStateMachine::WrappedEvent::object() const
+
+ Returns the object that the event is associated with.
+*/
+
+/*!
+ \fn QStateMachine::WrappedEvent::event() const
+
+ Returns a clone of the original event.
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qstatemachine.cpp"
+
+#endif //QT_NO_STATEMACHINE
diff --git a/src/corelib/statemachine/qstatemachine.h b/src/corelib/statemachine/qstatemachine.h
new file mode 100644
index 0000000000..8c4fb025e7
--- /dev/null
+++ b/src/corelib/statemachine/qstatemachine.h
@@ -0,0 +1,196 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTATEMACHINE_H
+#define QSTATEMACHINE_H
+
+#include <QtCore/qstate.h>
+
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qset.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Core)
+
+#ifndef QT_NO_STATEMACHINE
+
+class QStateMachinePrivate;
+class QAbstractAnimation;
+class Q_CORE_EXPORT QStateMachine : public QState
+{
+ Q_OBJECT
+ Q_PROPERTY(QString errorString READ errorString)
+ Q_PROPERTY(RestorePolicy globalRestorePolicy READ globalRestorePolicy WRITE setGlobalRestorePolicy)
+ Q_ENUMS(RestorePolicy)
+#ifndef QT_NO_ANIMATION
+ Q_PROPERTY(bool animated READ isAnimated WRITE setAnimated)
+#endif
+public:
+ class Q_CORE_EXPORT SignalEvent : public QEvent
+ {
+ public:
+ SignalEvent(QObject *sender, int signalIndex,
+ const QList<QVariant> &arguments);
+ ~SignalEvent();
+
+ inline QObject *sender() const { return m_sender; }
+ inline int signalIndex() const { return m_signalIndex; }
+ inline QList<QVariant> arguments() const { return m_arguments; }
+
+ private:
+ QObject *m_sender;
+ int m_signalIndex;
+ QList<QVariant> m_arguments;
+
+ friend class QSignalTransitionPrivate;
+ };
+
+ class Q_CORE_EXPORT WrappedEvent : public QEvent
+ {
+ public:
+ WrappedEvent(QObject *object, QEvent *event);
+ ~WrappedEvent();
+
+ inline QObject *object() const { return m_object; }
+ inline QEvent *event() const { return m_event; }
+
+ private:
+ QObject *m_object;
+ QEvent *m_event;
+ };
+
+ enum EventPriority {
+ NormalPriority,
+ HighPriority
+ };
+
+ enum RestorePolicy {
+ DontRestoreProperties,
+ RestoreProperties
+ };
+
+ enum Error {
+ NoError,
+ NoInitialStateError,
+ NoDefaultStateInHistoryStateError,
+ NoCommonAncestorForTransitionError
+ };
+
+ QStateMachine(QObject *parent = 0);
+ ~QStateMachine();
+
+ void addState(QAbstractState *state);
+ void removeState(QAbstractState *state);
+
+ Error error() const;
+ QString errorString() const;
+ void clearError();
+
+ bool isRunning() const;
+
+#ifndef QT_NO_ANIMATION
+ bool isAnimated() const;
+ void setAnimated(bool enabled);
+
+ void addDefaultAnimation(QAbstractAnimation *animation);
+ QList<QAbstractAnimation *> defaultAnimations() const;
+ void removeDefaultAnimation(QAbstractAnimation *animation);
+#endif // QT_NO_ANIMATION
+
+ QStateMachine::RestorePolicy globalRestorePolicy() const;
+ void setGlobalRestorePolicy(QStateMachine::RestorePolicy restorePolicy);
+
+ void postEvent(QEvent *event, EventPriority priority = NormalPriority);
+ int postDelayedEvent(QEvent *event, int delay);
+ bool cancelDelayedEvent(int id);
+
+ QSet<QAbstractState*> configuration() const;
+
+#ifndef QT_NO_STATEMACHINE_EVENTFILTER
+ bool eventFilter(QObject *watched, QEvent *event);
+#endif
+
+public Q_SLOTS:
+ void start();
+ void stop();
+
+Q_SIGNALS:
+ void started();
+ void stopped();
+
+protected:
+ void onEntry(QEvent *event);
+ void onExit(QEvent *event);
+
+ virtual void beginSelectTransitions(QEvent *event);
+ virtual void endSelectTransitions(QEvent *event);
+
+ virtual void beginMicrostep(QEvent *event);
+ virtual void endMicrostep(QEvent *event);
+
+ bool event(QEvent *e);
+
+protected:
+ QStateMachine(QStateMachinePrivate &dd, QObject *parent);
+
+private:
+ Q_DISABLE_COPY(QStateMachine)
+ Q_DECLARE_PRIVATE(QStateMachine)
+ Q_PRIVATE_SLOT(d_func(), void _q_start())
+ Q_PRIVATE_SLOT(d_func(), void _q_process())
+#ifndef QT_NO_ANIMATION
+ Q_PRIVATE_SLOT(d_func(), void _q_animationFinished())
+#endif
+};
+
+#endif //QT_NO_STATEMACHINE
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif
diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h
new file mode 100644
index 0000000000..70b7b8f104
--- /dev/null
+++ b/src/corelib/statemachine/qstatemachine_p.h
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSTATEMACHINE_P_H
+#define QSTATEMACHINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qstate_p.h"
+
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qset.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_NAMESPACE
+
+class QEvent;
+#ifndef QT_NO_STATEMACHINE_EVENTFILTER
+class QEventTransition;
+#endif
+class QSignalEventGenerator;
+class QSignalTransition;
+class QAbstractState;
+class QAbstractTransition;
+class QFinalState;
+class QHistoryState;
+class QState;
+
+#ifndef QT_NO_ANIMATION
+class QAbstractAnimation;
+#endif
+
+class QStateMachine;
+class Q_CORE_EXPORT QStateMachinePrivate : public QStatePrivate
+{
+ Q_DECLARE_PUBLIC(QStateMachine)
+public:
+ enum State {
+ NotRunning,
+ Starting,
+ Running
+ };
+ enum EventProcessingMode {
+ DirectProcessing,
+ QueuedProcessing
+ };
+ enum StopProcessingReason {
+ EventQueueEmpty,
+ Finished,
+ Stopped
+ };
+
+ QStateMachinePrivate();
+ ~QStateMachinePrivate();
+
+ static QStateMachinePrivate *get(QStateMachine *q);
+
+ QState *findLCA(const QList<QAbstractState*> &states) const;
+
+ static bool stateEntryLessThan(QAbstractState *s1, QAbstractState *s2);
+ static bool stateExitLessThan(QAbstractState *s1, QAbstractState *s2);
+
+ QAbstractState *findErrorState(QAbstractState *context);
+ void setError(QStateMachine::Error error, QAbstractState *currentContext);
+
+ // private slots
+ void _q_start();
+ void _q_process();
+#ifndef QT_NO_ANIMATION
+ void _q_animationFinished();
+#endif
+
+ QState *rootState() const;
+
+ QState *startState();
+ void removeStartState();
+
+ void clearHistory();
+
+ void microstep(QEvent *event, const QList<QAbstractTransition*> &transitionList);
+ bool isPreempted(const QAbstractState *s, const QSet<QAbstractTransition*> &transitions) const;
+ QSet<QAbstractTransition*> selectTransitions(QEvent *event) const;
+ QList<QAbstractState*> exitStates(QEvent *event, const QList<QAbstractTransition*> &transitionList);
+ void executeTransitionContent(QEvent *event, const QList<QAbstractTransition*> &transitionList);
+ QList<QAbstractState*> enterStates(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions);
+ void addStatesToEnter(QAbstractState *s, QState *root,
+ QSet<QAbstractState*> &statesToEnter,
+ QSet<QAbstractState*> &statesForDefaultEntry);
+
+ void applyProperties(const QList<QAbstractTransition*> &transitionList,
+ const QList<QAbstractState*> &exitedStates,
+ const QList<QAbstractState*> &enteredStates);
+
+ static QState *toStandardState(QAbstractState *state);
+ static const QState *toStandardState(const QAbstractState *state);
+ static QFinalState *toFinalState(QAbstractState *state);
+ static QHistoryState *toHistoryState(QAbstractState *state);
+
+ bool isInFinalState(QAbstractState *s) const;
+ static bool isFinal(const QAbstractState *s);
+ static bool isParallel(const QAbstractState *s);
+ bool isCompound(const QAbstractState *s) const;
+ bool isAtomic(const QAbstractState *s) const;
+ static bool isDescendantOf(const QAbstractState *s, const QAbstractState *other);
+ static QList<QState*> properAncestors(const QAbstractState *s, const QState *upperBound);
+
+ void goToState(QAbstractState *targetState);
+
+ void registerTransitions(QAbstractState *state);
+ void registerSignalTransition(QSignalTransition *transition);
+ void unregisterSignalTransition(QSignalTransition *transition);
+#ifndef QT_NO_STATEMACHINE_EVENTFILTER
+ void registerEventTransition(QEventTransition *transition);
+ void unregisterEventTransition(QEventTransition *transition);
+ void handleFilteredEvent(QObject *watched, QEvent *event);
+#endif
+ void unregisterTransition(QAbstractTransition *transition);
+ void unregisterAllTransitions();
+ void handleTransitionSignal(QObject *sender, int signalIndex,
+ void **args);
+
+ void postInternalEvent(QEvent *e);
+ void postExternalEvent(QEvent *e);
+ QEvent *dequeueInternalEvent();
+ QEvent *dequeueExternalEvent();
+ bool isInternalEventQueueEmpty();
+ bool isExternalEventQueueEmpty();
+ void processEvents(EventProcessingMode processingMode);
+ void cancelAllDelayedEvents();
+
+#ifndef QT_NO_PROPERTIES
+ typedef QPair<QObject *, QByteArray> RestorableId;
+ QHash<RestorableId, QVariant> registeredRestorables;
+ void registerRestorable(QObject *object, const QByteArray &propertyName);
+ void unregisterRestorable(QObject *object, const QByteArray &propertyName);
+ bool hasRestorable(QObject *object, const QByteArray &propertyName) const;
+ QVariant restorableValue(QObject *object, const QByteArray &propertyName) const;
+ QList<QPropertyAssignment> restorablesToPropertyList(const QHash<RestorableId, QVariant> &restorables) const;
+#endif
+
+ State state;
+ QState *_startState;
+ bool processing;
+ bool processingScheduled;
+ bool stop;
+ StopProcessingReason stopProcessingReason;
+ QSet<QAbstractState*> configuration;
+ QList<QEvent*> internalEventQueue;
+ QList<QEvent*> externalEventQueue;
+ QMutex internalEventMutex;
+ QMutex externalEventMutex;
+
+ QStateMachine::Error error;
+ QStateMachine::RestorePolicy globalRestorePolicy;
+
+ QString errorString;
+ QSet<QAbstractState *> pendingErrorStates;
+ QSet<QAbstractState *> pendingErrorStatesForDefaultEntry;
+
+#ifndef QT_NO_ANIMATION
+ bool animated;
+
+ QPair<QList<QAbstractAnimation*>, QList<QAbstractAnimation*> >
+ initializeAnimation(QAbstractAnimation *abstractAnimation,
+ const QPropertyAssignment &prop);
+
+ QHash<QAbstractState*, QList<QAbstractAnimation*> > animationsForState;
+ QHash<QAbstractAnimation*, QPropertyAssignment> propertyForAnimation;
+ QHash<QAbstractAnimation*, QAbstractState*> stateForAnimation;
+ QSet<QAbstractAnimation*> resetAnimationEndValues;
+
+ QList<QAbstractAnimation *> defaultAnimations;
+ QMultiHash<QAbstractState *, QAbstractAnimation *> defaultAnimationsForSource;
+ QMultiHash<QAbstractState *, QAbstractAnimation *> defaultAnimationsForTarget;
+
+#endif // QT_NO_ANIMATION
+
+ QSignalEventGenerator *signalEventGenerator;
+
+ QHash<const QObject*, QVector<int> > connections;
+#ifndef QT_NO_STATEMACHINE_EVENTFILTER
+ QHash<QObject*, QHash<QEvent::Type, int> > qobjectEvents;
+#endif
+ QHash<int, QEvent*> delayedEvents;
+ QMutex delayedEventsMutex;
+
+ typedef QEvent* (*f_cloneEvent)(QEvent*);
+ struct Handler {
+ f_cloneEvent cloneEvent;
+ };
+
+ static const Handler *handler;
+};
+
+Q_CORE_EXPORT const QStateMachinePrivate::Handler *qcoreStateMachineHandler();
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/corelib/statemachine/statemachine.pri b/src/corelib/statemachine/statemachine.pri
new file mode 100644
index 0000000000..910cf5e9b3
--- /dev/null
+++ b/src/corelib/statemachine/statemachine.pri
@@ -0,0 +1,28 @@
+HEADERS += $$PWD/qstatemachine.h \
+ $$PWD/qstatemachine_p.h \
+ $$PWD/qsignaleventgenerator_p.h \
+ $$PWD/qabstractstate.h \
+ $$PWD/qabstractstate_p.h \
+ $$PWD/qstate.h \
+ $$PWD/qstate_p.h \
+ $$PWD/qfinalstate.h \
+ $$PWD/qhistorystate.h \
+ $$PWD/qhistorystate_p.h \
+ $$PWD/qabstracttransition.h \
+ $$PWD/qabstracttransition_p.h \
+ $$PWD/qsignaltransition.h \
+ $$PWD/qsignaltransition_p.h
+
+SOURCES += $$PWD/qstatemachine.cpp \
+ $$PWD/qabstractstate.cpp \
+ $$PWD/qstate.cpp \
+ $$PWD/qfinalstate.cpp \
+ $$PWD/qhistorystate.cpp \
+ $$PWD/qabstracttransition.cpp \
+ $$PWD/qsignaltransition.cpp
+
+!contains(DEFINES, QT_NO_STATEMACHINE_EVENTFILTER) {
+HEADERS += $$PWD/qeventtransition.h \
+ $$PWD/qeventtransition_p.h
+SOURCES += $$PWD/qeventtransition.cpp
+}