diff options
Diffstat (limited to 'src/corelib/statemachine')
27 files changed, 6044 insertions, 0 deletions
diff --git a/src/corelib/statemachine/qabstractstate.cpp b/src/corelib/statemachine/qabstractstate.cpp new file mode 100644 index 0000000..973057b --- /dev/null +++ b/src/corelib/statemachine/qabstractstate.cpp @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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() +{ +} + +QAbstractStatePrivate *QAbstractStatePrivate::get(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, 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 +{ + return qobject_cast<QState*>(parent()); +} + +/*! + 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 0000000..ee55541 --- /dev/null +++ b/src/corelib/statemachine/qabstractstate.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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: + 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 0000000..b4f3108 --- /dev/null +++ b/src/corelib/statemachine/qabstractstate_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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: + QAbstractStatePrivate(); + + static QAbstractStatePrivate *get(QAbstractState *q); + + QStateMachine *machine() const; + + void callOnEntry(QEvent *e); + void callOnExit(QEvent *e); + + void emitEntered(); + void emitExited(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qabstracttransition.cpp b/src/corelib/statemachine/qabstracttransition.cpp new file mode 100644 index 0000000..c1b553c --- /dev/null +++ b/src/corelib/statemachine/qabstracttransition.cpp @@ -0,0 +1,345 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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. + + 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 +*/ + +/*! + \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 +{ + QObject *par = parent; + while (par != 0) { + if (QStateMachine *mach = qobject_cast<QStateMachine*>(par)) + return mach; + par = par->parent(); + } + return 0; +} + +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); +} + +/*! + Constructs a new QAbstractTransition object with the given \a sourceState. +*/ +QAbstractTransition::QAbstractTransition(QState *sourceState) + : QObject(*new QAbstractTransitionPrivate, sourceState) +{ +} + +/*! + Constructs a new QAbstractTransition object with the given \a targets and \a + sourceState. +*/ +QAbstractTransition::QAbstractTransition(const QList<QAbstractState*> &targets, + QState *sourceState) + : QObject(*new QAbstractTransitionPrivate, sourceState) +{ + setTargetStates(targets); +} + +/*! + \internal +*/ +QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, + QState *parent) + : QObject(dd, parent) +{ +} + +/*! + \internal +*/ +QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, + const QList<QAbstractState*> &targets, + QState *parent) + : QObject(dd, parent) +{ + setTargetStates(targets); +} + +/*! + 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(); +} + +/*! + 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); + 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; + } + if (target->machine() != 0 && target->machine()->rootState() == target) { + qWarning("QAbstractTransition::setTargetStates: root state cannot be target of transition"); + 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. +*/ + +/*! + \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 0000000..a1a62c9 --- /dev/null +++ b/src/corelib/statemachine/qabstracttransition.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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); + QAbstractTransition(const QList<QAbstractState*> &targets, 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 + +protected: + virtual bool eventTest(QEvent *event) = 0; + + virtual void onTransition(QEvent *event) = 0; + + bool event(QEvent *e); + +protected: + QAbstractTransition(QAbstractTransitionPrivate &dd, QState *parent); + QAbstractTransition(QAbstractTransitionPrivate &dd, + const QList<QAbstractState*> &targets, 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 0000000..f067984 --- /dev/null +++ b/src/corelib/statemachine/qabstracttransition_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/qpointer.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); + void callOnTransition(QEvent *e); + QState *sourceState() const; + QStateMachine *machine() const; + + QList<QPointer<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 0000000..4c40256 --- /dev/null +++ b/src/corelib/statemachine/qeventtransition.cpp @@ -0,0 +1,290 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qeventtransition.h" + +#ifndef QT_NO_STATEMACHINE + +#include "qeventtransition_p.h" +#include "qwrappedevent.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 QWrappedEvent for the proper + object and event type. You may then cast the event to a QWrappedEvent and + get the original event by calling QWrappedEvent::event(), and perform + additional checks on that object. + + \sa QState::addTransition() +*/ + +/*! + \property QEventTransition::eventObject + + \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; +} + +/*! + Constructs a new QEventTransition object associated with events of the given + \a type for the given \a object. The transition has the given \a targets and + \a sourceState. +*/ +QEventTransition::QEventTransition(QObject *object, QEvent::Type type, + const QList<QAbstractState*> &targets, + QState *sourceState) + : QAbstractTransition(*new QEventTransitionPrivate, targets, 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; +} + +/*! + \internal +*/ +QEventTransition::QEventTransition(QEventTransitionPrivate &dd, QObject *object, + QEvent::Type type, const QList<QAbstractState*> &targets, + QState *parent) + : QAbstractTransition(dd, targets, 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::eventObject() 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::setEventObject(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::Wrapped) { + QWrappedEvent *we = static_cast<QWrappedEvent*>(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 0000000..40ffecf --- /dev/null +++ b/src/corelib/statemachine/qeventtransition.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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* eventObject READ eventObject WRITE setEventObject) + 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 *object, QEvent::Type type, + const QList<QAbstractState*> &targets, QState *sourceState = 0); + ~QEventTransition(); + + QObject *eventObject() const; + void setEventObject(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); + QEventTransition(QEventTransitionPrivate &dd, QObject *object, + QEvent::Type type, const QList<QAbstractState*> &targets, + 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 0000000..ab17ad3 --- /dev/null +++ b/src/corelib/statemachine/qeventtransition_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 0000000..0eb531f --- /dev/null +++ b/src/corelib/statemachine/qfinalstate.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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() +{ +} + +/*! + 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 0000000..865f333 --- /dev/null +++ b/src/corelib/statemachine/qfinalstate.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 0000000..4304da3 --- /dev/null +++ b/src/corelib/statemachine/qhistorystate.cpp @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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() + : defaultState(0) +{ +} + +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) +{ + Q_D(QHistoryState); + d->historyType = ShallowHistory; +} +/*! + 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. +*/ +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 0000000..eee43d1 --- /dev/null +++ b/src/corelib/statemachine/qhistorystate.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 0000000..875dac8 --- /dev/null +++ b/src/corelib/statemachine/qhistorystate_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/qsignalevent.h b/src/corelib/statemachine/qsignalevent.h new file mode 100644 index 0000000..79d1053 --- /dev/null +++ b/src/corelib/statemachine/qsignalevent.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSIGNALEVENT_H +#define QSIGNALEVENT_H + +#include <QtCore/qcoreevent.h> + +#include <QtCore/qlist.h> +#include <QtCore/qvariant.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_STATEMACHINE + +class Q_CORE_EXPORT QSignalEvent : public QEvent +{ +public: + QSignalEvent(const QObject *sender, int signalIndex, + const QList<QVariant> &arguments); + ~QSignalEvent(); + + inline const QObject *sender() const { return m_sender; } + inline int signalIndex() const { return m_signalIndex; } + inline QList<QVariant> arguments() const { return m_arguments; } + +private: + const QObject *m_sender; + int m_signalIndex; + QList<QVariant> m_arguments; +}; + +#endif //QT_NO_STATEMACHINE + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/qsignaleventgenerator_p.h b/src/corelib/statemachine/qsignaleventgenerator_p.h new file mode 100644 index 0000000..cf0ea1e --- /dev/null +++ b/src/corelib/statemachine/qsignaleventgenerator_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 0000000..2e150a7 --- /dev/null +++ b/src/corelib/statemachine/qsignaltransition.cpp @@ -0,0 +1,265 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsignaltransition.h" + +#ifndef QT_NO_STATEMACHINE + +#include "qsignaltransition_p.h" +#include "qsignalevent.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 QSignalEvent object. Example: + + \code + class CheckedTransition : public QSignalTransition + { + public: + CheckedTransition(QCheckBox *check) + : QSignalTransition(check, SIGNAL(stateChanged(int))) {} + protected: + bool eventTest(QEvent *e) const { + if (!QSignalTransition::eventTest(e)) + return false; + QSignalEvent *se = static_cast<QSignalEvent*>(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; +} + +/*! + Constructs a new signal transition associated with the given \a signal of + the given \a sender. The transition has the given \a targets and \a + sourceState. +*/ +QSignalTransition::QSignalTransition(QObject *sender, const char *signal, + const QList<QAbstractState*> &targets, + QState *sourceState) + : QAbstractTransition(*new QSignalTransitionPrivate, targets, 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 \a event is a QSignalEvent object. The default implementation returns + true if 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::Signal) { + if (d->signalIndex == -1) + return false; + QSignalEvent *se = static_cast<QSignalEvent*>(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); +} + +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 0000000..02b1de9 --- /dev/null +++ b/src/corelib/statemachine/qsignaltransition.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 *sender, const char *signal, + const QList<QAbstractState*> &targets, + 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 0000000..aacb6fc --- /dev/null +++ b/src/corelib/statemachine/qsignaltransition_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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(); + + QObject *sender; + QByteArray signal; + int signalIndex; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp new file mode 100644 index 0000000..5dd56c0 --- /dev/null +++ b/src/corelib/statemachine/qstate.cpp @@ -0,0 +1,489 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 assignProperty() function is used for defining property assignments that + should be performed when a state is entered. + + Top-level states must be passed QStateMachine::rootState() 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() + : errorState(0), initialState(0), childMode(QState::ExclusiveStates) +{ +} + +QStatePrivate::~QStatePrivate() +{ +} + +void QStatePrivate::emitFinished() +{ + Q_Q(QState); + emit q->finished(); +} + +void QStatePrivate::emitPolished() +{ + Q_Q(QState); + emit q->polished(); +} + +/*! + 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 +{ + QList<QAbstractState*> result; + 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; + result.append(s); + } + return result; +} + +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 +{ + QList<QAbstractTransition*> result; + QList<QObject*>::const_iterator it; + for (it = children.constBegin(); it != children.constEnd(); ++it) { + QAbstractTransition *t = qobject_cast<QAbstractTransition*>(*it); + if (t) + result.append(t); + } + return result; +} + +/*! + 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 polished() +*/ +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)); +} + +/*! + Returns this state group's error state. + + \sa QStateMachine::errorState(), QStateMachine::setErrorState() +*/ +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. + + \sa QStateMachine::setErrorState(), QStateMachine::errorState() +*/ +void QState::setErrorState(QAbstractState *state) +{ + Q_D(QState); + if (state != 0 && state->machine() != machine()) { + qWarning("QState::setErrorState: error state cannot belong " + "to a different state machine"); + return; + } + if (state != 0 && state->machine() != 0 && state->machine()->rootState() == state) { + qWarning("QStateMachine::setErrorState: root state cannot be error state"); + return; + } + + d->errorState = state; +} + +/*! + Adds the given \a transition. The transition has this state as the source. + This state takes ownership of the transition. If the transition is successfully + added, the function will return the \a transition pointer. Otherwise it will return null. +*/ +QAbstractTransition *QState::addTransition(QAbstractTransition *transition) +{ + Q_D(QState); + if (!transition) { + qWarning("QState::addTransition: cannot add null transition"); + return 0; + } + + // machine() will always be non-null for root state + if (machine() != 0 && machine()->rootState() == this) { + qWarning("QState::addTransition: cannot add transition from root state"); + return 0; + } + + const QList<QPointer<QAbstractState> > &targets = QAbstractTransitionPrivate::get(transition)->targetStates; + for (int i = 0; i < targets.size(); ++i) { + QAbstractState *t = targets.at(i); + if (!t) { + qWarning("QState::addTransition: cannot add transition to null state"); + return 0; + } + 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 0; + } + } + transition->setParent(this); + if (machine() != 0 && machine()->configuration().contains(this)) + QStateMachinePrivate::get(machine())->registerTransitions(this); + return transition; +} + +/*! + 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; + if (sender->metaObject()->indexOfSignal(signal+offset) == -1) { + qWarning("QState::addTransition: no such signal %s::%s", + sender->metaObject()->className(), signal+offset); + return 0; + } + QSignalTransition *trans = new QSignalTransition(sender, signal, QList<QAbstractState*>() << target); + addTransition(trans); + return trans; +} + +namespace { + +// ### Make public? +class UnconditionalTransition : public QAbstractTransition +{ +public: + UnconditionalTransition(QAbstractState *target) + : QAbstractTransition(QList<QAbstractState*>() << 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); + return addTransition(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); +} + +/*! + \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) +{ + 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::polished() + + This signal is emitted when all properties have been assigned their final value. + + \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 0000000..c98bb64 --- /dev/null +++ b/src/corelib/statemachine/qstate.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSTATE_H +#define QSTATE_H + +#include <QtCore/qabstractstate.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); + + QAbstractTransition *addTransition(QAbstractTransition *transition); + QSignalTransition *addTransition(QObject *sender, const char *signal, QAbstractState *target); + QAbstractTransition *addTransition(QAbstractState *target); + void removeTransition(QAbstractTransition *transition); + + QAbstractState *initialState() const; + void setInitialState(QAbstractState *state); + + ChildMode childMode() const; + void setChildMode(ChildMode mode); + + void assignProperty(QObject *object, const char *name, + const QVariant &value); + +Q_SIGNALS: + void finished(); + void polished(); + +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 0000000..eddd831 --- /dev/null +++ b/src/corelib/statemachine/qstate_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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) {} + 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 emitPolished(); + + QAbstractState *errorState; + QAbstractState *initialState; + QState::ChildMode childMode; + + 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 0000000..64b33ac --- /dev/null +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -0,0 +1,2214 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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 "qsignalevent.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" +#include "qwrappedevent.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. + + The rootState() is the parent of all top-level states in the + machine; it is used, for instance, when the state graph is + deleted. It is created by the machine. + + Use the addState() function to add a state to the state machine. + All top-level states are added to the root state. States are + removed with the removeState() function. Removing states while the + machine is running is discouraged. + + 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: + + \code + QPushButton button; + + QStateMachine machine; + QState *s1 = new QState(); + s1->assignProperty(&button, "text", "Click me"); + + QFinalState *s2 = new QFinalState(); + s1->addTransition(&button, SIGNAL(clicked()), s2); + + machine.addState(s1); + machine.addState(s2); + machine.setInitialState(s1); + machine.start(); + \endcode + + 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 enter the + \l{errorState}{error state}, which is a special state created by + the machine. 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. + So it is possible to handle the error, for instance, by connecting + to the \l{QAbstractState::}{entered()} signal of the error state. + It is also possible to set a custom error state with + setErrorState(). + + \omit This stuff will be moved elsewhere +This is + typically used in conjunction with \l{Signals and Slots}{signals}; the + signals determine the flow of the state graph, whereas the states' property + assignments and method invocations are the actions. + + The postEvent() function posts an event to the state machine. This is useful + when you are using custom events to trigger transitions. + \endomit + + \sa QAbstractState, QAbstractTransition, QState, {The State Machine Framework} +*/ + +/*! + \property QStateMachine::rootState + + \brief the root state of this state machine +*/ + +/*! + \property QStateMachine::initialState + + \brief the initial state of this state machine + + The initial state must be one of the rootState()'s child states. +*/ + +/*! + \property QStateMachine::errorState + + \brief the error state of this state machine +*/ + +/*! + \property QStateMachine::errorString + + \brief the error string of this state machine +*/ + +/*! + \property QStateMachine::globalRestorePolicy + + \brief the restore policy for states of this state machine. + + The default value of this property is + QStateMachine::DoNotRestoreProperties. +*/ + +#ifndef QT_NO_ANIMATION +/*! + \property QStateMachine::animationsEnabled + + \brief whether animations are enabled + + The default value of this property is true. + + \sa QAbstractTransition::addAnimation() +*/ +#endif + +// #define QSTATEMACHINE_DEBUG + +QStateMachinePrivate::QStateMachinePrivate() +{ + state = NotRunning; + processing = false; + processingScheduled = false; + stop = false; + error = QStateMachine::NoError; + globalRestorePolicy = QStateMachine::DoNotRestoreProperties; + rootState = 0; + initialErrorStateForRoot = 0; + signalEventGenerator = 0; +#ifndef QT_NO_ANIMATION + animationsEnabled = true; +#endif +} + +QStateMachinePrivate::~QStateMachinePrivate() +{ + qDeleteAll(internalEventQueue); + qDeleteAll(externalEventQueue); +} + +QStateMachinePrivate *QStateMachinePrivate::get(QStateMachine *q) +{ + if (q) + return q->d_func(); + return 0; +} + +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 { + QState *lca = 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 { + QState *lca = findLCA(QList<QAbstractState*>() << s1 << s2); + Q_ASSERT(lca != 0); + return (indexOfDescendant(lca, s1) < indexOfDescendant(lca, s2)); + } +} + +QState *QStateMachinePrivate::findLCA(const QList<QAbstractState*> &states) +{ + if (states.isEmpty()) + return 0; + QList<QState*> ancestors = properAncestors(states.at(0), 0); + 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, 0); + if (QState *grp = qobject_cast<QState*>(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); + applyProperties(enabledTransitions, exitedStates, enteredStates); +#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 = qobject_cast<QState*>(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); + } +} + +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) { + QState *grandparent = parent->parentState(); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": emitting finished signal for" << parent; +#endif + QStatePrivate::get(parent)->emitFinished(); + 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) { +#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 = qobject_cast<QHistoryState*>(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 { + statesToEnter.insert(s); + if (isParallel(s)) { + QState *grp = qobject_cast<QState*>(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 = qobject_cast<QState*>(s); + QAbstractState *initial = grp->initialState(); + if (initial != 0) { + 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); + } + } + } + } +} + +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 = qobject_cast<QState*>(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 (animationsEnabled) { + 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 = qFindChildren<QVariantAnimation*>(anim); + 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) { + 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 polished signal for entered states that have no animated properties. + for (int i = 0; i < enteredStates.size(); ++i) { + QState *s = qobject_cast<QState*>(enteredStates.at(i)); + if (s +#ifndef QT_NO_ANIMATION + && !animationsForState.contains(s) +#endif + ) + QStatePrivate::get(s)->emitPolished(); + } +} + +bool QStateMachinePrivate::isFinal(const QAbstractState *s) +{ + return qobject_cast<const QFinalState*>(s) != 0; +} + +bool QStateMachinePrivate::isParallel(const QAbstractState *s) +{ + const QState *ss = qobject_cast<const QState*>(s); + return ss && (QStatePrivate::get(ss)->childMode == QState::ParallelStates); +} + +bool QStateMachinePrivate::isCompound(const QAbstractState *s) +{ + const QState *group = qobject_cast<const QState*>(s); + if (!group) + return false; + return (!isParallel(group) && !QStatePrivate::get(group)->childStates().isEmpty()) + || (qobject_cast<QStateMachine*>(group->parent()) != 0); +} + +bool QStateMachinePrivate::isAtomic(const QAbstractState *s) +{ + const QState *ss = qobject_cast<const QState*>(s); + return (ss && QStatePrivate::get(ss)->childStates().isEmpty()) + || isFinal(s); +} + + +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; +} + +bool QStateMachinePrivate::isInFinalState(QAbstractState* s) const +{ + if (isCompound(s)) { + QState *grp = qobject_cast<QState*>(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 = qobject_cast<QState*>(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; +} + +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); +} + +QAbstractState *QStateMachinePrivate::findErrorState(QAbstractState *context) +{ + // If the user sets the root state's error state to 0, we return the initial error state + if (context == 0) + return initialErrorStateForRoot; + + // Find error state recursively in parent hierarchy if not set explicitly for context state + QAbstractState *errorState = 0; + + QState *s = qobject_cast<QState*>(context); + if (s) + errorState = s->errorState(); + + if (!errorState) + errorState = findErrorState(context->parentState()); + + return errorState; +} + +void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractState *currentContext) +{ + 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) { + Q_ASSERT(currentContext != initialErrorStateForRoot); // RootErrorState is broken + currentErrorState = initialErrorStateForRoot; + } + + Q_ASSERT(currentErrorState != 0); + Q_ASSERT(currentErrorState != rootState); + + QState *lca = findLCA(QList<QAbstractState*>() << currentErrorState << currentContext); + addStatesToEnter(currentErrorState, lca, pendingErrorStates, pendingErrorStatesForDefaultEntry); +} + +#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); + } + + // 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); + + 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(qobject_cast<QState*>(state))->emitPolished(); + } +} + +#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(QList<QAbstractState*>() << target) {} +protected: + virtual bool eventTest(QEvent *) { return true; } + virtual void onTransition(QEvent *) {} +}; + +} // namespace + +void QStateMachinePrivate::_q_start() +{ + Q_Q(QStateMachine); + Q_ASSERT(state == Starting); + if (!rootState) { + state = NotRunning; + return; + } + QAbstractState *initial = rootState->initialState(); + if (initial == 0) + setError(QStateMachine::NoInitialStateError, rootState); + + configuration.clear(); + qDeleteAll(internalEventQueue); + internalEventQueue.clear(); + qDeleteAll(externalEventQueue); + externalEventQueue.clear(); + +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": starting"; +#endif + state = Running; + processingScheduled = true; // we call _q_process() below + emit q->started(); + + StartState *start = new StartState(rootState); + QAbstractTransition *initialTransition = new InitialTransition(initial); + start->addTransition(initialTransition); + QList<QAbstractTransition*> transitions; + transitions.append(initialTransition); + QEvent nullEvent(QEvent::None); + executeTransitionContent(&nullEvent, transitions); + enterStates(&nullEvent, transitions); + applyProperties(transitions, QList<QAbstractState*>() << start, + QList<QAbstractState*>() << initial); + delete start; + +#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) { + stop = false; + processing = false; + stopProcessingReason = Stopped; + break; + } + QSet<QAbstractTransition*> enabledTransitions; + QEvent *e = new QEvent(QEvent::None); + enabledTransitions = selectTransitions(e); + if (enabledTransitions.isEmpty()) { + delete e; + e = 0; + } + if (enabledTransitions.isEmpty() && !internalEventQueue.isEmpty()) { + e = internalEventQueue.takeFirst(); +#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 (externalEventQueue.isEmpty()) { + if (internalEventQueue.isEmpty()) { + processing = false; + stopProcessingReason = EventQueueEmpty; + } + } else { + e = externalEventQueue.takeFirst(); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << q << ": dequeued external event" << e << "of type" << e->type(); +#endif + enabledTransitions = selectTransitions(e); + if (enabledTransitions.isEmpty()) { + delete e; + e = 0; + } + } + } + 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 + switch (stopProcessingReason) { + case EventQueueEmpty: + break; + case Finished: + state = NotRunning; + unregisterAllTransitions(); + emit q->finished(); + break; + case Stopped: + state = NotRunning; + unregisterAllTransitions(); + emit q->stopped(); + break; + } +} + +void QStateMachinePrivate::scheduleProcess() +{ + if ((state != Running) || processing || processingScheduled) + return; + processingScheduled = true; + QMetaObject::invokeMethod(q_func(), "_q_process", Qt::QueuedConnection); +} + +void QStateMachinePrivate::registerTransitions(QAbstractState *state) +{ + QState *group = qobject_cast<QState*>(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); + int signalIndex = sender->metaObject()->indexOfSignal(signal); + if (signalIndex == -1) { + qWarning("QSignalTransition: no such signal: %s::%s", + sender->metaObject()->className(), signal.constData()); + return; + } + 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; +#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() +{ + { + QList<QSignalTransition*> transitions = qFindChildren<QSignalTransition*>(rootState); + for (int i = 0; i < transitions.size(); ++i) + unregisterSignalTransition(transitions.at(i)); + } + { + QList<QEventTransition*> transitions = qFindChildren<QEventTransition*>(rootState); + for (int i = 0; i < transitions.size(); ++i) + unregisterEventTransition(transitions.at(i)); + } +} + +#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; +} +#endif + +void QStateMachinePrivate::handleTransitionSignal(const 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 + internalEventQueue.append(new QSignalEvent(sender, signalIndex, vargs)); + scheduleProcess(); +} + +/*! + Constructs a new state machine with the given \a parent. +*/ +QStateMachine::QStateMachine(QObject *parent) + : QObject(*new QStateMachinePrivate, parent) +{ +} + +/*! + \internal +*/ +QStateMachine::QStateMachine(QStateMachinePrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + Destroys this state machine. +*/ +QStateMachine::~QStateMachine() +{ +} + +namespace { + +class RootErrorState : public QAbstractState +{ +public: + RootErrorState(QState *parent) + : QAbstractState(parent) + { + setObjectName(QString::fromLatin1("DefaultErrorState")); + } + + void onEntry(QEvent *) + { + QAbstractStatePrivate *d = QAbstractStatePrivate::get(this); + QStateMachine *machine = d->machine(); + + qWarning("Unrecoverable error detected in running state machine: %s", + qPrintable(machine->errorString())); + } + + void onExit(QEvent *) {} +}; + +class RootState : public QState +{ +public: + RootState(QState *parent) + : QState(parent) + { + } + + void onEntry(QEvent *) {} + void onExit(QEvent *) {} +}; + +} // namespace + +/*! + Returns this state machine's root state. +*/ +QState *QStateMachine::rootState() const +{ + Q_D(const QStateMachine); + if (!d->rootState) { + const_cast<QStateMachinePrivate*>(d)->rootState = new RootState(0); + const_cast<QStateMachinePrivate*>(d)->initialErrorStateForRoot = new RootErrorState(d->rootState); + d->rootState->setParent(const_cast<QStateMachine*>(this)); + d->rootState->setErrorState(d->initialErrorStateForRoot); + } + return d->rootState; +} + +/*! + Returns the error state of the state machine's root state. + + \sa QState::errorState() +*/ +QAbstractState *QStateMachine::errorState() const +{ + return rootState()->errorState(); +} + +/*! + Sets the error state of this state machine's root state to be \a state. When a running state + machine encounters an error which puts it in an undefined state, it will enter an error state + based on the context of the error that occurred. It will enter this state regardless of what + is currently in the event queue. + + If the erroneous state has an error state set, this will be entered by the machine. If no error + state has been set, the state machine will search the parent hierarchy recursively for an + error state. The error state of the root state can thus be seen as a global error state that + applies for the states for which a more specific error state has not been set. + + Before entering the error state, the state machine will set the error code returned by error() and + error message returned by errorString(). + + The default error state will print a warning to the console containing the information returned by + errorString(). By setting a new error state on either the state machine itself, or on specific + states, you can fine tune error handling in the state machine. + + If the root state's error state is set to 0, or if the error state selected by the machine itself + contains an error, the default error state will be used. + + \sa QState::setErrorState(), rootState() +*/ +void QStateMachine::setErrorState(QAbstractState *state) +{ + rootState()->setErrorState(state); +} + +/*! \enum QStateMachine::Error + + This enum type defines errors that can occur in the state machine at run time. When the state + 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 DoNotRestoreProperties 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::DoNotRestoreProperties. + + \sa globalRestorePolicy() +*/ +void QStateMachine::setGlobalRestorePolicy(QStateMachine::RestorePolicy restorePolicy) +{ + Q_D(QStateMachine); + d->globalRestorePolicy = restorePolicy; +} + +/*! + Returns this state machine's initial state, or 0 if no initial state has + been set. +*/ +QAbstractState *QStateMachine::initialState() const +{ + Q_D(const QStateMachine); + if (!d->rootState) + return 0; + return d->rootState->initialState(); +} + +/*! + Sets this state machine's initial \a state. +*/ +void QStateMachine::setInitialState(QAbstractState *state) +{ + Q_D(QStateMachine); + if (!d->rootState) { + if (!state) + return; + rootState()->setInitialState(state); + } + d->rootState->setInitialState(state); +} + +/*! + Adds the given \a state to this state machine. The state becomes a top-level + state (i.e. a child of the rootState()). + + If the state is already in a different machine, it will first be removed + from its old machine, and then added to this machine. + + \sa removeState(), rootState(), setInitialState() +*/ +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(rootState()); +} + +/*! + 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. + + \sa started(), finished(), stop(), initialState() +*/ +void QStateMachine::start() +{ + Q_D(QStateMachine); + + if (rootState()->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->scheduleProcess(); + break; + } +} + +/*! + Posts the given \a event for processing by this state machine, with a delay + of \a delay milliseconds. + + 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. +*/ +void QStateMachine::postEvent(QEvent *event, int delay) +{ + Q_D(QStateMachine); + if (d->state != QStateMachinePrivate::Running) { + qWarning("QStateMachine::postEvent: cannot post event when the state machine is not running"); + return; + } +#ifdef QSTATEMACHINE_DEBUG + qDebug() << this << ": posting external event" << event << "with delay" << delay; +#endif + if (delay) { + int tid = startTimer(delay); + d->delayedEvents[tid] = event; + } else { + d->externalEventQueue.append(event); + d->scheduleProcess(); + } +} + +/*! + \internal + + Posts the given internal \a event for processing by this state machine. +*/ +void QStateMachine::postInternalEvent(QEvent *event) +{ + Q_D(QStateMachine); +#ifdef QSTATEMACHINE_DEBUG + qDebug() << this << ": posting internal event" << event; +#endif + d->internalEventQueue.append(event); + d->scheduleProcess(); +} + +/*! + \internal + + Returns the maximal consistent set of states (including parallel and final + states) that this state machine is currently in. If a state \c s is in the + configuration, it is always the case that the parent of \c s is also in + c. Note, however, that the rootState() is not an explicit member of the + 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::finished() + + This signal is emitted when the state machine has reached a top-level final + state (QFinalState). + + \sa QStateMachine::started() +*/ + +/*! + \fn QStateMachine::stopped() + + This signal is emitted when the state machine has stopped. + + \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->delayedEvents.contains(tid)) { + killTimer(tid); + QEvent *ee = d->delayedEvents.take(tid); + d->externalEventQueue.append(ee); + d->scheduleProcess(); + return true; + } + } else if (e->type() == QEvent::ChildAdded) { + QChildEvent *ce = static_cast<QChildEvent*>(e); + if (QAbstractState *state = qobject_cast<QAbstractState*>(ce->child())) { + if (state != rootState()) { + state->setParent(rootState()); + return true; + } + } + } + return QObject::event(e); +} + +#ifndef QT_NO_STATEMACHINE_EVENTFILTER +/*! + \reimp +*/ +bool QStateMachine::eventFilter(QObject *watched, QEvent *event) +{ + Q_D(QStateMachine); + Q_ASSERT(d->qobjectEvents.contains(watched)); + if (d->qobjectEvents[watched].contains(event->type())) + postEvent(new QWrappedEvent(watched, d->handler->cloneEvent(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); +} + +#ifndef QT_NO_ANIMATION + +/*! + Returns whether animations are enabled for this state machine. +*/ +bool QStateMachine::animationsEnabled() const +{ + Q_D(const QStateMachine); + return d->animationsEnabled; +} + +/*! + Sets whether animations are \a enabled for this state machine. +*/ +void QStateMachine::setAnimationsEnabled(bool enabled) +{ + Q_D(QStateMachine); + d->animationsEnabled = 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); + 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 QSignalEvent + + \brief The QSignalEvent 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. QSignalEvent 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 QSignalEvent object with the given \a sender, \a + signalIndex and \a arguments. +*/ +QSignalEvent::QSignalEvent(const QObject *sender, int signalIndex, + const QList<QVariant> &arguments) + : QEvent(QEvent::Signal), m_sender(sender), + m_signalIndex(signalIndex), m_arguments(arguments) +{ +} + +/*! + Destroys this QSignalEvent. +*/ +QSignalEvent::~QSignalEvent() +{ +} + +/*! + \fn QSignalEvent::sender() const + + Returns the object that emitted the signal. + + \sa QObject::sender() +*/ + +/*! + \fn QSignalEvent::signalIndex() const + + Returns the index of the signal. + + \sa QMetaObject::indexOfSignal(), QMetaObject::method() +*/ + +/*! + \fn QSignalEvent::arguments() const + + Returns the arguments of the signal. +*/ + + +/*! + \class QWrappedEvent + + \brief The QWrappedEvent 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. QWrappedEvent 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 QWrappedEvent object with the given \a object + and \a event. +*/ +QWrappedEvent::QWrappedEvent(QObject *object, QEvent *event) + : QEvent(QEvent::Wrapped), m_object(object), m_event(event) +{ +} + +/*! + Destroys this QWrappedEvent. +*/ +QWrappedEvent::~QWrappedEvent() +{ +} + +/*! + \fn QWrappedEvent::object() const + + Returns the object that the event is associated with. +*/ + +/*! + \fn QWrappedEvent::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 0000000..0b3c728 --- /dev/null +++ b/src/corelib/statemachine/qstatemachine.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSTATEMACHINE_H +#define QSTATEMACHINE_H + +#include <QtCore/qabstractstate.h> + +#include <QtCore/qlist.h> +#include <QtCore/qobject.h> +#include <QtCore/qset.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_STATEMACHINE + +class QEvent; +class QAbstractState; +class QState; + +class QStateMachinePrivate; +class QAbstractAnimation; +class QAbstractState; +class Q_CORE_EXPORT QStateMachine : public QObject +{ + Q_OBJECT + Q_PROPERTY(QState* rootState READ rootState) + Q_PROPERTY(QAbstractState* initialState READ initialState WRITE setInitialState) + Q_PROPERTY(QAbstractState* errorState READ errorState WRITE setErrorState) + Q_PROPERTY(QString errorString READ errorString) + Q_PROPERTY(RestorePolicy globalRestorePolicy READ globalRestorePolicy WRITE setGlobalRestorePolicy) + Q_ENUMS(RestorePolicy) +#ifndef QT_NO_ANIMATION + Q_PROPERTY(bool animationsEnabled READ animationsEnabled WRITE setAnimationsEnabled) +#endif +public: + enum RestorePolicy { + DoNotRestoreProperties, + RestoreProperties + }; + + enum Error { + NoError, + NoInitialStateError, + NoDefaultStateInHistoryStateError, + NoCommonAncestorForTransitionError + }; + + QStateMachine(QObject *parent = 0); + ~QStateMachine(); + + void addState(QAbstractState *state); + void removeState(QAbstractState *state); + + QState *rootState() const; + + QAbstractState *initialState() const; + void setInitialState(QAbstractState *state); + + QAbstractState *errorState() const; + void setErrorState(QAbstractState *state); + + Error error() const; + QString errorString() const; + void clearError(); + + bool isRunning() const; + +#ifndef QT_NO_ANIMATION + bool animationsEnabled() const; + void setAnimationsEnabled(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, int delay = 0); + + 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(); + void finished(); + +protected: + void postInternalEvent(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 0000000..24073ca --- /dev/null +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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/qobject_p.h> +#include <QtCore/qcoreevent.h> +#include <QtCore/qhash.h> +#include <QtCore/qlist.h> +#include <QtCore/qpair.h> +#include <QtCore/qset.h> +#include <QtCore/qvector.h> + +#include "qstate.h" +#include "private/qstate_p.h" + +QT_BEGIN_NAMESPACE + +class QEvent; +#ifndef QT_NO_STATEMACHINE_EVENTFILTER +class QEventTransition; +#endif +class QSignalEventGenerator; +class QSignalTransition; +class QAbstractState; +class QAbstractTransition; +class QState; + +#ifndef QT_NO_ANIMATION +class QAbstractAnimation; +#endif + +class QStateMachine; +class QStateMachinePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QStateMachine) +public: + enum State { + NotRunning, + Starting, + Running + }; + enum StopProcessingReason { + EventQueueEmpty, + Finished, + Stopped + }; + + QStateMachinePrivate(); + ~QStateMachinePrivate(); + + static QStateMachinePrivate *get(QStateMachine *q); + + static QState *findLCA(const QList<QAbstractState*> &states); + + 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 + + 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); + + bool isInFinalState(QAbstractState *s) const; + static bool isFinal(const QAbstractState *s); + static bool isParallel(const QAbstractState *s); + static bool isCompound(const QAbstractState *s); + static bool isAtomic(const QAbstractState *s); + static bool isDescendantOf(const QAbstractState *s, const QAbstractState *other); + static QList<QState*> properAncestors(const QAbstractState *s, const QState *upperBound); + + 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); +#endif + void unregisterTransition(QAbstractTransition *transition); + void unregisterAllTransitions(); + void handleTransitionSignal(const QObject *sender, int signalIndex, + void **args); + void scheduleProcess(); + + 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; + + State state; + bool processing; + bool processingScheduled; + bool stop; + StopProcessingReason stopProcessingReason; + QState *rootState; + QSet<QAbstractState*> configuration; + QList<QEvent*> internalEventQueue; + QList<QEvent*> externalEventQueue; + + QStateMachine::Error error; + QStateMachine::RestorePolicy globalRestorePolicy; + + QString errorString; + QSet<QAbstractState *> pendingErrorStates; + QSet<QAbstractState *> pendingErrorStatesForDefaultEntry; + QAbstractState *initialErrorStateForRoot; + +#ifndef QT_NO_ANIMATION + bool animationsEnabled; + + 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; + + typedef QEvent* (*f_cloneEvent)(QEvent*); + struct Handler { + f_cloneEvent cloneEvent; + }; + + static Q_CORE_EXPORT const Handler *handler; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/corelib/statemachine/qwrappedevent.h b/src/corelib/statemachine/qwrappedevent.h new file mode 100644 index 0000000..cb4261b --- /dev/null +++ b/src/corelib/statemachine/qwrappedevent.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWRAPPEDEVENT_H +#define QWRAPPEDEVENT_H + +#include <QtCore/qcoreevent.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +#ifndef QT_NO_STATEMACHINE + +class QObject; + +class Q_CORE_EXPORT QWrappedEvent : public QEvent +{ +public: + QWrappedEvent(QObject *object, QEvent *event); + ~QWrappedEvent(); + + inline QObject *object() const { return m_object; } + inline QEvent *event() const { return m_event; } + +private: + QObject *m_object; + QEvent *m_event; + +private: + Q_DISABLE_COPY(QWrappedEvent) +}; + +#endif //QT_NO_STATEMACHINE + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/corelib/statemachine/statemachine.pri b/src/corelib/statemachine/statemachine.pri new file mode 100644 index 0000000..5b19bc1 --- /dev/null +++ b/src/corelib/statemachine/statemachine.pri @@ -0,0 +1,30 @@ +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/qsignalevent.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/qwrappedevent.h \ + $$PWD/qeventtransition.h \ + $$PWD/qeventtransition_p.h +SOURCES += $$PWD/qeventtransition.cpp +} |