summaryrefslogtreecommitdiffstats
path: root/src/statemachine
diff options
context:
space:
mode:
Diffstat (limited to 'src/statemachine')
-rw-r--r--src/statemachine/CMakeLists.txt53
-rw-r--r--src/statemachine/configure.cmake29
-rw-r--r--src/statemachine/doc/images/animations-architecture.pngbin0 -> 27619 bytes
-rw-r--r--src/statemachine/doc/images/move-blocks-chart.pngbin0 -> 15740 bytes
-rw-r--r--src/statemachine/doc/images/rogue-statechart.pngbin0 -> 2490 bytes
-rw-r--r--src/statemachine/doc/images/statemachine-button-history.pngbin0 -> 8493 bytes
-rw-r--r--src/statemachine/doc/images/statemachine-button-nested.pngbin0 -> 7051 bytes
-rw-r--r--src/statemachine/doc/images/statemachine-button.pngbin0 -> 4233 bytes
-rw-r--r--src/statemachine/doc/images/statemachine-customevents.pngbin0 -> 2544 bytes
-rw-r--r--src/statemachine/doc/images/statemachine-customevents2.pngbin0 -> 6713 bytes
-rw-r--r--src/statemachine/doc/images/statemachine-examples.pngbin0 -> 3326 bytes
-rw-r--r--src/statemachine/doc/images/statemachine-finished.pngbin0 -> 5518 bytes
-rw-r--r--src/statemachine/doc/images/statemachine-nonparallel.pngbin0 -> 5350 bytes
-rw-r--r--src/statemachine/doc/images/statemachine-parallel.pngbin0 -> 8631 bytes
-rw-r--r--src/statemachine/doc/qstatemachine-qml-guide.qdoc293
-rw-r--r--src/statemachine/doc/qt6-changes.qdoc43
-rw-r--r--src/statemachine/doc/qtstatemachine-cpp-guide.qdoc511
-rw-r--r--src/statemachine/doc/qtstatemachine-examples.qdoc12
-rw-r--r--src/statemachine/doc/qtstatemachine-index.qdoc68
-rw-r--r--src/statemachine/doc/qtstatemachine-module-cpp.qdoc16
-rw-r--r--src/statemachine/doc/qtstatemachine-module-qml.qdoc18
-rw-r--r--src/statemachine/doc/qtstatemachine-module-use.qdocinc15
-rw-r--r--src/statemachine/doc/qtstatemachine-overview.qdoc42
-rw-r--r--src/statemachine/doc/qtstatemachine.qdocconf55
-rw-r--r--src/statemachine/doc/snippets/code/src_corelib_statemachine_qstatemachine.cpp18
-rw-r--r--src/statemachine/doc/snippets/qml/statemachine/Button.qml51
-rw-r--r--src/statemachine/doc/snippets/qml/statemachine/basicstate.qml18
-rw-r--r--src/statemachine/doc/snippets/qml/statemachine/finalstate.qml26
-rw-r--r--src/statemachine/doc/snippets/qml/statemachine/guardcondition.qml31
-rw-r--r--src/statemachine/doc/snippets/qml/statemachine/historystate.qml48
-rw-r--r--src/statemachine/doc/snippets/qml/statemachine/signaltransition.qml40
-rw-r--r--src/statemachine/doc/snippets/qml/statemachine/signaltransitionsignal.qml25
-rw-r--r--src/statemachine/doc/snippets/qml/statemachine/simplestatemachine.qml31
-rw-r--r--src/statemachine/doc/snippets/qml/statemachine/statemachine-button-history.qml112
-rw-r--r--src/statemachine/doc/snippets/qml/statemachine/statemachine-button-nested-ignore-quit.qml97
-rw-r--r--src/statemachine/doc/snippets/qml/statemachine/statemachine-button-nested.qml89
-rw-r--r--src/statemachine/doc/snippets/qml/statemachine/statemachine-button.qml63
-rw-r--r--src/statemachine/doc/snippets/qml/statemachine/timeouttransition.qml32
-rw-r--r--src/statemachine/doc/snippets/statemachine/eventtest.cpp37
-rw-r--r--src/statemachine/doc/snippets/statemachine/main.cpp52
-rw-r--r--src/statemachine/doc/snippets/statemachine/main2.cpp57
-rw-r--r--src/statemachine/doc/snippets/statemachine/main3.cpp24
-rw-r--r--src/statemachine/doc/snippets/statemachine/main4.cpp75
-rw-r--r--src/statemachine/doc/snippets/statemachine/main5.cpp136
-rw-r--r--src/statemachine/doc/src/images/moveblocks-example.pngbin0 -> 4532 bytes
-rw-r--r--src/statemachine/doc/src/images/rogue-example.pngbin0 -> 10364 bytes
-rw-r--r--src/statemachine/doc/src/images/trafficlight-example.pngbin0 -> 5325 bytes
-rw-r--r--src/statemachine/gui/qbasickeyeventtransition.cpp175
-rw-r--r--src/statemachine/gui/qbasickeyeventtransition_p.h60
-rw-r--r--src/statemachine/gui/qbasicmouseeventtransition.cpp182
-rw-r--r--src/statemachine/gui/qbasicmouseeventtransition_p.h63
-rw-r--r--src/statemachine/gui/qkeyeventtransition.cpp151
-rw-r--r--src/statemachine/gui/qkeyeventtransition.h45
-rw-r--r--src/statemachine/gui/qmouseeventtransition.cpp180
-rw-r--r--src/statemachine/gui/qmouseeventtransition.h49
-rw-r--r--src/statemachine/qabstractstate.cpp203
-rw-r--r--src/statemachine/qabstractstate.h54
-rw-r--r--src/statemachine/qabstractstate_p.h70
-rw-r--r--src/statemachine/qabstracttransition.cpp400
-rw-r--r--src/statemachine/qabstracttransition.h84
-rw-r--r--src/statemachine/qabstracttransition_p.h62
-rw-r--r--src/statemachine/qeventtransition.cpp230
-rw-r--r--src/statemachine/qeventtransition.h53
-rw-r--r--src/statemachine/qeventtransition_p.h57
-rw-r--r--src/statemachine/qfinalstate.cpp105
-rw-r--r--src/statemachine/qfinalstate.h37
-rw-r--r--src/statemachine/qfinalstate_p.h36
-rw-r--r--src/statemachine/qhistorystate.cpp310
-rw-r--r--src/statemachine/qhistorystate.h64
-rw-r--r--src/statemachine/qhistorystate_p.h60
-rw-r--r--src/statemachine/qsignaleventgenerator_p.h44
-rw-r--r--src/statemachine/qsignaltransition.cpp266
-rw-r--r--src/statemachine/qsignaltransition.h67
-rw-r--r--src/statemachine/qsignaltransition_p.h60
-rw-r--r--src/statemachine/qstate.cpp574
-rw-r--r--src/statemachine/qstate.h105
-rw-r--r--src/statemachine/qstate_p.h110
-rw-r--r--src/statemachine/qstatemachine.cpp3188
-rw-r--r--src/statemachine/qstatemachine.h168
-rw-r--r--src/statemachine/qstatemachine_p.h305
-rw-r--r--src/statemachine/qstatemachineglobal.h16
-rw-r--r--src/statemachine/qt_cmdline.cmake0
82 files changed, 9850 insertions, 0 deletions
diff --git a/src/statemachine/CMakeLists.txt b/src/statemachine/CMakeLists.txt
new file mode 100644
index 0000000..e68c571
--- /dev/null
+++ b/src/statemachine/CMakeLists.txt
@@ -0,0 +1,53 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+#####################################################################
+## StateMachine Module:
+#####################################################################
+
+qt_internal_include_in_repo_target_set(qtstatemachine)
+
+qt_internal_add_module(StateMachine
+ SOURCES
+ qabstractstate.cpp qabstractstate.h qabstractstate_p.h
+ qabstracttransition.cpp qabstracttransition.h qabstracttransition_p.h
+ qfinalstate.cpp qfinalstate.h qfinalstate_p.h
+ qhistorystate.cpp qhistorystate.h qhistorystate_p.h
+ qsignaleventgenerator_p.h
+ qsignaltransition.cpp qsignaltransition.h qsignaltransition_p.h
+ qstate.cpp qstate.h qstate_p.h
+ qstatemachine.cpp qstatemachine.h qstatemachine_p.h
+ qstatemachineglobal.h
+ DEFINES
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+ LIBRARIES
+ Qt::CorePrivate
+ PUBLIC_LIBRARIES
+ Qt::Core
+ PRIVATE_MODULE_INTERFACE
+ Qt::CorePrivate
+)
+
+## Scopes:
+#####################################################################
+
+qt_internal_extend_target(StateMachine CONDITION QT_FEATURE_qeventtransition
+ SOURCES
+ gui/qbasickeyeventtransition.cpp gui/qbasickeyeventtransition_p.h
+ gui/qbasicmouseeventtransition.cpp gui/qbasicmouseeventtransition_p.h
+ gui/qkeyeventtransition.cpp gui/qkeyeventtransition.h
+ gui/qmouseeventtransition.cpp gui/qmouseeventtransition.h
+ qeventtransition.cpp qeventtransition.h qeventtransition_p.h
+ LIBRARIES
+ Qt::GuiPrivate
+ PUBLIC_LIBRARIES
+ Qt::Gui
+ PRIVATE_MODULE_INTERFACE
+ Qt::GuiPrivate
+)
+
+qt_internal_add_docs(StateMachine
+ doc/qtstatemachine.qdocconf
+)
diff --git a/src/statemachine/configure.cmake b/src/statemachine/configure.cmake
new file mode 100644
index 0000000..c6cda82
--- /dev/null
+++ b/src/statemachine/configure.cmake
@@ -0,0 +1,29 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+
+#### Inputs
+
+
+
+#### Libraries
+
+
+
+#### Tests
+
+
+
+#### Features
+
+qt_feature("statemachine" PUBLIC
+ SECTION "Utilities"
+ LABEL "State machine"
+ PURPOSE "Provides hierarchical finite state machines."
+)
+qt_feature_definition("statemachine" "QT_NO_STATEMACHINE" NEGATE VALUE "1")
+qt_feature("qeventtransition" PUBLIC
+ LABEL "QEventTransition class"
+ CONDITION QT_FEATURE_statemachine AND TARGET Qt::Gui # special case
+)
diff --git a/src/statemachine/doc/images/animations-architecture.png b/src/statemachine/doc/images/animations-architecture.png
new file mode 100644
index 0000000..9b581af
--- /dev/null
+++ b/src/statemachine/doc/images/animations-architecture.png
Binary files differ
diff --git a/src/statemachine/doc/images/move-blocks-chart.png b/src/statemachine/doc/images/move-blocks-chart.png
new file mode 100644
index 0000000..fd0c165
--- /dev/null
+++ b/src/statemachine/doc/images/move-blocks-chart.png
Binary files differ
diff --git a/src/statemachine/doc/images/rogue-statechart.png b/src/statemachine/doc/images/rogue-statechart.png
new file mode 100644
index 0000000..c5f4048
--- /dev/null
+++ b/src/statemachine/doc/images/rogue-statechart.png
Binary files differ
diff --git a/src/statemachine/doc/images/statemachine-button-history.png b/src/statemachine/doc/images/statemachine-button-history.png
new file mode 100644
index 0000000..7f51cae
--- /dev/null
+++ b/src/statemachine/doc/images/statemachine-button-history.png
Binary files differ
diff --git a/src/statemachine/doc/images/statemachine-button-nested.png b/src/statemachine/doc/images/statemachine-button-nested.png
new file mode 100644
index 0000000..762ac14
--- /dev/null
+++ b/src/statemachine/doc/images/statemachine-button-nested.png
Binary files differ
diff --git a/src/statemachine/doc/images/statemachine-button.png b/src/statemachine/doc/images/statemachine-button.png
new file mode 100644
index 0000000..10102bd
--- /dev/null
+++ b/src/statemachine/doc/images/statemachine-button.png
Binary files differ
diff --git a/src/statemachine/doc/images/statemachine-customevents.png b/src/statemachine/doc/images/statemachine-customevents.png
new file mode 100644
index 0000000..62a4222
--- /dev/null
+++ b/src/statemachine/doc/images/statemachine-customevents.png
Binary files differ
diff --git a/src/statemachine/doc/images/statemachine-customevents2.png b/src/statemachine/doc/images/statemachine-customevents2.png
new file mode 100644
index 0000000..57b37ef
--- /dev/null
+++ b/src/statemachine/doc/images/statemachine-customevents2.png
Binary files differ
diff --git a/src/statemachine/doc/images/statemachine-examples.png b/src/statemachine/doc/images/statemachine-examples.png
new file mode 100644
index 0000000..b2ec66e
--- /dev/null
+++ b/src/statemachine/doc/images/statemachine-examples.png
Binary files differ
diff --git a/src/statemachine/doc/images/statemachine-finished.png b/src/statemachine/doc/images/statemachine-finished.png
new file mode 100644
index 0000000..0ac081d
--- /dev/null
+++ b/src/statemachine/doc/images/statemachine-finished.png
Binary files differ
diff --git a/src/statemachine/doc/images/statemachine-nonparallel.png b/src/statemachine/doc/images/statemachine-nonparallel.png
new file mode 100644
index 0000000..f9850a7
--- /dev/null
+++ b/src/statemachine/doc/images/statemachine-nonparallel.png
Binary files differ
diff --git a/src/statemachine/doc/images/statemachine-parallel.png b/src/statemachine/doc/images/statemachine-parallel.png
new file mode 100644
index 0000000..a65c297
--- /dev/null
+++ b/src/statemachine/doc/images/statemachine-parallel.png
Binary files differ
diff --git a/src/statemachine/doc/qstatemachine-qml-guide.qdoc b/src/statemachine/doc/qstatemachine-qml-guide.qdoc
new file mode 100644
index 0000000..deb9d94
--- /dev/null
+++ b/src/statemachine/doc/qstatemachine-qml-guide.qdoc
@@ -0,0 +1,293 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qmlstatemachine-qml-guide.html
+ \title Qt State Machine QML Guide
+ \brief Overview of the Qt State Machine QML for constructing and executing state graphs.
+ \ingroup technology-apis
+ \ingroup explanation
+
+ \tableofcontents
+
+ Qt State Machine QML APIs provide types for creating and
+ executing state graphs in QML. It is similar to the C++ State Machine
+ framework based on Harel's
+ \l{Statecharts: A visual formalism for complex systems}, which
+ is also the basis for UML state diagrams. Like its
+ \l{Qt State Machine C++ Classes}{C++ counterpart}, the framework provides an
+ API and execution model based on \l{State Chart XML: State Machine Notation for
+ Control Abstraction}{State Chart XML (SCXML)}
+ to embed the elements and semantics of statecharts in QML applications.
+
+ For user interfaces with multiple visual states, independent of the
+ application's logical state, consider using QML States and Transitions.
+
+ For the full list of QML types provided by the framework to create event-driven
+ state machines, see: \l {Qt State Machine QML Types}
+
+ \section1 Using Both QtQuick and QtQml.StateMachine Imports
+
+ \warning If you're attempting to import both \l{QtQuick} and
+ \e{QtQml.StateMachine} in one single QML file, make sure to import
+ \e{QtQml.StateMachine} \e{last}. This way, the \e{State} type is provided
+ by the Declarative State Machine Framework and not by \l{QtQuick}:
+
+ \qml
+ import QtQuick
+ import QtQml.StateMachine
+
+ StateMachine {
+ State {
+ // okay, is of type QtQml.StateMachine.State
+ }
+ }
+ \endqml
+
+ Alternatively, you can import \e{QtQml.StateMachine} into a separate
+ namespace to avoid any ambiguity with QtQuick's \e{State} item:
+
+ \qml \QtMinorVersion
+ import QtQuick
+ import QtQml.StateMachine as DSM
+
+ DSM.StateMachine {
+ DSM.State {
+ // ...
+ }
+ }
+ \endqml
+
+ \section1 A Simple State Machine
+
+ To demonstrate the core functionality of the State Machine API, let's look
+ at an example: A state machine with three states, \c s1, \c s2 and \c
+ s3. The state machine is controlled by a single Button; when the button
+ is clicked, the machine transitions to another state. Initially, the state
+ machine is in state \c s1. The following is a state chart showing the
+ different states in our example.
+
+ \image statemachine-button.png
+
+ The following snippet shows the code needed to create such a state machine.
+
+ \snippet qml/statemachine/statemachine-button.qml 0
+
+ The state machine runs asynchronously to become part of your application's
+ event loop.
+
+ \section1 State Machines That Finish
+
+ The state machine defined in the previous section never finishes. In order
+ for a state machine to be able to finish, it needs to have a top-level \e
+ final state (FinalState object). When the state machine enters the top-level
+ final state, the machine emits the \l{State::finished}{finished}
+ signal and halts.
+
+ All you need to do to introduce a final state in the graph is create a
+ FinalState object and use it as the target of one or more transitions.
+
+ \section1 Sharing Transitions
+
+ Assume we wanted the user to be able to quit the application at any time by
+ clicking a Quit button. In order to achieve this, we need to create a final
+ state and make it the target of a transition associated with the Quit
+ button's \e clicked() signal. We could add a transition for each state;
+ however, this seems redundant and one would also have to
+ remember to add such a transition from every new state that is added in the
+ future.
+
+ We can achieve the same behavior (namely that clicking the Quit button quits
+ the state machine, regardless of which state the state machine is in) by
+ grouping states \c s1, \c s2 and \c s3. This is done by creating a new
+ top-level state and making the three original states children of the new
+ state. The following diagram shows the new state machine.
+
+ \image statemachine-button-nested.png
+
+ The three original states have been renamed \c s11, \c s12 and \c s13 to
+ reflect that they are now childrens of the new top-level state, \c s1. Child
+ states implicitly inherit the transitions of their parent state. This means
+ it is now sufficient to add a single transition from \c s1 to the final
+ state, \c s2. New states added to \c s1 will automatically inherit this
+ transition.
+
+ All that's needed to group states is to specify the proper parent when the
+ state is created. You also need to specify which of the child states is the
+ initial one (the child state the state machine should enter when the
+ parent state is the target of a transition).
+
+ \snippet qml/statemachine/statemachine-button-nested.qml 0
+
+ In this case we want the application to quit when the state machine is
+ finished, so the machine's \e finished() signal is connected to the
+ application's \e quit() slot.
+
+ A child state can override an inherited transition. For example, the
+ following code adds a transition that effectively causes the Quit button to
+ be ignored when the state machine is in state, \c s12.
+
+ \snippet qml/statemachine/statemachine-button-nested-ignore-quit.qml 0
+
+ A transition can have any state as its target irrespective of where the
+ target state is in the state hierarchy.
+
+ \section1 Using History States
+
+ Imagine that we wanted to add an "interrupt" mechanism to the example
+ discussed in the previous section; the user should be able to click a button
+ to have the state machine perform some non-related task, after which the
+ state machine should resume whatever it was doing before (i.e. return to the
+ old state, which is one of the three states in this case).
+
+ Such behavior can easily be modeled using \e{history states}. A history
+ state (HistoryState object) is a pseudo-state that represents the child
+ state that the parent state was in before it exited last.
+
+ A history state is created as a child of the state for which we wish to
+ record the current child state; when the state machine detects the presence
+ of such a state at runtime, it automatically records the current (real)
+ child state when the parent state exits. A transition to the history
+ state is in fact a transition to the child state that the state machine had
+ previously saved; the state machine automatically "forwards" the transition
+ to the real child state.
+
+ The following diagram shows the state machine after the interrupt mechanism
+ has been added.
+
+ \image statemachine-button-history.png
+
+ The following code shows how it can be implemented; in this example we
+ simply display a message box when \c s3 is entered, then immediately return
+ to the previous child state of \c s1 via the history state.
+
+ \snippet qml/statemachine/statemachine-button-history.qml 0
+
+ \section1 Using Parallel States
+
+ Assume that you wanted to model a set of mutually exclusive properties of a
+ car in a single state machine. Let's say the properties we are interested in
+ are Clean vs Dirty, and Moving vs Not moving. It would take four mutually
+ exclusive states and eight transitions to represent the states and freely
+ move between all possible combinations as shown in the following state chart.
+
+ \image statemachine-nonparallel.png
+
+ If we added a third property (say, Red vs Blue), the total number of states
+ would double, to eight; and if we added a fourth property (say, Enclosed vs
+ Convertible), the total number of states would double again, to 16.
+
+ This exponential increase can be reduced using parallel states, which enables
+ linear growth in the number of states and transitions as we add more
+ properties. Furthermore, states can be added to or removed from the parallel
+ state without affecting any of their sibling states. The following state
+ chart shows the different paralles states for the car example.
+
+ \image statemachine-parallel.png
+
+ To create a parallel state group, set childMode to QState.ParallelStates.
+
+ \qml
+ State {
+ id: s1
+ childMode: QState.ParallelStates
+ State {
+ id: s11
+ }
+ State {
+ id: s12
+ }
+ }
+ \endqml
+
+ When a parallel state group is entered, all its child states will be
+ simultaneously entered. Transitions within the individual child states
+ operate normally. However, any of the child states may take a transition
+ which exits the parent state. When this happens, the parent state and all of
+ its child states are exited.
+
+ The parallelism in the State Machine framework follows an interleaved
+ semantics. All parallel operations will be executed in a single, atomic step
+ of the event processing, so no event can interrupt the parallel operations.
+ However, events will still be processed sequentially, as the machine itself
+ is single threaded. For example, consider the situation where there are two
+ transitions that exit the same parallel state group, and their conditions
+ become true simultaneously. In this case, the event that is processed last
+ of the two will not have any effect.
+
+ \section1 Exiting a Composite State
+
+ A child state can be final (a FinalState object); when a final child state
+ is entered, the parent state emits the State::finished signal. The
+ following diagram shows a composite state \c s1 which does some processing
+ before entering a final state:
+
+ \image statemachine-finished.png
+
+ When \c s1 's final state is entered, \c s1 will automatically emit
+ \l{State::finished}{finished}. We use a signal transition to cause this event to
+ trigger a state change:
+
+ \qml
+ State {
+ id: s1
+ SignalTransition {
+ targetState: s2
+ signal: s1.finished
+ }
+ }
+ \endqml
+
+ Using final states in composite states is useful when you want to hide the
+ internal details of a composite state. The outside world should be able to
+ enter the state and get a notification when the state has completed its work,
+ without the need to know the internal details. This is a very powerful
+ abstraction and encapsulation mechanism when building complex (deeply nested)
+ state machines. (In the above example, you could of course create a transition
+ directly from \c s1 's \c done state rather than relying on \c s1 's
+ finished() signal, but with the consequence that implementation details of
+ \c s1 are exposed and depended on).
+
+ For parallel state groups, the State::finished signal is emitted when \e
+ all the child states have entered final states.
+
+ \section1 Targetless Transitions
+
+ A transition need not have a target state. A transition without a target can
+ be triggered the same way as any other transition; the difference is that
+ it doesn't cause any state changes. This allows you to react to a signal or
+ event when your machine is in a certain state, without having to leave that
+ state. For example:
+
+ \qml
+ Button {
+ id: button
+ text: "button"
+ StateMachine {
+ id: stateMachine
+ initialState: s1
+ running: true
+ State {
+ id: s1
+ SignalTransition {
+ signal: button.clicked
+ onTriggered: console.log("button pressed")
+ }
+ }
+ }
+ }
+ \endqml
+
+ The "button pressed" message will be displayed each time the button is clicked, but the
+ state machine will remain in its current state (s1). If the target state
+ were explicitly set to s1, s1 would be exited and re-entered each
+ time (the QAbstractState::entered and QAbstractState::exited
+ signals would be emitted).
+
+ \section1 Related Information
+
+ \list
+ \li \l{Qt State Machine QML Types}
+ \li \l{Qt State Machine Overview}
+ \endlist
+*/
diff --git a/src/statemachine/doc/qt6-changes.qdoc b/src/statemachine/doc/qt6-changes.qdoc
new file mode 100644
index 0000000..c7f6efe
--- /dev/null
+++ b/src/statemachine/doc/qt6-changes.qdoc
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qtstatemachine-changes-qt6.html
+ \title Changes to Qt State Machine
+ \ingroup changes-qt-5-to-6
+ \brief Migrate Qt State Machine to Qt 6.
+
+ Qt 6 is a result of the conscious effort to make the framework more
+ efficient and easy to use.
+
+ We try to maintain binary and source compatibility for all the public
+ APIs in each release. But some changes were inevitable in an effort to
+ make Qt a better framework.
+
+ In this topic we summarize those changes in Qt State Machine module, and provide
+ guidance to handle them.
+
+ \section1 Changes
+
+ The Qt StateMachine module is generally speaking source compatible with the Qt5
+ version and users of the library should be able to continue with no or
+ minor changes to their project.
+
+ \section2 QSignalTransition
+
+ The \l [CPP] QSignalTransition::senderObject() getter and the related
+ Q_PROPERTY now also use const QObject*. These are now better aligned
+ with the setter \l [CPP] QSignalTransition::setSenderObject(const QObject*) that takes a
+ const QObject* as a parameter.
+
+ \section1 Build system
+
+ As with Qt 6 in general, the Qt State Machine module has cmake support in addition
+ to qmake.
+
+ \section1 QML imports
+
+ The QML import versioning is optional unless one has a specific
+ reason for not using the latest. Generally speaking the versioned imports
+ work from version 5.8 to 6.x, where 'x' is the current minor release.
+*/
diff --git a/src/statemachine/doc/qtstatemachine-cpp-guide.qdoc b/src/statemachine/doc/qtstatemachine-cpp-guide.qdoc
new file mode 100644
index 0000000..a898ba4
--- /dev/null
+++ b/src/statemachine/doc/qtstatemachine-cpp-guide.qdoc
@@ -0,0 +1,511 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qtstatemachine-cpp-guide.html
+ \title Qt State Machine C++ Guide
+ \brief An overview of the State Machine framework for constructing and
+ executing state graphs with the C++ API.
+ \ingroup technology-apis
+ \ingroup explanation
+
+ \tableofcontents
+
+ The State Machine framework provides classes for creating and executing
+ state graphs. This page illustrates the framework's key features in C++.
+
+ \sa {Qt State Machine Overview}
+ \sa {Qt State Machine QML Guide}
+
+ \section1 C++ Classes in the State Machine Framework
+
+ For the full list of C++ classes in the State Machine framework see
+ \l {Qt State Machine C++ Classes}
+
+ \section1 A Simple State Machine
+
+ To demonstrate the core functionality of the State Machine API, let's look
+ at a small example: A state machine with three states, \c s1, \c s2 and \c
+ s3. The state machine is controlled by a single QPushButton; when the button
+ is clicked, the machine transitions to another state. Initially, the state
+ machine is in state \c s1. The statechart for this machine is as follows:
+
+ \image statemachine-button.png
+
+ The following snippet shows the code needed to create such a state machine.
+ First, we create the state machine and states:
+
+ \snippet statemachine/main.cpp 0
+
+ Then, we create the transitions by using the QState::addTransition()
+ function:
+
+ \snippet statemachine/main.cpp 1
+
+ Next, we add the states to the machine and set the machine's initial state:
+
+ \snippet statemachine/main.cpp 2
+
+ Finally, we start the state machine:
+
+ \snippet statemachine/main.cpp 3
+
+ The state machine executes asynchronously, i.e. it becomes part of your
+ application's event loop.
+
+ \section1 Doing Useful Work on State Entry and Exit
+
+ The above state machine merely transitions from one state to another, it
+ doesn't perform any operations. The QState::assignProperty() function can be
+ used to have a state set a property of a QObject when the state is
+ entered. In the following snippet, the value that should be assigned to a
+ QLabel's text property is specified for each state:
+
+ \snippet statemachine/main.cpp 4
+
+ When any of the states is entered, the label's text will be changed
+ accordingly.
+
+ The QState::entered() signal is emitted when the state is entered, and the
+ QState::exited() signal is emitted when the state is exited. In the
+ following snippet, the button's \l {QPushButton::}{showMaximized()} slot
+ will be called when state \c s3 is entered, and the button's \l {QPushButton::}{showMinimized()}
+ slot will be called when \c s3 is exited:
+
+ \snippet statemachine/main.cpp 5
+
+ Custom states can reimplement QAbstractState::onEntry() and
+ QAbstractState::onExit().
+
+ \section1 State Machines That Finish
+
+ The state machine defined in the previous section never finishes. In order
+ for a state machine to be able to finish, it needs to have a top-level \e
+ final state (QFinalState object). When the state machine enters a top-level
+ final state, the machine will emit the QStateMachine::finished() signal and
+ halt.
+
+ All you need to do to introduce a final state in the graph is create a
+ QFinalState object and use it as the target of one or more transitions.
+
+ \section1 Sharing Transitions By Grouping States
+
+ Assume we wanted the user to be able to quit the application at any time by
+ clicking a Quit button. In order to achieve this, we need to create a final
+ state and make it the target of a transition associated with the Quit
+ button's \l{QPushButton::}{clicked()} signal. We could add a transition from each of \c s1, \c
+ s2 and \c s3; however, this seems redundant, and one would also have to
+ remember to add such a transition from every new state that is added in the
+ future.
+
+ We can achieve the same behavior (namely that clicking the Quit button quits
+ the state machine, regardless of which state the state machine is in) by
+ grouping states \c s1, \c s2 and \c s3. This is done by creating a new
+ top-level state and making the three original states children of the new
+ state. The following diagram shows the new state machine.
+
+ \image statemachine-button-nested.png
+
+ The three original states have been renamed \c s11, \c s12 and \c s13 to
+ reflect that they are now children of the new top-level state, \c s1. Child
+ states implicitly inherit the transitions of their parent state. This means
+ it is now sufficient to add a single transition from \c s1 to the final
+ state \c s2. New states added to \c s1 will also automatically inherit this
+ transition.
+
+ All that's needed to group states is to specify the proper parent when the
+ state is created. You also need to specify which of the child states is the
+ initial one (i.e. which child state the state machine should enter when the
+ parent state is the target of a transition).
+
+ \snippet statemachine/main2.cpp 0
+
+ \snippet statemachine/main2.cpp 1
+
+ In this case we want the application to quit when the state machine is
+ finished, so the machine's \l {QStateMachine::}{finished()} signal is connected to the
+ application's \l {QCoreApplication::}{quit()} slot.
+
+ A child state can override an inherited transition. For example, the
+ following code adds a transition that effectively causes the Quit button to
+ be ignored when the state machine is in state \c s12.
+
+ \snippet statemachine/main2.cpp 2
+
+ A transition can have any state as its target, i.e. the target state does
+ not have to be on the same level in the state hierarchy as the source state.
+
+ \section1 Using History States to Save and Restore the Current State
+
+ Imagine that we wanted to add an "interrupt" mechanism to the example
+ discussed in the previous section; the user should be able to click a button
+ to have the state machine perform some non-related task, after which the
+ state machine should resume whatever it was doing before (i.e. return to the
+ old state, which is one of \c s11, \c s12 and \c s13 in this case).
+
+ Such behavior can easily be modeled using \e{history states}. A history
+ state (QHistoryState object) is a pseudo-state that represents the child
+ state that the parent state was in the last time the parent state was
+ exited.
+
+ A history state is created as a child of the state for which we wish to
+ record the current child state; when the state machine detects the presence
+ of such a state at runtime, it automatically records the current (real)
+ child state when the parent state is exited. A transition to the history
+ state is in fact a transition to the child state that the state machine had
+ previously saved; the state machine automatically "forwards" the transition
+ to the real child state.
+
+ The following diagram shows the state machine after the interrupt mechanism
+ has been added.
+
+ \image statemachine-button-history.png
+
+ The following code shows how it can be implemented; in this example we
+ simply display a message box when \c s3 is entered, then immediately return
+ to the previous child state of \c s1 via the history state.
+
+ \snippet statemachine/main2.cpp 3
+
+ \section1 Using Parallel States to Avoid a Combinatorial Explosion of States
+
+ Assume that you wanted to model a set of mutually exclusive properties of a
+ car in a single state machine. Let's say the properties we are interested in
+ are Clean vs Dirty, and Moving vs Not moving. It would take four mutually
+ exclusive states and eight transitions to be able to represent and freely
+ move between all possible combinations.
+
+ \image statemachine-nonparallel.png
+
+ If we added a third property (say, Red vs Blue), the total number of states
+ would double, to eight; and if we added a fourth property (say, Enclosed vs
+ Convertible), the total number of states would double again, to 16.
+
+ Using parallel states, the total number of states and transitions grows
+ linearly as we add more properties, instead of exponentially. Furthermore,
+ states can be added to or removed from the parallel state without affecting
+ any of their sibling states.
+
+ \image statemachine-parallel.png
+
+ To create a parallel state group, pass QState::ParallelStates to the QState
+ constructor.
+
+ \snippet statemachine/main3.cpp 0
+
+ When a parallel state group is entered, all its child states will be
+ simultaneously entered. Transitions within the individual child states
+ operate normally. However, any of the child states may take a transition which exits the parent
+ state. When this happens, the parent state and all of its child states are exited.
+
+ The parallelism in the State Machine framework follows an interleaved semantics. All parallel
+ operations will be executed in a single, atomic step of the event processing, so no event can
+ interrupt the parallel operations. However, events will still be processed sequentially, since
+ the machine itself is single threaded. As an example: Consider the situation where there are two
+ transitions that exit the same parallel state group, and their conditions become true
+ simultaneously. In this case, the event that is processed last of the two will not have any
+ effect, since the first event will already have caused the machine to exit from the parallel
+ state.
+
+ \section1 Detecting that a Composite State has Finished
+
+ A child state can be final (a QFinalState object); when a final child state
+ is entered, the parent state emits the QState::finished() signal. The
+ following diagram shows a composite state \c s1 which does some processing
+ before entering a final state:
+
+ \image statemachine-finished.png
+
+ When \c s1 's final state is entered, \c s1 will automatically emit
+ \l {QState::}{finished()}. We use a signal transition to cause this event to trigger a
+ state change:
+
+ \snippet statemachine/main3.cpp 1
+
+ Using final states in composite states is useful when you want to hide the
+ internal details of a composite state; i.e. the only thing the outside world
+ should be able to do is enter the state, and get a notification when the
+ state has completed its work. This is a very powerful abstraction and
+ encapsulation mechanism when building complex (deeply nested) state
+ machines. (In the above example, you could of course create a transition
+ directly from \c s1 's \c done state rather than relying on \c s1 's
+ \l {QState::}{finished()} signal, but with the consequence that implementation details of
+ \c s1 are exposed and depended on).
+
+ For parallel state groups, the QState::finished() signal is emitted when \e
+ all the child states have entered final states.
+
+ \section1 Targetless Transitions
+
+ A transition need not have a target state. A transition without a target can
+ be triggered the same way as any other transition; the difference is that
+ when a targetless transition is triggered, it doesn't cause any state
+ changes. This allows you to react to a signal or event when your machine is
+ in a certain state, without having to leave that state. Example:
+
+ \code
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+
+ QPushButton button;
+ QSignalTransition *trans = new QSignalTransition(&button, &QPushButton::clicked);
+ s1->addTransition(trans);
+
+ QMessageBox msgBox;
+ msgBox.setText("The button was clicked; carry on.");
+ QObject::connect(trans, QSignalTransition::triggered, &msgBox, &QMessageBox::exec);
+
+ machine.setInitialState(s1);
+ \endcode
+
+ The message box will be displayed each time the button is clicked, but the
+ state machine will remain in its current state (s1). If the target state
+ were explicitly set to s1, however, s1 would be exited and re-entered each
+ time (e.g. the QAbstractState::entered() and QAbstractState::exited()
+ signals would be emitted).
+
+ \section1 Events, Transitions and Guards
+
+ A QStateMachine runs its own event loop. For signal transitions
+ (QSignalTransition objects), QStateMachine automatically posts a
+ QStateMachine::SignalEvent to itself when it intercepts the corresponding
+ signal; similarly, for QObject event transitions (QEventTransition objects)
+ a QStateMachine::WrappedEvent is posted.
+
+ You can post your own events to the state machine using
+ QStateMachine::postEvent().
+
+ When posting a custom event to the state machine, you typically also have
+ one or more custom transitions that can be triggered from events of that
+ type. To create such a transition, you subclass QAbstractTransition and
+ reimplement \l [CPP] {QAbstractTransition::}{eventTest()}, where you check if an event
+ matches your event type (and optionally other criteria, e.g. attributes of
+ the event object).
+
+ Here we define our own custom event type, \c StringEvent, for posting
+ strings to the state machine:
+
+ \snippet statemachine/main4.cpp 0
+
+ Next, we define a transition that only triggers when the event's string
+ matches a particular string (a \e guarded transition):
+
+ \snippet statemachine/main4.cpp 1
+
+ In the \l [CPP] {QAbstractTransition::}{eventTest()} reimplementation, we first check if the event type is the
+ desired one; if so, we cast the event to a \c StringEvent and perform the
+ string comparison.
+
+ The following is a statechart that uses the custom event and transition:
+
+ \image statemachine-customevents.png
+
+ Here's what the implementation of the statechart looks like:
+
+ \snippet statemachine/main4.cpp 2
+
+ Once the machine is started, we can post events to it.
+
+ \snippet statemachine/main4.cpp 3
+
+ An event that is not handled by any relevant transition will be silently
+ consumed by the state machine. It can be useful to group states and provide
+ a default handling of such events; for example, as illustrated in the
+ following statechart:
+
+ \image statemachine-customevents2.png
+
+ For deeply nested statecharts, you can add such "fallback" transitions at
+ the level of granularity that's most appropriate.
+
+ \section1 Using Restore Policy To Automatically Restore Properties
+
+ In some state machines it can be useful to focus the attention on assigning properties in states,
+ not on restoring them when the state is no longer active. If you know that a property should
+ always be restored to its initial value when the machine enters a state that does not explicitly
+ give the property a value, you can set the global restore policy to
+ QStateMachine::RestoreProperties.
+
+ \code
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);
+ \endcode
+
+ When this restore policy is set, the machine will automatically restore all properties. If it
+ enters a state where a given property is not set, it will first search the hierarchy of ancestors
+ to see if the property is defined there. If it is, the property will be restored to the value
+ defined by the closest ancestor. If not, it will be restored to its initial value (i.e. the
+ value of the property before any property assignments in states were executed.)
+
+ Take the following code:
+
+ \snippet statemachine/main5.cpp 0
+
+ Lets say the property \c fooBar is 0.0 when the machine starts. When the machine is in state
+ \c s1, the property will be 1.0, since the state explicitly assigns this value to it. When the
+ machine is in state \c s2, no value is explicitly defined for the property, so it will implicitly
+ be restored to 0.0.
+
+ If we are using nested states, the parent defines a value for the property which is inherited by
+ all descendants that do not explicitly assign a value to the property.
+
+ \snippet statemachine/main5.cpp 2
+
+ Here \c s1 has two children: \c s2 and \c s3. When \c s2 is entered, the property \c fooBar
+ will have the value 2.0, since this is explicitly defined for the state. When the machine is in
+ state \c s3, no value is defined for the state, but \c s1 defines the property to be 1.0, so this
+ is the value that will be assigned to \c fooBar.
+
+ \section1 Animations and States Machines
+
+ The State Machine API connects with the \l {The Animation Framework} to allow automatically
+ animating properties as they are assigned in states.
+
+ The state machine provides a special state that can play an animation.
+ A QState can also set properties when the state is entered or exited, and
+ this special animation state will interpolate between these values when given a
+ QPropertyAnimation.
+
+ We can associate one or more animations to a transition between states
+ using a QSignalTransition or QEventTransition class. These classes
+ are both derived from QAbstractTransition, which defines the
+ convenience function \l [CPP] {QAbstractTransition::}{addAnimation()} that
+ enables the appending of one or more animations triggered when the
+ transition occurs.
+
+ We also have the possibility to associate properties with the
+ states rather than setting the start and end values ourselves.
+
+ Say we have the following code:
+
+ \snippet statemachine/main5.cpp 3
+
+ Here we define two states of a user interface. In \c s1 the \c button is small, and in \c s2
+ it is bigger. If we click the button to transition from \c s1 to \c s2, the geometry of the button
+ will be set immediately when a given state has been entered. If we want the transition to be
+ smooth, however, all we need to do is make a QPropertyAnimation and add this to the transition
+ object.
+
+ \snippet statemachine/main5.cpp 4
+
+ Adding an animation for the property in question means that the property assignment will no
+ longer take immediate effect when the state has been entered. Instead, the animation will start
+ playing when the state has been entered and smoothly animate the property assignment. Since we
+ do not set the start value or end value of the animation, these will be set implicitly. The
+ start value of the animation will be the property's current value when the animation starts, and
+ the end value will be set based on the property assignments defined for the state.
+
+ If the global restore policy of the state machine is set to QStateMachine::RestoreProperties,
+ it is possible to also add animations for the property restorations.
+
+ \section1 Detecting That All Properties Have Been Set In A State
+
+ When animations are used to assign properties, a state no longer defines the exact values that a
+ property will have when the machine is in the given state. While the animation is running, the
+ property can potentially have any value, depending on the animation.
+
+ In some cases, it can be useful to be able to detect when the property has actually been assigned
+ the value defined by a state.
+
+ Say we have the following code:
+
+ \snippet statemachine/main5.cpp 5
+
+ When \c button is clicked, the machine will transition into state \c s2, which will set the
+ geometry of the button, and then pop up a message box to alert the user that the geometry has
+ been changed.
+
+ In the normal case, where animations are not used, this will operate as expected. However, if
+ an animation for the \c geometry of \c button is set on the transition between \c s1 and \c s2,
+ the animation will be started when \c s2 is entered, but the \c geometry property will not
+ actually reach its defined value before the animation is finished running. In this case, the
+ message box will pop up before the geometry of the button has actually been set.
+
+ To ensure that the message box does not pop up until the geometry actually reaches its final
+ value, we can use the state's \l {QState::}{propertiesAssigned()} signal.
+ The \l {QState::}{propertiesAssigned()} signal will be emitted when the property is assigned
+ its final value, whether this is done immediately or after the animation has finished playing.
+
+ \snippet statemachine/main5.cpp 6
+
+ In this example, when \c button is clicked, the machine will enter \c s2. It will remain in state
+ \c s2 until the \c geometry property has been set to \c QRect(0, 0, 50, 50). Then it will
+ transition into \c s3. When \c s3 is entered, the message box will pop up. If the transition into
+ \c s2 has an animation for the \c geometry property, then the machine will stay in \c s2 until the
+ animation has finished playing. If there is no such animation, it will simply set the property and
+ immediately enter state \c s3.
+
+ Either way, when the machine is in state \c s3, you are guaranteed that the property \c geometry
+ has been assigned the defined value.
+
+ If the global restore policy is set to QStateMachine::RestoreProperties, the state will not emit
+ the \l {QState::}{propertiesAssigned()} signal until these have been executed as well.
+
+ \section1 What Happens If A State Is Exited Before The Animation Has Finished
+
+ If a state has property assignments, and the transition into the state has animations for the
+ properties, the state can potentially be exited before the properties have been assigned to the
+ values defines by the state. This is true in particular when there are transitions out from the
+ state that do not depend on the \l {QState::}{propertiesAssigned()} signal, as described in the previous section.
+
+ The State Machine API guarantees that a property assigned by the state machine either:
+ \list
+ \li Has a value explicitly assigned to the property.
+ \li Is currently being animated into a value explicitly assigned to the property.
+ \endlist
+
+ When a state is exited prior to the animation finishing, the behavior of the state machine depends
+ on the target state of the transition. If the target state explicitly assigns a value to the
+ property, no additional action will be taken. The property will be assigned the value defined by
+ the target state.
+
+ If the target state does not assign any value to the property, there are two
+ options: By default, the property will be assigned the value defined by the state it is leaving
+ (the value it would have been assigned if the animation had been permitted to finish playing). If
+ a global restore policy is set, however, this will take precedence, and the property will be
+ restored as usual.
+
+ \section1 Default Animations
+
+ As described earlier, you can add animations to transitions to make sure property assignments
+ in the target state are animated. If you want a specific animation to be used for a given property
+ regardless of which transition is taken, you can add it as a default animation to the state
+ machine. This is in particular useful when the properties assigned (or restored) by specific
+ states is not known when the machine is constructed.
+
+ \code
+ QState *s1 = new QState();
+ QState *s2 = new QState();
+
+ s2->assignProperty(object, "fooBar", 2.0);
+ s1->addTransition(s2);
+
+ QStateMachine machine;
+ machine.setInitialState(s1);
+ machine.addDefaultAnimation(new QPropertyAnimation(object, "fooBar"));
+ \endcode
+
+ When the machine is in state \c s2, the machine will play the default animation for the
+ property \c fooBar since this property is assigned by \c s2.
+
+ Note that animations explicitly set on transitions will take precedence over any default
+ animation for the given property.
+
+ \section1 Nesting State Machines
+
+ QStateMachine is a subclass of QState. This allows for a state machine to be a child state of
+ another machine. QStateMachine reimplements QState::onEntry() and calls QStateMachine::start(),
+ so that when the child state machine is entered, it will automatically start running.
+
+ The parent state machine treats the child machine as an \e atomic state in the state machine
+ algorithm. The child state machine is self-contained; it maintains its own event queue and
+ configuration. In particular, note that the \l{QStateMachine::}{configuration()}
+ of the child machine is not part of the parent machine's configuration (only the child machine
+ itself is).
+
+ States of the child state machine cannot be specified as targets of transitions in the parent
+ state machine; only the child state machine itself can. Conversely, states of the parent state
+ machine cannot be specified as targets of transitions in the child state machine. The child
+ state machine's \l{QState::}{finished}() signal can be used to trigger a transition
+ in the parent machine.
+*/
diff --git a/src/statemachine/doc/qtstatemachine-examples.qdoc b/src/statemachine/doc/qtstatemachine-examples.qdoc
new file mode 100644
index 0000000..7794b6e
--- /dev/null
+++ b/src/statemachine/doc/qtstatemachine-examples.qdoc
@@ -0,0 +1,12 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\group examples-qtstatemachine
+\title Qt State Machine Examples
+\brief Examples for the Qt State Machine module.
+
+The Qt State Machine example applications demonstrate the functionality provided by the
+\l{Qt State Machine} module.
+
+*/
diff --git a/src/statemachine/doc/qtstatemachine-index.qdoc b/src/statemachine/doc/qtstatemachine-index.qdoc
new file mode 100644
index 0000000..0557efa
--- /dev/null
+++ b/src/statemachine/doc/qtstatemachine-index.qdoc
@@ -0,0 +1,68 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qtstatemachine-index.html
+ \title Qt State Machine
+ \brief Provides functionality to create and execute state graphs.
+
+ The State Machine framework provides classes for creating and executing
+ state graphs.
+
+ \section1 Using the Module
+
+ \section2 QML API
+
+ \include {module-use.qdocinc} {using the qml api} {QtQml.StateMachine}
+
+ \section2 C++ API
+
+ \include {module-use.qdocinc} {using the c++ api}
+
+ \section3 Building with CMake
+
+ \include {module-use.qdocinc} {building with cmake} {StateMachine}
+
+ \section3 Building with qmake
+
+ \include {module-use.qdocinc} {building_with_qmake} {statemachine}
+
+ \section1 Articles and Guides
+
+ \list
+ \li \l {Qt State Machine Overview}
+ \li \l {Qt State Machine C++ Guide}
+ \li \l {Qt State Machine QML Guide}
+ \endlist
+
+ \section1 Examples
+
+ \list
+ \li \l {Qt State Machine Examples}
+ \endlist
+
+ \section1 Reference
+
+ \list
+ \li \l {Qt State Machine C++ Classes} {C++ Classes and Namespaces}
+ \li \l {Qt State Machine QML Types} {QML Types}
+ \endlist
+
+ \section1 Module Evolution
+ \l{Changes to Qt State Machine} lists important changes in the module API
+ and functionality that were done for the Qt 6 series of Qt.
+
+ \section1 Licenses and Trademarks
+
+ The Qt State Machine module is available under commercial licenses from
+ \l{The Qt Company}. In addition, it is available under free software licenses:
+ The \l{GNU Lesser General Public License, version 3}, or
+ the \l{GNU General Public License, version 2}.
+ See \l{Qt Licensing} for further details.
+
+ Furthermore, Qt State Machine in Qt \QtVersion may contain third party
+ modules under following permissive licenses:
+
+ \generatelist{groupsbymodule attributions-qtstatemachine}
+
+*/
diff --git a/src/statemachine/doc/qtstatemachine-module-cpp.qdoc b/src/statemachine/doc/qtstatemachine-module-cpp.qdoc
new file mode 100644
index 0000000..fc81267
--- /dev/null
+++ b/src/statemachine/doc/qtstatemachine-module-cpp.qdoc
@@ -0,0 +1,16 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \module QtStateMachine
+ \title Qt State Machine C++ Classes
+ \ingroup modules
+ \ingroup technology-apis
+ \qtvariable statemachine
+ \qtcmakepackage StateMachine
+
+ \brief The Qt State Machine module provides classes for creating and executing state graphs.
+
+ The \l {Qt State Machine} page contains information about how to use the module.
+
+*/
diff --git a/src/statemachine/doc/qtstatemachine-module-qml.qdoc b/src/statemachine/doc/qtstatemachine-module-qml.qdoc
new file mode 100644
index 0000000..d9f84bf
--- /dev/null
+++ b/src/statemachine/doc/qtstatemachine-module-qml.qdoc
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \qmlmodule QtQml.StateMachine 6.\QtMinorVersion
+ \title Qt State Machine QML Types
+ \ingroup qmlmodules
+ \brief Enables the use of State Machine with QML.
+
+ To import the QML types into your application, use the following import statement
+ in your .qml file:
+
+ \qml
+ import QtQml.StateMachine
+ \endqml
+
+ For more information, see \l {Qt State Machine QML Guide}.
+ */
diff --git a/src/statemachine/doc/qtstatemachine-module-use.qdocinc b/src/statemachine/doc/qtstatemachine-module-use.qdocinc
new file mode 100644
index 0000000..fb3101d
--- /dev/null
+++ b/src/statemachine/doc/qtstatemachine-module-use.qdocinc
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+//! [cmakebuild]
+ \code
+ find_package(Qt6 REQUIRED COMPONENTS StateMachine)
+ target_link_libraries(mytarget PRIVATE Qt6::StateMachine)
+ \endcode
+//! [cmakebuild]
+
+//! [qmakebuild]
+ \code
+ QT += statemachine
+ \endcode
+//! [qmakebuild]
diff --git a/src/statemachine/doc/qtstatemachine-overview.qdoc b/src/statemachine/doc/qtstatemachine-overview.qdoc
new file mode 100644
index 0000000..3438d86
--- /dev/null
+++ b/src/statemachine/doc/qtstatemachine-overview.qdoc
@@ -0,0 +1,42 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qtstatemachine-overview.html
+ \title Qt State Machine Overview
+ \brief An overview of the State Machine framework for constructing and executing state graphs.
+ \ingroup technology-apis
+ \ingroup explanation
+
+ \tableofcontents
+
+ The State Machine framework provides classes for creating and executing
+ state graphs. The concepts and notation are based on those from Harel's
+ \l{http://www.wisdom.weizmann.ac.il/~dharel/SCANNED.PAPERS/Statecharts.pdf}{Statecharts: A visual formalism for complex systems}, which
+ is also the basis of UML state diagrams. The semantics of state machine
+ execution are based on \l{State Chart XML: State Machine Notation for
+ Control Abstraction}{State Chart XML (SCXML)}.
+
+ Statecharts provide a graphical way of modeling how a system reacts to
+ stimuli. This is done by defining the possible \e states that the system can
+ be in, and how the system can move from one state to another (\e transitions
+ between states). A key characteristic of event-driven systems (such as Qt
+ applications) is that behavior often depends not only on the last or current
+ event, but also the events that preceded it. With statecharts, this
+ information is easy to express.
+
+ The State Machine framework provides an API and execution model that can be
+ used to effectively embed the elements and semantics of statecharts in Qt
+ applications. The framework integrates tightly with Qt's meta-object system;
+ for example, transitions between states can be triggered by signals, and
+ states can be configured to set properties and invoke methods on {QObject}s.
+ Qt's event system is used to drive the state machines.
+
+ The state graph in the State Machine framework is hierarchical. States can be nested inside of
+ other states, and the current configuration of the state machine consists of the set of states
+ which are currently active. All the states in a valid configuration of the state machine will
+ have a common ancestor.
+
+ \sa {Qt State Machine C++ Guide}, {Qt State Machine QML Guide}, {Qt SCXML Overview}
+
+ */
diff --git a/src/statemachine/doc/qtstatemachine.qdocconf b/src/statemachine/doc/qtstatemachine.qdocconf
new file mode 100644
index 0000000..7736501
--- /dev/null
+++ b/src/statemachine/doc/qtstatemachine.qdocconf
@@ -0,0 +1,55 @@
+include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
+include($QT_INSTALL_DOCS/config/exampleurl-qtscxml.qdocconf)
+
+project = QtStateMachine
+description = Qt State Machine Reference Documentation
+version = $QT_VERSION
+
+imagedirs = images src/images
+
+# Install path for the examples
+examplesinstallpath = statemachine
+exampledirs = ../../../examples/statemachine \
+ snippets
+
+qhp.QtStateMachine.subprojects = classes qmltypes examples
+qhp.QtStateMachine.subprojects.classes.title = C++ Classes
+qhp.QtStateMachine.subprojects.classes.indexTitle = Qt State Machine C++ Classes
+qhp.QtStateMachine.subprojects.classes.selectors = class headerfile
+qhp.QtStateMachine.subprojects.classes.sortPages = true
+qhp.QtStateMachine.subprojects.qmltypes.title = QML Types
+qhp.QtStateMachine.subprojects.qmltypes.indexTitle = Qt State Machine QML Types
+qhp.QtStateMachine.subprojects.qmltypes.selectors = qmltype
+qhp.QtStateMachine.subprojects.qmltypes.sortPages = true
+qhp.QtStateMachine.subprojects.examples.title = Examples
+qhp.QtStateMachine.subprojects.examples.indexTitle = Qt State Machine Examples
+qhp.QtStateMachine.subprojects.examples.selectors = doc:example
+
+qhp.projects = QtStateMachine
+
+qhp.QtStateMachine.file = qtstatemachine.qhp
+qhp.QtStateMachine.namespace = org.qt-project.qtstatemachine.$QT_VERSION_TAG
+qhp.QtStateMachine.virtualFolder = qtstatemachine
+qhp.QtStateMachine.indexTitle = Qt StateMachine
+qhp.QtStateMachine.indexRoot =
+
+depends += qtcore qtdoc qmake qtquick qtwidgets qtgui qtqml qtscxml qtcmake
+
+headerdirs = .. \
+ ../gui \
+ ../../statemachineqml \
+ ../../../examples/statemachine
+
+sourcedirs += .. \
+ ../gui \
+ ../../statemachineqml \
+ ../../../examples/statemachine
+
+tagfile = qtstatemachine.tags
+
+navigation.landingpage = "Qt State Machine"
+navigation.cppclassespage = "Qt State Machine C++ Classes"
+navigation.qmltypespage = "Qt State Machine QML Types"
+
+# Highlighted examples in Data Processing & IO category
+manifestmeta.highlighted.names = "QtStateMachine/Traffic Light"
diff --git a/src/statemachine/doc/snippets/code/src_corelib_statemachine_qstatemachine.cpp b/src/statemachine/doc/snippets/code/src_corelib_statemachine_qstatemachine.cpp
new file mode 100644
index 0000000..4c9a0a5
--- /dev/null
+++ b/src/statemachine/doc/snippets/code/src_corelib_statemachine_qstatemachine.cpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [simple state machine]
+QPushButton button;
+
+QStateMachine machine;
+QState *s1 = new QState();
+s1->assignProperty(&button, "text", "Click me");
+
+QFinalState *s2 = new QFinalState();
+s1->addTransition(&button, &QPushButton::clicked, s2);
+
+machine.addState(s1);
+machine.addState(s2);
+machine.setInitialState(s1);
+machine.start();
+//! [simple state machine]
diff --git a/src/statemachine/doc/snippets/qml/statemachine/Button.qml b/src/statemachine/doc/snippets/qml/statemachine/Button.qml
new file mode 100644
index 0000000..b968a18
--- /dev/null
+++ b/src/statemachine/doc/snippets/qml/statemachine/Button.qml
@@ -0,0 +1,51 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import QtQuick.Window
+
+Item {
+ id: container
+
+ property alias text: buttonLabel.text
+ property alias label: buttonLabel
+ signal clicked
+ property alias containsMouse: mouseArea.containsMouse
+ property alias pressed: mouseArea.pressed
+ implicitHeight: Math.max(Screen.pixelDensity * 7, buttonLabel.implicitHeight * 1.2)
+ implicitWidth: Math.max(Screen.pixelDensity * 11, buttonLabel.implicitWidth * 1.3)
+ height: implicitHeight
+ width: implicitWidth
+ property bool checkable: false
+ property bool checked: false
+
+ SystemPalette { id: palette }
+
+ Rectangle {
+ id: frame
+ anchors.fill: parent
+ color: palette.button
+ gradient: Gradient {
+ GradientStop { position: 0.0; color: mouseArea.pressed ? Qt.darker(palette.button, 1.3) : palette.button }
+ GradientStop { position: 1.0; color: Qt.darker(palette.button, 1.3) }
+ }
+ antialiasing: true
+ radius: height / 6
+ border.color: Qt.darker(palette.button, 1.5)
+ border.width: 1
+ }
+
+ MouseArea {
+ id: mouseArea
+ anchors.fill: parent
+ onClicked: container.clicked()
+ hoverEnabled: true
+ }
+
+ Text {
+ id: buttonLabel
+ text: container.text
+ color: palette.buttonText
+ anchors.centerIn: parent
+ }
+}
diff --git a/src/statemachine/doc/snippets/qml/statemachine/basicstate.qml b/src/statemachine/doc/snippets/qml/statemachine/basicstate.qml
new file mode 100644
index 0000000..dac2914
--- /dev/null
+++ b/src/statemachine/doc/snippets/qml/statemachine/basicstate.qml
@@ -0,0 +1,18 @@
+// Copyright (C) 2014 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [document]
+import QtQuick
+import QtQml.StateMachine as DSM
+
+Rectangle {
+ DSM.StateMachine {
+ id: stateMachine
+ initialState: state
+ running: true
+ DSM.State {
+ id: state
+ }
+ }
+}
+//! [document]
diff --git a/src/statemachine/doc/snippets/qml/statemachine/finalstate.qml b/src/statemachine/doc/snippets/qml/statemachine/finalstate.qml
new file mode 100644
index 0000000..160419a
--- /dev/null
+++ b/src/statemachine/doc/snippets/qml/statemachine/finalstate.qml
@@ -0,0 +1,26 @@
+// Copyright (C) 2014 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [document]
+import QtQuick
+import QtQml.StateMachine as DSM
+
+Rectangle {
+ DSM.StateMachine {
+ id: stateMachine
+ initialState: state
+ running: true
+ DSM.State {
+ id: state
+ DSM.TimeoutTransition {
+ targetState: finalState
+ timeout: 200
+ }
+ }
+ DSM.FinalState {
+ id: finalState
+ }
+ onFinished: console.log("state finished")
+ }
+}
+//! [document]
diff --git a/src/statemachine/doc/snippets/qml/statemachine/guardcondition.qml b/src/statemachine/doc/snippets/qml/statemachine/guardcondition.qml
new file mode 100644
index 0000000..9bacea3
--- /dev/null
+++ b/src/statemachine/doc/snippets/qml/statemachine/guardcondition.qml
@@ -0,0 +1,31 @@
+// Copyright (C) 2014 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [document]
+import QtQuick
+import QtQml.StateMachine as DSM
+
+Rectangle {
+ Button {
+ anchors.fill: parent
+ id: button
+ DSM.StateMachine {
+ DSM.State {
+ DSM.SignalTransition {
+ targetState: finalState
+ signal: button.mysignal
+ // the guard condition uses the mystr string argument from mysignal
+ guard: mystr == "test"
+ }
+ }
+ DSM.FinalState {
+ id: finalState
+ }
+ }
+ // define the signal the SignalTransition is connected with
+ signal mysignal(mystr: string)
+ // on clicking the button emit the signal with a single string argument
+ onClicked: button.mysignal("test")
+ }
+}
+//! [document]
diff --git a/src/statemachine/doc/snippets/qml/statemachine/historystate.qml b/src/statemachine/doc/snippets/qml/statemachine/historystate.qml
new file mode 100644
index 0000000..e3e7a43
--- /dev/null
+++ b/src/statemachine/doc/snippets/qml/statemachine/historystate.qml
@@ -0,0 +1,48 @@
+// Copyright (C) 2014 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [document]
+import QtQuick
+import QtQml.StateMachine as DSM
+
+Rectangle {
+ Button {
+ anchors.fill: parent
+ id: button
+ text: "Press me"
+ DSM.StateMachine {
+ id: stateMachine
+ initialState: parentState
+ running: true
+ DSM.State {
+ id: parentState
+ initialState: child2
+ onEntered: console.log("parentState entered")
+ onExited: console.log("parentState exited")
+ DSM.State {
+ id: child1
+ onEntered: console.log("child1 entered")
+ onExited: console.log("child1 exited")
+ }
+ DSM.State {
+ id: child2
+ onEntered: console.log("child2 entered")
+ onExited: console.log("child2 exited")
+ }
+ DSM.HistoryState {
+ id: historyState
+ defaultState: child1
+ }
+ DSM.SignalTransition {
+ targetState: historyState
+
+ // Clicking the button will cause the state machine to enter the child state
+ // that parentState was in the last time parentState was exited, or the history state's default
+ // state if parentState has never been entered.
+ signal: button.clicked
+ }
+ }
+ }
+ }
+}
+//! [document]
diff --git a/src/statemachine/doc/snippets/qml/statemachine/signaltransition.qml b/src/statemachine/doc/snippets/qml/statemachine/signaltransition.qml
new file mode 100644
index 0000000..3d7b4b0
--- /dev/null
+++ b/src/statemachine/doc/snippets/qml/statemachine/signaltransition.qml
@@ -0,0 +1,40 @@
+// Copyright (C) 2014 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [document]
+import QtQuick
+import QtQml.StateMachine as DSM
+
+Rectangle {
+ DSM.StateMachine {
+ id: stateMachine
+ initialState: state
+ running: true
+ DSM.State {
+ id: state
+ DSM.SignalTransition {
+ targetState: finalState
+ signal: button.clicked
+ guard: guardButton.checked
+ }
+ }
+ DSM.FinalState {
+ id: finalState
+ }
+ onFinished: Qt.quit()
+ }
+ Row {
+ spacing: 2
+ Button {
+ id: button
+ text: "Finish state"
+ }
+
+ Button {
+ id: guardButton
+ checkable: true
+ text: checked ? "Press to block the SignalTransition" : "Press to unblock the SignalTransition"
+ }
+ }
+}
+//! [document]
diff --git a/src/statemachine/doc/snippets/qml/statemachine/signaltransitionsignal.qml b/src/statemachine/doc/snippets/qml/statemachine/signaltransitionsignal.qml
new file mode 100644
index 0000000..2aeae2a
--- /dev/null
+++ b/src/statemachine/doc/snippets/qml/statemachine/signaltransitionsignal.qml
@@ -0,0 +1,25 @@
+// Copyright (C) 2014 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [document]
+import QtQuick
+import QtQml.StateMachine as DSM
+
+Rectangle {
+ Button {
+ anchors.fill: parent
+ id: button
+ DSM.StateMachine {
+ DSM.State {
+ DSM.SignalTransition {
+ targetState: finalState
+ signal: button.clicked
+ }
+ }
+ DSM.FinalState {
+ id: finalState
+ }
+ }
+ }
+}
+//! [document]
diff --git a/src/statemachine/doc/snippets/qml/statemachine/simplestatemachine.qml b/src/statemachine/doc/snippets/qml/statemachine/simplestatemachine.qml
new file mode 100644
index 0000000..cd7e01e
--- /dev/null
+++ b/src/statemachine/doc/snippets/qml/statemachine/simplestatemachine.qml
@@ -0,0 +1,31 @@
+// Copyright (C) 2014 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [document]
+import QtQuick
+import QtQml.StateMachine as DSM
+
+Rectangle {
+ Button {
+ anchors.fill: parent
+ id: button
+ text: "Finish state"
+ DSM.StateMachine {
+ id: stateMachine
+ initialState: state
+ running: true
+ DSM.State {
+ id: state
+ DSM.SignalTransition {
+ targetState: finalState
+ signal: button.clicked
+ }
+ }
+ DSM.FinalState {
+ id: finalState
+ }
+ onFinished: Qt.quit()
+ }
+ }
+}
+//! [document]
diff --git a/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-history.qml b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-history.qml
new file mode 100644
index 0000000..e5947ae
--- /dev/null
+++ b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-history.qml
@@ -0,0 +1,112 @@
+// Copyright (C) 2014 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [document]
+import QtQuick
+import QtQml.StateMachine
+
+Rectangle {
+//![0]
+ Row {
+ anchors.fill: parent
+ spacing: 2
+ Button {
+ id: button
+ // change the button label to the active state id
+ text: s11.active ? "s11" : s12.active ? "s12" : s13.active ? "s13" : "s3"
+ }
+ Button {
+ id: interruptButton
+ text: s1.active ? "Interrupt" : "Resume"
+ }
+ Button {
+ id: quitButton
+ text: "quit"
+ }
+ }
+
+ StateMachine {
+ id: stateMachine
+ // set the initial state
+ initialState: s1
+
+ // start the state machine
+ running: true
+
+ State {
+ id: s1
+ // set the initial state
+ initialState: s11
+
+ // create a transition from s1 to s2 when the button is clicked
+ SignalTransition {
+ targetState: s2
+ signal: quitButton.clicked
+ }
+ // do something when the state enters/exits
+ onEntered: console.log("s1 entered")
+ onExited: console.log("s1 exited")
+ State {
+ id: s11
+ // create a transition from s1 to s2 when the button is clicked
+ SignalTransition {
+ targetState: s12
+ signal: button.clicked
+ }
+ // do something when the state enters/exits
+ onEntered: console.log("s11 entered")
+ onExited: console.log("s11 exited")
+ }
+
+ State {
+ id: s12
+ // create a transition from s2 to s3 when the button is clicked
+ SignalTransition {
+ targetState: s13
+ signal: button.clicked
+ }
+ // do something when the state enters/exits
+ onEntered: console.log("s12 entered")
+ onExited: console.log("s12 exited")
+ }
+ State {
+ id: s13
+ // create a transition from s3 to s1 when the button is clicked
+ SignalTransition {
+ targetState: s1
+ signal: button.clicked
+ }
+ // do something when the state enters/exits
+ onEntered: console.log("s13 entered")
+ onExited: console.log("s13 exited")
+ }
+
+ // create a transition from s1 to s3 when the button is clicked
+ SignalTransition {
+ targetState: s3
+ signal: interruptButton.clicked
+ }
+ HistoryState {
+ id: s1h
+ }
+ }
+ FinalState {
+ id: s2
+ onEntered: console.log("s2 entered")
+ onExited: console.log("s2 exited")
+ }
+ State {
+ id: s3
+ SignalTransition {
+ targetState: s1h
+ signal: interruptButton.clicked
+ }
+ // do something when the state enters/exits
+ onEntered: console.log("s3 entered")
+ onExited: console.log("s3 exited")
+ }
+ onFinished: Qt.quit()
+ }
+//![0]
+}
+//! [document]
diff --git a/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-nested-ignore-quit.qml b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-nested-ignore-quit.qml
new file mode 100644
index 0000000..db40f79
--- /dev/null
+++ b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-nested-ignore-quit.qml
@@ -0,0 +1,97 @@
+// Copyright (C) 2014 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [document]
+import QtQuick
+import QtQml.StateMachine
+
+Rectangle {
+ Row {
+ anchors.fill: parent
+ spacing: 2
+ Button {
+ id: button
+ // change the button label to the active state id
+ text: s11.active ? "s11" : s12.active ? "s12" : "s13"
+ }
+ Button {
+ id: quitButton
+ text: "quit"
+ }
+ }
+
+ StateMachine {
+ id: stateMachine
+ // set the initial state
+ initialState: s1
+
+ // start the state machine
+ running: true
+
+ State {
+ id: s1
+ // set the initial state
+ initialState: s11
+
+ // create a transition from s1 to s2 when the button is clicked
+ SignalTransition {
+ targetState: s2
+ signal: quitButton.clicked
+ }
+ // do something when the state enters/exits
+ onEntered: console.log("s1 entered")
+ onExited: console.log("s1 exited")
+ State {
+ id: s11
+ // create a transition from s11 to s12 when the button is clicked
+ SignalTransition {
+ targetState: s12
+ signal: button.clicked
+ }
+ // do something when the state enters/exits
+ onEntered: console.log("s11 entered")
+ onExited: console.log("s11 exited")
+ }
+
+//![0]
+ State {
+ id: s12
+ // create a transition from s12 to s13 when the button is clicked
+ SignalTransition {
+ targetState: s13
+ signal: button.clicked
+ }
+
+ // ignore Quit button when we are in state 12
+ SignalTransition {
+ targetState: s12
+ signal: quitButton.clicked
+ }
+
+ // do something when the state enters/exits
+ onEntered: console.log("s12 entered")
+ onExited: console.log("s12 exited")
+ }
+//![0]
+
+ State {
+ id: s13
+ // create a transition from s13 to s11 when the button is clicked
+ SignalTransition {
+ targetState: s11
+ signal: button.clicked
+ }
+ // do something when the state enters/exits
+ onEntered: console.log("s13 entered")
+ onExited: console.log("s13 exited")
+ }
+ }
+ FinalState {
+ id: s2
+ onEntered: console.log("s2 entered")
+ onExited: console.log("s2 exited")
+ }
+ onFinished: Qt.quit()
+ }
+}
+//! [document]
diff --git a/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-nested.qml b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-nested.qml
new file mode 100644
index 0000000..eae7427
--- /dev/null
+++ b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button-nested.qml
@@ -0,0 +1,89 @@
+// Copyright (C) 2014 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [document]
+import QtQuick
+import QtQml.StateMachine
+
+Rectangle {
+//![0]
+ Row {
+ anchors.fill: parent
+ spacing: 2
+ Button {
+ id: button
+ // change the button label to the active state id
+ text: s11.active ? "s11" : s12.active ? "s12" : "s13"
+ }
+ Button {
+ id: quitButton
+ text: "quit"
+ }
+ }
+
+ StateMachine {
+ id: stateMachine
+ // set the initial state
+ initialState: s1
+
+ // start the state machine
+ running: true
+
+ State {
+ id: s1
+ // set the initial state
+ initialState: s11
+
+ // create a transition from s1 to s2 when the button is clicked
+ SignalTransition {
+ targetState: s2
+ signal: quitButton.clicked
+ }
+ // do something when the state enters/exits
+ onEntered: console.log("s1 entered")
+ onExited: console.log("s1 exited")
+ State {
+ id: s11
+ // create a transition from s11 to s12 when the button is clicked
+ SignalTransition {
+ targetState: s12
+ signal: button.clicked
+ }
+ // do something when the state enters/exits
+ onEntered: console.log("s11 entered")
+ onExited: console.log("s11 exited")
+ }
+
+ State {
+ id: s12
+ // create a transition from s12 to s13 when the button is clicked
+ SignalTransition {
+ targetState: s13
+ signal: button.clicked
+ }
+ // do something when the state enters/exits
+ onEntered: console.log("s12 entered")
+ onExited: console.log("s12 exited")
+ }
+ State {
+ id: s13
+ // create a transition from s13 to s11 when the button is clicked
+ SignalTransition {
+ targetState: s11
+ signal: button.clicked
+ }
+ // do something when the state enters/exits
+ onEntered: console.log("s13 entered")
+ onExited: console.log("s13 exited")
+ }
+ }
+ FinalState {
+ id: s2
+ onEntered: console.log("s2 entered")
+ onExited: console.log("s2 exited")
+ }
+ onFinished: Qt.quit()
+ }
+//![0]
+}
+//! [document]
diff --git a/src/statemachine/doc/snippets/qml/statemachine/statemachine-button.qml b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button.qml
new file mode 100644
index 0000000..8873117
--- /dev/null
+++ b/src/statemachine/doc/snippets/qml/statemachine/statemachine-button.qml
@@ -0,0 +1,63 @@
+// Copyright (C) 2014 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [document]
+import QtQuick
+import QtQml.StateMachine
+
+Rectangle {
+//![0]
+ Button {
+ anchors.fill: parent
+ id: button
+
+ // change the button label to the active state id
+ text: s1.active ? "s1" : s2.active ? "s2" : "s3"
+ }
+
+ StateMachine {
+ id: stateMachine
+ // set the initial state
+ initialState: s1
+
+ // start the state machine
+ running: true
+
+ State {
+ id: s1
+ // create a transition from s1 to s2 when the button is clicked
+ SignalTransition {
+ targetState: s2
+ signal: button.clicked
+ }
+ // do something when the state enters/exits
+ onEntered: console.log("s1 entered")
+ onExited: console.log("s1 exited")
+ }
+
+ State {
+ id: s2
+ // create a transition from s2 to s3 when the button is clicked
+ SignalTransition {
+ targetState: s3
+ signal: button.clicked
+ }
+ // do something when the state enters/exits
+ onEntered: console.log("s2 entered")
+ onExited: console.log("s2 exited")
+ }
+ State {
+ id: s3
+ // create a transition from s3 to s1 when the button is clicked
+ SignalTransition {
+ targetState: s1
+ signal: button.clicked
+ }
+ // do something when the state enters/exits
+ onEntered: console.log("s3 entered")
+ onExited: console.log("s3 exited")
+ }
+ }
+//![0]
+}
+//! [document]
diff --git a/src/statemachine/doc/snippets/qml/statemachine/timeouttransition.qml b/src/statemachine/doc/snippets/qml/statemachine/timeouttransition.qml
new file mode 100644
index 0000000..60bd420
--- /dev/null
+++ b/src/statemachine/doc/snippets/qml/statemachine/timeouttransition.qml
@@ -0,0 +1,32 @@
+// Copyright (C) 2014 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [document]
+import QtQuick
+import QtQml.StateMachine as DSM
+
+Rectangle {
+ Button {
+ anchors.fill: parent
+ id: button
+ text: "Finish state"
+ enabled: !stateMachine.running
+ onClicked: stateMachine.running = true
+ DSM.StateMachine {
+ id: stateMachine
+ initialState: state
+ running: true
+ DSM.State {
+ id: state
+ DSM.TimeoutTransition {
+ targetState: finalState
+ timeout: 1000
+ }
+ }
+ DSM.FinalState {
+ id: finalState
+ }
+ }
+ }
+}
+//! [document]
diff --git a/src/statemachine/doc/snippets/statemachine/eventtest.cpp b/src/statemachine/doc/snippets/statemachine/eventtest.cpp
new file mode 100644
index 0000000..14cb48b
--- /dev/null
+++ b/src/statemachine/doc/snippets/statemachine/eventtest.cpp
@@ -0,0 +1,37 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtGui>
+#include <QtStateMachine>
+
+class MyTransition : public QAbstractTransition
+{
+ Q_OBJECT
+public:
+ MyTransition() {}
+
+protected:
+//![0]
+ bool eventTest(QEvent *event) override
+ {
+ if (event->type() == QEvent::Wrapped) {
+ QEvent *wrappedEvent = static_cast<QStateMachine::WrappedEvent *>(event)->event();
+ if (wrappedEvent->type() == QEvent::KeyPress) {
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(wrappedEvent);
+ // Do your event test
+ }
+ }
+ return false;
+ }
+//![0]
+
+ void onTransition(QEvent *event) override
+ {
+
+ }
+};
+
+int main(int argv, char **args)
+{
+ return 0;
+}
diff --git a/src/statemachine/doc/snippets/statemachine/main.cpp b/src/statemachine/doc/snippets/statemachine/main.cpp
new file mode 100644
index 0000000..3cbeb5d
--- /dev/null
+++ b/src/statemachine/doc/snippets/statemachine/main.cpp
@@ -0,0 +1,52 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtStateMachine>
+#include <QtWidgets>
+
+int main(int argv, char **args)
+{
+ QApplication app(argv, args);
+
+ QLabel *label = new QLabel;
+ QPushButton *button = new QPushButton;
+
+//![0]
+ QStateMachine machine;
+ QState *s1 = new QState();
+ QState *s2 = new QState();
+ QState *s3 = new QState();
+//![0]
+
+//![4]
+ s1->assignProperty(label, "text", "In state s1");
+ s2->assignProperty(label, "text", "In state s2");
+ s3->assignProperty(label, "text", "In state s3");
+//![4]
+
+//![5]
+ QObject::connect(s3, &QState::entered, button, &QPushButton:showMaximized);
+ QObject::connect(s3, &QState::exited, button, &QPushButton::showMinimized);
+//![5]
+
+//![1]
+ s1->addTransition(button, &QPushButton::clicked, s2);
+ s2->addTransition(button, &QPushButton::clicked, s3);
+ s3->addTransition(button, &QPushButton::clicked, s1);
+//![1]
+
+//![2]
+ machine.addState(s1);
+ machine.addState(s2);
+ machine.addState(s3);
+ machine.setInitialState(s1);
+//![2]
+
+//![3]
+ machine.start();
+//![3]
+
+ label->show();
+
+ return app.exec();
+}
diff --git a/src/statemachine/doc/snippets/statemachine/main2.cpp b/src/statemachine/doc/snippets/statemachine/main2.cpp
new file mode 100644
index 0000000..4fe4c19
--- /dev/null
+++ b/src/statemachine/doc/snippets/statemachine/main2.cpp
@@ -0,0 +1,57 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtStateMachine>
+#include <QtWidgets>
+
+int main(int argv, char **args)
+{
+ QApplication app(argv, args);
+
+ QStateMachine machine;
+
+//![0]
+ QState *s1 = new QState();
+ QState *s11 = new QState(s1);
+ QState *s12 = new QState(s1);
+ QState *s13 = new QState(s1);
+ s1->setInitialState(s11);
+ machine.addState(s1);
+//![0]
+
+//![2]
+ s12->addTransition(quitButton, &QPushButton::clicked, s12);
+//![2]
+
+//![1]
+ QFinalState *s2 = new QFinalState();
+ s1->addTransition(quitButton, &QPushButton::clicked, s2);
+ machine.addState(s2);
+ machine.setInitialState(s1);
+
+ QObject::connect(&machine, &QStateMachine::finished,
+ QCoreApplication::instance(), &QCoreApplication::quit);
+//![1]
+
+ QButton *interruptButton = new QPushButton("Interrupt Button");
+ QWidget *mainWindow = new QWidget();
+
+//![3]
+ QHistoryState *s1h = new QHistoryState(s1);
+
+ QState *s3 = new QState();
+ s3->assignProperty(label, "text", "In s3");
+ QMessageBox *mbox = new QMessageBox(mainWindow);
+ mbox->addButton(QMessageBox::Ok);
+ mbox->setText("Interrupted!");
+ mbox->setIcon(QMessageBox::Information);
+ QObject::connect(s3, &QState::entered, mbox, &QMessageBox::exec);
+ s3->addTransition(s1h);
+ machine.addState(s3);
+
+ s1->addTransition(interruptButton, &QPushButton::clicked, s3);
+//![3]
+
+ return app.exec();
+}
+
diff --git a/src/statemachine/doc/snippets/statemachine/main3.cpp b/src/statemachine/doc/snippets/statemachine/main3.cpp
new file mode 100644
index 0000000..7bae11f
--- /dev/null
+++ b/src/statemachine/doc/snippets/statemachine/main3.cpp
@@ -0,0 +1,24 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtGui>
+#include <QtStateMachine>
+
+int main(int argv, char **args)
+{
+ QApplication app(argv, args);
+
+//![0]
+ QState *s1 = new QState(QState::ParallelStates);
+ // s11 and s12 will be entered in parallel
+ QState *s11 = new QState(s1);
+ QState *s12 = new QState(s1);
+//![0]
+
+//![1]
+ s1->addTransition(s1, &QState::finished, s2);
+//![1]
+
+ return app.exec();
+}
+
diff --git a/src/statemachine/doc/snippets/statemachine/main4.cpp b/src/statemachine/doc/snippets/statemachine/main4.cpp
new file mode 100644
index 0000000..bac255a
--- /dev/null
+++ b/src/statemachine/doc/snippets/statemachine/main4.cpp
@@ -0,0 +1,75 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtGui>
+#include <QtStateMachine>
+
+//![0]
+struct StringEvent : public QEvent
+{
+ StringEvent(const QString &val)
+ : QEvent(QEvent::Type(QEvent::User+1)),
+ value(val) {}
+
+ QString value;
+};
+//![0]
+
+//![1]
+class StringTransition : public QAbstractTransition
+{
+ Q_OBJECT
+
+public:
+ StringTransition(const QString &value)
+ : m_value(value) {}
+
+protected:
+ bool eventTest(QEvent *e) override
+ {
+ if (e->type() != QEvent::Type(QEvent::User+1)) // StringEvent
+ return false;
+ StringEvent *se = static_cast<StringEvent*>(e);
+ return (m_value == se->value);
+ }
+
+ void onTransition(QEvent *) override {}
+
+private:
+ QString m_value;
+};
+//![1]
+
+int main(int argv, char **args)
+{
+ QApplication app(argv, args);
+
+//![2]
+ QStateMachine machine;
+ QState *s1 = new QState();
+ QState *s2 = new QState();
+ QFinalState *done = new QFinalState();
+
+ StringTransition *t1 = new StringTransition("Hello");
+ t1->setTargetState(s2);
+ s1->addTransition(t1);
+ StringTransition *t2 = new StringTransition("world");
+ t2->setTargetState(done);
+ s2->addTransition(t2);
+
+ machine.addState(s1);
+ machine.addState(s2);
+ machine.addState(done);
+ machine.setInitialState(s1);
+//![2]
+
+//![3]
+ machine.postEvent(new StringEvent("Hello"));
+ machine.postEvent(new StringEvent("world"));
+//![3]
+
+ return app.exec();
+}
+
+#include "main4.moc"
+
diff --git a/src/statemachine/doc/snippets/statemachine/main5.cpp b/src/statemachine/doc/snippets/statemachine/main5.cpp
new file mode 100644
index 0000000..3b1b7e9
--- /dev/null
+++ b/src/statemachine/doc/snippets/statemachine/main5.cpp
@@ -0,0 +1,136 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtGui>
+#include <QtStateMachine>
+
+int main(int argv, char **args)
+{
+ QApplication app(argv, args);
+ QWidget *button;
+
+ {
+//![0]
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);
+
+ QState *s1 = new QState();
+ s1->assignProperty(object, "fooBar", 1.0);
+ machine.addState(s1);
+ machine.setInitialState(s1);
+
+ QState *s2 = new QState();
+ machine.addState(s2);
+//![0]
+ }
+
+ {
+
+//![2]
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);
+
+ QState *s1 = new QState();
+ s1->assignProperty(object, "fooBar", 1.0);
+ machine.addState(s1);
+ machine.setInitialState(s1);
+
+ QState *s2 = new QState(s1);
+ s2->assignProperty(object, "fooBar", 2.0);
+ s1->setInitialState(s2);
+
+ QState *s3 = new QState(s1);
+//![2]
+
+ }
+
+ {
+//![3]
+ QState *s1 = new QState();
+ QState *s2 = new QState();
+
+ s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));
+ s2->assignProperty(button, "geometry", QRectF(0, 0, 100, 100));
+
+ s1->addTransition(button, &QPushButton::clicked, s2);
+//![3]
+
+ }
+
+ {
+//![4]
+ QState *s1 = new QState();
+ QState *s2 = new QState();
+
+ s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));
+ s2->assignProperty(button, "geometry", QRectF(0, 0, 100, 100));
+
+ QSignalTransition *transition = s1->addTransition(button, &QPushButton::clicked, s2);
+ transition->addAnimation(new QPropertyAnimation(button, "geometry"));
+//![4]
+
+ }
+
+ {
+ QMainWindow *mainWindow = 0;
+
+//![5]
+ QMessageBox *messageBox = new QMessageBox(mainWindow);
+ messageBox->addButton(QMessageBox::Ok);
+ messageBox->setText("Button geometry has been set!");
+ messageBox->setIcon(QMessageBox::Information);
+
+ QState *s1 = new QState();
+
+ QState *s2 = new QState();
+ s2->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));
+ connect(s2, &QState::entered, messageBox, SLOT(exec()));
+
+ s1->addTransition(button, &QPushButton::clicked, s2);
+//![5]
+ }
+
+ {
+ QMainWindow *mainWindow = 0;
+
+//![6]
+ QMessageBox *messageBox = new QMessageBox(mainWindow);
+ messageBox->addButton(QMessageBox::Ok);
+ messageBox->setText("Button geometry has been set!");
+ messageBox->setIcon(QMessageBox::Information);
+
+ QState *s1 = new QState();
+
+ QState *s2 = new QState();
+ s2->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));
+
+ QState *s3 = new QState();
+ connect(s3, &QState::entered, messageBox, SLOT(exec()));
+
+ s1->addTransition(button, &QPushButton::clicked, s2);
+ s2->addTransition(s2, &QState::propertiesAssigned, s3);
+//![6]
+
+ }
+
+ {
+
+//![7]
+ QState *s1 = new QState();
+ QState *s2 = new QState();
+
+ s2->assignProperty(object, "fooBar", 2.0);
+ s1->addTransition(s2);
+
+ QStateMachine machine;
+ machine.setInitialState(s1);
+ machine.addDefaultAnimation(new QPropertyAnimation(object, "fooBar"));
+//![7]
+
+ }
+
+
+
+ return app.exec();
+}
+
diff --git a/src/statemachine/doc/src/images/moveblocks-example.png b/src/statemachine/doc/src/images/moveblocks-example.png
new file mode 100644
index 0000000..56353d1
--- /dev/null
+++ b/src/statemachine/doc/src/images/moveblocks-example.png
Binary files differ
diff --git a/src/statemachine/doc/src/images/rogue-example.png b/src/statemachine/doc/src/images/rogue-example.png
new file mode 100644
index 0000000..7aeb0e5
--- /dev/null
+++ b/src/statemachine/doc/src/images/rogue-example.png
Binary files differ
diff --git a/src/statemachine/doc/src/images/trafficlight-example.png b/src/statemachine/doc/src/images/trafficlight-example.png
new file mode 100644
index 0000000..3431542
--- /dev/null
+++ b/src/statemachine/doc/src/images/trafficlight-example.png
Binary files differ
diff --git a/src/statemachine/gui/qbasickeyeventtransition.cpp b/src/statemachine/gui/qbasickeyeventtransition.cpp
new file mode 100644
index 0000000..cace92b
--- /dev/null
+++ b/src/statemachine/gui/qbasickeyeventtransition.cpp
@@ -0,0 +1,175 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qbasickeyeventtransition_p.h"
+
+#include <QtGui/qevent.h>
+
+#include <private/qabstracttransition_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+ \class QBasicKeyEventTransition
+ \since 4.6
+ \ingroup statemachine
+
+ \brief The QBasicKeyEventTransition class provides a transition for Qt key events.
+*/
+
+class QBasicKeyEventTransitionPrivate : public QAbstractTransitionPrivate
+{
+ Q_DECLARE_PUBLIC(QBasicKeyEventTransition)
+public:
+ QBasicKeyEventTransitionPrivate() = default;
+
+ static QBasicKeyEventTransitionPrivate *get(QBasicKeyEventTransition *q);
+
+ QEvent::Type eventType = QEvent::None;
+
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QBasicKeyEventTransitionPrivate, int, key, 0);
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QBasicKeyEventTransitionPrivate, Qt::KeyboardModifiers,
+ modifierMask, Qt::NoModifier);
+};
+
+QBasicKeyEventTransitionPrivate *QBasicKeyEventTransitionPrivate::get(QBasicKeyEventTransition *q)
+{
+ return q->d_func();
+}
+
+/*!
+ Constructs a new key event transition with the given \a sourceState.
+*/
+QBasicKeyEventTransition::QBasicKeyEventTransition(QState *sourceState)
+ : QAbstractTransition(*new QBasicKeyEventTransitionPrivate, sourceState)
+{
+}
+
+/*!
+ Constructs a new event transition for events of the given \a type for the
+ given \a key, with the given \a sourceState.
+*/
+QBasicKeyEventTransition::QBasicKeyEventTransition(QEvent::Type type, int key,
+ QState *sourceState)
+ : QAbstractTransition(*new QBasicKeyEventTransitionPrivate, sourceState)
+{
+ Q_D(QBasicKeyEventTransition);
+ d->eventType = type;
+ d->key = key;
+}
+
+/*!
+ Constructs a new event transition for events of the given \a type for the
+ given \a key, with the given \a modifierMask and \a sourceState.
+*/
+QBasicKeyEventTransition::QBasicKeyEventTransition(QEvent::Type type, int key,
+ Qt::KeyboardModifiers modifierMask,
+ QState *sourceState)
+ : QAbstractTransition(*new QBasicKeyEventTransitionPrivate, sourceState)
+{
+ Q_D(QBasicKeyEventTransition);
+ d->eventType = type;
+ d->key = key;
+ d->modifierMask = modifierMask;
+}
+
+/*!
+ Destroys this event transition.
+*/
+QBasicKeyEventTransition::~QBasicKeyEventTransition()
+{
+}
+
+/*!
+ Returns the event type that this key event transition is associated with.
+*/
+QEvent::Type QBasicKeyEventTransition::eventType() const
+{
+ Q_D(const QBasicKeyEventTransition);
+ return d->eventType;
+}
+
+/*!
+ Sets the event \a type that this key event transition is associated with.
+*/
+void QBasicKeyEventTransition::setEventType(QEvent::Type type)
+{
+ Q_D(QBasicKeyEventTransition);
+ d->eventType = type;
+}
+
+/*!
+ Returns the key that this key event transition checks for.
+*/
+int QBasicKeyEventTransition::key() const
+{
+ Q_D(const QBasicKeyEventTransition);
+ return d->key;
+}
+
+/*!
+ Sets the key that this key event transition will check for.
+*/
+void QBasicKeyEventTransition::setKey(int key)
+{
+ Q_D(QBasicKeyEventTransition);
+ d->key = key;
+}
+
+QBindable<int> QBasicKeyEventTransition::bindableKey()
+{
+ Q_D(QBasicKeyEventTransition);
+ return &d->key;
+}
+
+/*!
+ Returns the keyboard modifier mask that this key event transition checks
+ for.
+*/
+Qt::KeyboardModifiers QBasicKeyEventTransition::modifierMask() const
+{
+ Q_D(const QBasicKeyEventTransition);
+ return d->modifierMask;
+}
+
+/*!
+ Sets the keyboard modifier mask that this key event transition will check
+ for.
+*/
+void QBasicKeyEventTransition::setModifierMask(Qt::KeyboardModifiers modifierMask)
+{
+ Q_D(QBasicKeyEventTransition);
+ d->modifierMask = modifierMask;
+}
+
+QBindable<Qt::KeyboardModifiers> QBasicKeyEventTransition::bindableModifierMask()
+{
+ Q_D(QBasicKeyEventTransition);
+ return &d->modifierMask;
+}
+
+/*!
+ \reimp
+*/
+bool QBasicKeyEventTransition::eventTest(QEvent *event)
+{
+ Q_D(const QBasicKeyEventTransition);
+ if (event->type() == d->eventType) {
+ QKeyEvent *ke = static_cast<QKeyEvent*>(event);
+ return (ke->key() == d->key)
+ && ((ke->modifiers() & d->modifierMask.value()) == d->modifierMask.value());
+ }
+ return false;
+}
+
+/*!
+ \reimp
+*/
+void QBasicKeyEventTransition::onTransition(QEvent *)
+{
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qbasickeyeventtransition_p.cpp"
diff --git a/src/statemachine/gui/qbasickeyeventtransition_p.h b/src/statemachine/gui/qbasickeyeventtransition_p.h
new file mode 100644
index 0000000..ebb746c
--- /dev/null
+++ b/src/statemachine/gui/qbasickeyeventtransition_p.h
@@ -0,0 +1,60 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QBASICKEYEVENTTRANSITION_P_H
+#define QBASICKEYEVENTTRANSITION_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 <QtGui/qevent.h>
+#include <QtStateMachine/qabstracttransition.h>
+#include <QtCore/private/qglobal_p.h>
+
+QT_REQUIRE_CONFIG(qeventtransition);
+
+QT_BEGIN_NAMESPACE
+
+class QBasicKeyEventTransitionPrivate;
+class Q_AUTOTEST_EXPORT QBasicKeyEventTransition : public QAbstractTransition
+{
+ Q_OBJECT
+public:
+ QBasicKeyEventTransition(QState *sourceState = nullptr);
+ QBasicKeyEventTransition(QEvent::Type type, int key, QState *sourceState = nullptr);
+ QBasicKeyEventTransition(QEvent::Type type, int key,
+ Qt::KeyboardModifiers modifierMask,
+ QState *sourceState = nullptr);
+ ~QBasicKeyEventTransition();
+
+ QEvent::Type eventType() const;
+ void setEventType(QEvent::Type type);
+
+ int key() const;
+ void setKey(int key);
+ QBindable<int> bindableKey();
+
+ Qt::KeyboardModifiers modifierMask() const;
+ void setModifierMask(Qt::KeyboardModifiers modifiers);
+ QBindable<Qt::KeyboardModifiers> bindableModifierMask();
+
+protected:
+ bool eventTest(QEvent *event) override;
+ void onTransition(QEvent *) override;
+
+private:
+ Q_DISABLE_COPY_MOVE(QBasicKeyEventTransition)
+ Q_DECLARE_PRIVATE(QBasicKeyEventTransition)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/gui/qbasicmouseeventtransition.cpp b/src/statemachine/gui/qbasicmouseeventtransition.cpp
new file mode 100644
index 0000000..b880594
--- /dev/null
+++ b/src/statemachine/gui/qbasicmouseeventtransition.cpp
@@ -0,0 +1,182 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qbasicmouseeventtransition_p.h"
+
+#include <QtGui/qevent.h>
+#include <QtGui/qpainterpath.h>
+
+#include <private/qabstracttransition_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \internal
+ \class QBasicMouseEventTransition
+ \since 4.6
+ \ingroup statemachine
+
+ \brief The QBasicMouseEventTransition class provides a transition for Qt mouse events.
+*/
+
+class QBasicMouseEventTransitionPrivate : public QAbstractTransitionPrivate
+{
+ Q_DECLARE_PUBLIC(QBasicMouseEventTransition)
+public:
+ QBasicMouseEventTransitionPrivate() = default;
+
+ static QBasicMouseEventTransitionPrivate *get(QBasicMouseEventTransition *q);
+
+ QEvent::Type eventType = QEvent::None;
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QBasicMouseEventTransitionPrivate, Qt::MouseButton,
+ button, Qt::NoButton);
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QBasicMouseEventTransitionPrivate, Qt::KeyboardModifiers,
+ modifierMask, Qt::NoModifier);
+ QPainterPath path;
+};
+
+QBasicMouseEventTransitionPrivate *QBasicMouseEventTransitionPrivate::get(QBasicMouseEventTransition *q)
+{
+ return q->d_func();
+}
+
+/*!
+ Constructs a new mouse event transition with the given \a sourceState.
+*/
+QBasicMouseEventTransition::QBasicMouseEventTransition(QState *sourceState)
+ : QAbstractTransition(*new QBasicMouseEventTransitionPrivate, sourceState)
+{
+}
+
+/*!
+ Constructs a new mouse event transition for events of the given \a type.
+*/
+QBasicMouseEventTransition::QBasicMouseEventTransition(QEvent::Type type,
+ Qt::MouseButton button,
+ QState *sourceState)
+ : QAbstractTransition(*new QBasicMouseEventTransitionPrivate, sourceState)
+{
+ Q_D(QBasicMouseEventTransition);
+ d->eventType = type;
+ d->button = button;
+}
+
+/*!
+ Destroys this mouse event transition.
+*/
+QBasicMouseEventTransition::~QBasicMouseEventTransition()
+{
+}
+
+/*!
+ Returns the event type that this mouse event transition is associated with.
+*/
+QEvent::Type QBasicMouseEventTransition::eventType() const
+{
+ Q_D(const QBasicMouseEventTransition);
+ return d->eventType;
+}
+
+/*!
+ Sets the event \a type that this mouse event transition is associated with.
+*/
+void QBasicMouseEventTransition::setEventType(QEvent::Type type)
+{
+ Q_D(QBasicMouseEventTransition);
+ d->eventType = type;
+}
+
+/*!
+ Returns the button that this mouse event transition checks for.
+*/
+Qt::MouseButton QBasicMouseEventTransition::button() const
+{
+ Q_D(const QBasicMouseEventTransition);
+ return d->button;
+}
+
+/*!
+ Sets the button that this mouse event transition will check for.
+*/
+void QBasicMouseEventTransition::setButton(Qt::MouseButton button)
+{
+ Q_D(QBasicMouseEventTransition);
+ d->button = button;
+}
+
+QBindable<Qt::MouseButton> QBasicMouseEventTransition::bindableButton()
+{
+ Q_D(QBasicMouseEventTransition);
+ return &d->button;
+}
+
+/*!
+ Returns the keyboard modifier mask that this mouse event transition checks
+ for.
+*/
+Qt::KeyboardModifiers QBasicMouseEventTransition::modifierMask() const
+{
+ Q_D(const QBasicMouseEventTransition);
+ return d->modifierMask;
+}
+
+/*!
+ Sets the keyboard modifier mask that this mouse event transition will check
+ for.
+*/
+void QBasicMouseEventTransition::setModifierMask(Qt::KeyboardModifiers modifierMask)
+{
+ Q_D(QBasicMouseEventTransition);
+ d->modifierMask = modifierMask;
+}
+
+QBindable<Qt::KeyboardModifiers> QBasicMouseEventTransition::bindableModifierMask()
+{
+ Q_D(QBasicMouseEventTransition);
+ return &d->modifierMask;
+}
+
+
+/*!
+ Returns the hit test path for this mouse event transition.
+*/
+QPainterPath QBasicMouseEventTransition::hitTestPath() const
+{
+ Q_D(const QBasicMouseEventTransition);
+ return d->path;
+}
+
+/*!
+ Sets the hit test path for this mouse event transition.
+*/
+void QBasicMouseEventTransition::setHitTestPath(const QPainterPath &path)
+{
+ Q_D(QBasicMouseEventTransition);
+ d->path = path;
+}
+
+/*!
+ \reimp
+*/
+bool QBasicMouseEventTransition::eventTest(QEvent *event)
+{
+ Q_D(const QBasicMouseEventTransition);
+ if (event->type() == d->eventType) {
+ QMouseEvent *me = static_cast<QMouseEvent*>(event);
+ return (me->button() == d->button)
+ && ((me->modifiers() & d->modifierMask.value()) == d->modifierMask.value())
+ && (d->path.isEmpty() || d->path.contains(me->position().toPoint()));
+ }
+ return false;
+}
+
+/*!
+ \reimp
+*/
+void QBasicMouseEventTransition::onTransition(QEvent *)
+{
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qbasicmouseeventtransition_p.cpp"
diff --git a/src/statemachine/gui/qbasicmouseeventtransition_p.h b/src/statemachine/gui/qbasicmouseeventtransition_p.h
new file mode 100644
index 0000000..21d7ed3
--- /dev/null
+++ b/src/statemachine/gui/qbasicmouseeventtransition_p.h
@@ -0,0 +1,63 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QBASICMOUSEEVENTTRANSITION_P_H
+#define QBASICMOUSEEVENTTRANSITION_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 <QtGui/qevent.h>
+#include <QtStateMachine/qabstracttransition.h>
+#include <QtCore/private/qglobal_p.h>
+
+QT_REQUIRE_CONFIG(qeventtransition);
+
+QT_BEGIN_NAMESPACE
+
+class QPainterPath;
+
+class QBasicMouseEventTransitionPrivate;
+class Q_AUTOTEST_EXPORT QBasicMouseEventTransition : public QAbstractTransition
+{
+ Q_OBJECT
+public:
+ QBasicMouseEventTransition(QState *sourceState = nullptr);
+ QBasicMouseEventTransition(QEvent::Type type, Qt::MouseButton button,
+ QState *sourceState = nullptr);
+ ~QBasicMouseEventTransition();
+
+ QEvent::Type eventType() const;
+ void setEventType(QEvent::Type type);
+
+ Qt::MouseButton button() const;
+ void setButton(Qt::MouseButton button);
+ QBindable<Qt::MouseButton> bindableButton();
+
+ Qt::KeyboardModifiers modifierMask() const;
+ void setModifierMask(Qt::KeyboardModifiers modifiers);
+ QBindable<Qt::KeyboardModifiers> bindableModifierMask();
+
+ QPainterPath hitTestPath() const;
+ void setHitTestPath(const QPainterPath &path);
+
+protected:
+ bool eventTest(QEvent *event) override;
+ void onTransition(QEvent *) override;
+
+private:
+ Q_DISABLE_COPY_MOVE(QBasicMouseEventTransition)
+ Q_DECLARE_PRIVATE(QBasicMouseEventTransition)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/gui/qkeyeventtransition.cpp b/src/statemachine/gui/qkeyeventtransition.cpp
new file mode 100644
index 0000000..205e22f
--- /dev/null
+++ b/src/statemachine/gui/qkeyeventtransition.cpp
@@ -0,0 +1,151 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qkeyeventtransition.h"
+#include "qbasickeyeventtransition_p.h"
+#include "qstatemachine.h"
+
+#include <private/qeventtransition_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QKeyEventTransition
+
+ \brief The QKeyEventTransition class provides a transition for key events.
+
+ \since 4.6
+ \ingroup statemachine
+ \inmodule QtStateMachine
+
+ QKeyEventTransition is part of \l{Qt State Machine Overview}{Qt State Machine Framework}.
+
+ \sa QState::addTransition()
+*/
+
+/*!
+ \property QKeyEventTransition::key
+
+ \brief the key that this key event transition is associated with
+*/
+
+/*!
+ \property QKeyEventTransition::modifierMask
+
+ \brief the keyboard modifier mask that this key event transition checks for
+*/
+
+class QKeyEventTransitionPrivate : public QEventTransitionPrivate
+{
+ Q_DECLARE_PUBLIC(QKeyEventTransition)
+public:
+ QKeyEventTransitionPrivate() {}
+
+ QBasicKeyEventTransition *transition;
+};
+
+/*!
+ Constructs a new key event transition with the given \a sourceState.
+*/
+QKeyEventTransition::QKeyEventTransition(QState *sourceState)
+ : QEventTransition(*new QKeyEventTransitionPrivate, sourceState)
+{
+ Q_D(QKeyEventTransition);
+ d->transition = new QBasicKeyEventTransition();
+}
+
+/*!
+ Constructs a new key event transition for events of the given \a type for
+ the given \a object, with the given \a key and \a sourceState.
+*/
+QKeyEventTransition::QKeyEventTransition(QObject *object, QEvent::Type type,
+ int key, QState *sourceState)
+ : QEventTransition(*new QKeyEventTransitionPrivate, object, type, sourceState)
+{
+ Q_D(QKeyEventTransition);
+ d->transition = new QBasicKeyEventTransition(type, key);
+}
+
+/*!
+ Destroys this key event transition.
+*/
+QKeyEventTransition::~QKeyEventTransition()
+{
+ Q_D(QKeyEventTransition);
+ delete d->transition;
+}
+
+/*!
+ Returns the key that this key event transition checks for.
+*/
+int QKeyEventTransition::key() const
+{
+ Q_D(const QKeyEventTransition);
+ return d->transition->key();
+}
+
+/*!
+ Sets the \a key that this key event transition will check for.
+*/
+void QKeyEventTransition::setKey(int key)
+{
+ Q_D(QKeyEventTransition);
+ d->transition->setKey(key);
+}
+
+QBindable<int> QKeyEventTransition::bindableKey()
+{
+ Q_D(QKeyEventTransition);
+ return d->transition->bindableKey();
+}
+
+/*!
+ Returns the keyboard modifier mask that this key event transition checks
+ for.
+*/
+Qt::KeyboardModifiers QKeyEventTransition::modifierMask() const
+{
+ Q_D(const QKeyEventTransition);
+ return d->transition->modifierMask();
+}
+
+/*!
+ Sets the keyboard modifier mask that this key event transition will
+ check for to \a modifierMask.
+*/
+void QKeyEventTransition::setModifierMask(Qt::KeyboardModifiers modifierMask)
+{
+ Q_D(QKeyEventTransition);
+ d->transition->setModifierMask(modifierMask);
+}
+
+QBindable<Qt::KeyboardModifiers> QKeyEventTransition::bindableModifierMask()
+{
+ Q_D(QKeyEventTransition);
+ return d->transition->bindableModifierMask();
+}
+
+/*!
+ \reimp
+*/
+bool QKeyEventTransition::eventTest(QEvent *event)
+{
+ Q_D(const QKeyEventTransition);
+ if (!QEventTransition::eventTest(event))
+ return false;
+ QStateMachine::WrappedEvent *we = static_cast<QStateMachine::WrappedEvent*>(event);
+ d->transition->setEventType(we->event()->type());
+ return QAbstractTransitionPrivate::get(d->transition)->callEventTest(we->event());
+}
+
+/*!
+ \reimp
+*/
+void QKeyEventTransition::onTransition(QEvent *event)
+{
+ QEventTransition::onTransition(event);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qkeyeventtransition.cpp"
diff --git a/src/statemachine/gui/qkeyeventtransition.h b/src/statemachine/gui/qkeyeventtransition.h
new file mode 100644
index 0000000..e1362e5
--- /dev/null
+++ b/src/statemachine/gui/qkeyeventtransition.h
@@ -0,0 +1,45 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QKEYEVENTTRANSITION_H
+#define QKEYEVENTTRANSITION_H
+
+#include <QtStateMachine/qeventtransition.h>
+
+QT_REQUIRE_CONFIG(qeventtransition);
+
+QT_BEGIN_NAMESPACE
+
+class QKeyEventTransitionPrivate;
+class Q_STATEMACHINE_EXPORT QKeyEventTransition : public QEventTransition
+{
+ Q_OBJECT
+ Q_PROPERTY(int key READ key WRITE setKey BINDABLE bindableKey)
+ Q_PROPERTY(Qt::KeyboardModifiers modifierMask READ modifierMask WRITE setModifierMask
+ BINDABLE bindableModifierMask)
+public:
+ QKeyEventTransition(QState *sourceState = nullptr);
+ QKeyEventTransition(QObject *object, QEvent::Type type, int key,
+ QState *sourceState = nullptr);
+ ~QKeyEventTransition();
+
+ int key() const;
+ void setKey(int key);
+ QBindable<int> bindableKey();
+
+ Qt::KeyboardModifiers modifierMask() const;
+ void setModifierMask(Qt::KeyboardModifiers modifiers);
+ QBindable<Qt::KeyboardModifiers> bindableModifierMask();
+
+protected:
+ void onTransition(QEvent *event) override;
+ bool eventTest(QEvent *event) override;
+
+private:
+ Q_DISABLE_COPY(QKeyEventTransition)
+ Q_DECLARE_PRIVATE(QKeyEventTransition)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/gui/qmouseeventtransition.cpp b/src/statemachine/gui/qmouseeventtransition.cpp
new file mode 100644
index 0000000..6a1e715
--- /dev/null
+++ b/src/statemachine/gui/qmouseeventtransition.cpp
@@ -0,0 +1,180 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qmouseeventtransition.h"
+#include "qbasicmouseeventtransition_p.h"
+#include "qstatemachine.h"
+
+#include <QtGui/qpainterpath.h>
+#include <private/qeventtransition_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QMouseEventTransition
+
+ \brief The QMouseEventTransition class provides a transition for mouse events.
+
+ \since 4.6
+ \ingroup statemachine
+ \inmodule QtStateMachine
+
+ QMouseEventTransition is part of \l{Qt State Machine Overview}{Qt State Machine Framework}.
+
+ \sa QState::addTransition()
+*/
+
+/*!
+ \property QMouseEventTransition::button
+
+ \brief the button that this mouse event transition is associated with
+*/
+
+/*!
+ \property QMouseEventTransition::modifierMask
+
+ \brief the keyboard modifier mask that this mouse event transition checks for
+*/
+
+class QMouseEventTransitionPrivate : public QEventTransitionPrivate
+{
+ Q_DECLARE_PUBLIC(QMouseEventTransition)
+public:
+ QMouseEventTransitionPrivate();
+
+ QBasicMouseEventTransition *transition;
+};
+
+QMouseEventTransitionPrivate::QMouseEventTransitionPrivate()
+{
+}
+
+/*!
+ Constructs a new mouse event transition with the given \a sourceState.
+*/
+QMouseEventTransition::QMouseEventTransition(QState *sourceState)
+ : QEventTransition(*new QMouseEventTransitionPrivate, sourceState)
+{
+ Q_D(QMouseEventTransition);
+ d->transition = new QBasicMouseEventTransition();
+}
+
+/*!
+ Constructs a new mouse event transition for events of the given \a type for
+ the given \a object, with the given \a button and \a sourceState.
+*/
+QMouseEventTransition::QMouseEventTransition(QObject *object, QEvent::Type type,
+ Qt::MouseButton button,
+ QState *sourceState)
+ : QEventTransition(*new QMouseEventTransitionPrivate, object, type, sourceState)
+{
+ Q_D(QMouseEventTransition);
+ d->transition = new QBasicMouseEventTransition(type, button);
+}
+
+/*!
+ Destroys this mouse event transition.
+*/
+QMouseEventTransition::~QMouseEventTransition()
+{
+ Q_D(QMouseEventTransition);
+ delete d->transition;
+}
+
+/*!
+ Returns the button that this mouse event transition checks for.
+*/
+Qt::MouseButton QMouseEventTransition::button() const
+{
+ Q_D(const QMouseEventTransition);
+ return d->transition->button();
+}
+
+/*!
+ Sets the \a button that this mouse event transition will check for.
+*/
+void QMouseEventTransition::setButton(Qt::MouseButton button)
+{
+ Q_D(QMouseEventTransition);
+ d->transition->setButton(button);
+}
+
+QBindable<Qt::MouseButton> QMouseEventTransition::bindableButton()
+{
+ Q_D(QMouseEventTransition);
+ return d->transition->bindableButton();
+}
+
+/*!
+ Returns the keyboard modifier mask that this mouse event transition checks
+ for.
+*/
+Qt::KeyboardModifiers QMouseEventTransition::modifierMask() const
+{
+ Q_D(const QMouseEventTransition);
+ return d->transition->modifierMask();
+}
+
+/*!
+ Sets the keyboard modifier mask that this mouse event transition will
+ check for to \a modifierMask.
+*/
+void QMouseEventTransition::setModifierMask(Qt::KeyboardModifiers modifierMask)
+{
+ Q_D(QMouseEventTransition);
+ d->transition->setModifierMask(modifierMask);
+}
+
+QBindable<Qt::KeyboardModifiers> QMouseEventTransition::bindableModifierMask()
+{
+ Q_D(QMouseEventTransition);
+ return d->transition->bindableModifierMask();
+}
+
+
+/*!
+ Returns the hit test path for this mouse event transition.
+*/
+QPainterPath QMouseEventTransition::hitTestPath() const
+{
+ Q_D(const QMouseEventTransition);
+ return d->transition->hitTestPath();
+}
+
+/*!
+ Sets the hit test path for this mouse event transition to \a path.
+ If a valid path has been set, the transition will only trigger if the mouse
+ event position (QMouseEvent::pos()) is inside the path.
+
+ \sa QPainterPath::contains()
+*/
+void QMouseEventTransition::setHitTestPath(const QPainterPath &path)
+{
+ Q_D(QMouseEventTransition);
+ d->transition->setHitTestPath(path);
+}
+
+/*!
+ \reimp
+*/
+bool QMouseEventTransition::eventTest(QEvent *event)
+{
+ Q_D(const QMouseEventTransition);
+ if (!QEventTransition::eventTest(event))
+ return false;
+ QStateMachine::WrappedEvent *we = static_cast<QStateMachine::WrappedEvent*>(event);
+ d->transition->setEventType(we->event()->type());
+ return QAbstractTransitionPrivate::get(d->transition)->callEventTest(we->event());
+}
+
+/*!
+ \reimp
+*/
+void QMouseEventTransition::onTransition(QEvent *event)
+{
+ QEventTransition::onTransition(event);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qmouseeventtransition.cpp"
diff --git a/src/statemachine/gui/qmouseeventtransition.h b/src/statemachine/gui/qmouseeventtransition.h
new file mode 100644
index 0000000..e02295b
--- /dev/null
+++ b/src/statemachine/gui/qmouseeventtransition.h
@@ -0,0 +1,49 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QMOUSEEVENTTRANSITION_H
+#define QMOUSEEVENTTRANSITION_H
+
+#include <QtStateMachine/qeventtransition.h>
+
+QT_REQUIRE_CONFIG(qeventtransition);
+
+QT_BEGIN_NAMESPACE
+
+class QMouseEventTransitionPrivate;
+class QPainterPath;
+class Q_STATEMACHINE_EXPORT QMouseEventTransition : public QEventTransition
+{
+ Q_OBJECT
+ Q_PROPERTY(Qt::MouseButton button READ button WRITE setButton BINDABLE bindableButton)
+ Q_PROPERTY(Qt::KeyboardModifiers modifierMask READ modifierMask WRITE setModifierMask
+ BINDABLE bindableModifierMask)
+public:
+ QMouseEventTransition(QState *sourceState = nullptr);
+ QMouseEventTransition(QObject *object, QEvent::Type type,
+ Qt::MouseButton button, QState *sourceState = nullptr);
+ ~QMouseEventTransition();
+
+ Qt::MouseButton button() const;
+ void setButton(Qt::MouseButton button);
+ QBindable<Qt::MouseButton> bindableButton();
+
+ Qt::KeyboardModifiers modifierMask() const;
+ void setModifierMask(Qt::KeyboardModifiers modifiers);
+ QBindable<Qt::KeyboardModifiers> bindableModifierMask();
+
+ QPainterPath hitTestPath() const;
+ void setHitTestPath(const QPainterPath &path);
+
+protected:
+ void onTransition(QEvent *event) override;
+ bool eventTest(QEvent *event) override;
+
+private:
+ Q_DISABLE_COPY(QMouseEventTransition)
+ Q_DECLARE_PRIVATE(QMouseEventTransition)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/qabstractstate.cpp b/src/statemachine/qabstractstate.cpp
new file mode 100644
index 0000000..8e511c9
--- /dev/null
+++ b/src/statemachine/qabstractstate.cpp
@@ -0,0 +1,203 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qabstractstate.h"
+#include "qabstractstate_p.h"
+#include "qstate.h"
+#include "qstate_p.h"
+#include "qstatemachine.h"
+#include "qstatemachine_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAbstractState
+ \inmodule QtStateMachine
+
+ \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{Qt State Machine Overview}{Qt 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.
+*/
+
+/*!
+ \property QAbstractState::active
+ \since 5.4
+
+ \brief the active property of this state. A state is active between
+ entered() and exited() signals.
+*/
+
+
+QAbstractStatePrivate::QAbstractStatePrivate(StateType type)
+ : stateType(type), isMachine(false), active(false), parentState(nullptr)
+{
+}
+
+QStateMachine *QAbstractStatePrivate::machine() const
+{
+ QObject *par = parent;
+ while (par != nullptr) {
+ if (QStateMachine *mach = qobject_cast<QStateMachine*>(par))
+ return mach;
+ par = par->parent();
+ }
+ return nullptr;
+}
+
+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(QAbstractState::QPrivateSignal());
+ active.setValue(true);
+}
+
+void QAbstractStatePrivate::emitExited()
+{
+ Q_Q(QAbstractState);
+ active.setValue(false);
+ emit q->exited(QAbstractState::QPrivateSignal());
+}
+
+/*!
+ Constructs a new state with the given \a parent state.
+*/
+QAbstractState::QAbstractState(QState *parent)
+ : QObject(*new QAbstractStatePrivate(QAbstractStatePrivate::AbstractState), parent)
+{
+}
+
+/*!
+ \internal
+*/
+QAbstractState::QAbstractState(QAbstractStatePrivate &dd, QState *parent)
+ : QObject(dd, parent)
+{
+}
+
+/*!
+ Destroys this state.
+*/
+QAbstractState::~QAbstractState()
+{
+}
+
+/*!
+ Returns this state's parent state, or \nullptr if the state has no
+ parent state.
+*/
+QState *QAbstractState::parentState() const
+{
+ Q_D(const QAbstractState);
+ if (d->parentState != parent())
+ d->parentState = qobject_cast<QState*>(parent());
+ return d->parentState;
+}
+
+/*!
+ Returns the state machine that this state is part of, or \nullptr if
+ the state is not part of a state machine.
+*/
+QStateMachine *QAbstractState::machine() const
+{
+ Q_D(const QAbstractState);
+ return d->machine();
+}
+
+/*!
+ Returns whether this state is active.
+
+ \sa activeChanged(bool), entered(), exited()
+*/
+bool QAbstractState::active() const
+{
+ Q_D(const QAbstractState);
+ return d->active;
+}
+
+QBindable<bool> QAbstractState::bindableActive()
+{
+ Q_D(QAbstractState);
+ return &d->active;
+}
+
+/*!
+ \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).
+*/
+
+/*!
+ \fn QAbstractState::activeChanged(bool active)
+ \since 5.4
+
+ This signal is emitted when the active property is changed with \a active as argument.
+
+ \sa QAbstractState::active, entered(), exited()
+*/
+
+/*!
+ \reimp
+*/
+bool QAbstractState::event(QEvent *e)
+{
+ return QObject::event(e);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qabstractstate.cpp"
diff --git a/src/statemachine/qabstractstate.h b/src/statemachine/qabstractstate.h
new file mode 100644
index 0000000..6e79fcf
--- /dev/null
+++ b/src/statemachine/qabstractstate.h
@@ -0,0 +1,54 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QABSTRACTSTATE_H
+#define QABSTRACTSTATE_H
+
+#include <QtCore/qobject.h>
+#include <QtStateMachine/qstatemachineglobal.h>
+
+QT_REQUIRE_CONFIG(statemachine);
+
+QT_BEGIN_NAMESPACE
+
+class QState;
+class QStateMachine;
+
+class QAbstractStatePrivate;
+class Q_STATEMACHINE_EXPORT QAbstractState : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool active READ active NOTIFY activeChanged BINDABLE bindableActive)
+public:
+ ~QAbstractState();
+
+ QState *parentState() const;
+ QStateMachine *machine() const;
+
+ bool active() const;
+ QBindable<bool> bindableActive();
+
+Q_SIGNALS:
+ void entered(QPrivateSignal);
+ void exited(QPrivateSignal);
+ void activeChanged(bool active);
+
+protected:
+ QAbstractState(QState *parent = nullptr);
+
+ virtual void onEntry(QEvent *event) = 0;
+ virtual void onExit(QEvent *event) = 0;
+
+ bool event(QEvent *e) override;
+
+protected:
+ QAbstractState(QAbstractStatePrivate &dd, QState *parent);
+
+private:
+ Q_DISABLE_COPY(QAbstractState)
+ Q_DECLARE_PRIVATE(QAbstractState)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/qabstractstate_p.h b/src/statemachine/qabstractstate_p.h
new file mode 100644
index 0000000..6bfb54c
--- /dev/null
+++ b/src/statemachine/qabstractstate_p.h
@@ -0,0 +1,70 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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>
+#include <QtStateMachine/qabstractstate.h>
+
+QT_REQUIRE_CONFIG(statemachine);
+
+QT_BEGIN_NAMESPACE
+
+class QStateMachine;
+
+class QState;
+class QAbstractStatePrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractState)
+
+public:
+ enum StateType {
+ AbstractState,
+ StandardState,
+ FinalState,
+ HistoryState
+ };
+
+ QAbstractStatePrivate(StateType type);
+
+ static QAbstractStatePrivate *get(QAbstractState *q)
+ { return q->d_func(); }
+ static const QAbstractStatePrivate *get(const QAbstractState *q)
+ { return q->d_func(); }
+
+ QStateMachine *machine() const;
+
+ void callOnEntry(QEvent *e);
+ void callOnExit(QEvent *e);
+
+ void emitEntered();
+ void emitExited();
+
+ quint16 stateType;
+ bool isMachine;
+
+ void activeChanged()
+ {
+ emit q_func()->activeChanged(active.value());
+ }
+ Q_OBJECT_BINDABLE_PROPERTY(QAbstractStatePrivate, bool, active,
+ &QAbstractStatePrivate::activeChanged);
+
+ mutable QState *parentState;
+};
+
+QT_END_NAMESPACE
+
+#endif // QABSTRACTSTATE_P_H
diff --git a/src/statemachine/qabstracttransition.cpp b/src/statemachine/qabstracttransition.cpp
new file mode 100644
index 0000000..957091a
--- /dev/null
+++ b/src/statemachine/qabstracttransition.cpp
@@ -0,0 +1,400 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qabstracttransition.h"
+#include "qabstracttransition_p.h"
+#include "qabstractstate.h"
+#include "qhistorystate.h"
+#include "qstate.h"
+#include "qstatemachine.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QAbstractTransition
+ \inmodule QtStateMachine
+
+ \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{Qt State Machine Overview}
+ {Qt State Machine Framework}.
+
+ The sourceState() function returns the source of the transition. The
+ targetStates() function returns the targets of the transition. The machine()
+ function returns the state machine that the transition is part of.
+
+ The triggered() signal is emitted when the transition has been triggered.
+
+ Transitions can cause animations to be played. Use the addAnimation()
+ function to add an animation to the transition.
+
+ \section1 Subclassing
+
+ The eventTest() function is called by the state machine to determine whether
+ an event should trigger the transition. In your reimplementation you
+ typically check the event type and cast the event object to the proper type,
+ and check that one or more properties of the event meet your criteria.
+
+ The onTransition() function is called when the transition is triggered;
+ reimplement this function to perform custom processing for the transition.
+*/
+
+/*!
+ \property QAbstractTransition::sourceState
+
+ \brief the source state (parent) of this transition
+*/
+
+/*!
+ \property QAbstractTransition::targetState
+
+ \brief the target state of this transition
+
+ If a transition has no target state, the transition may still be
+ triggered, but this will not cause the state machine's configuration to
+ change (i.e. the current state will not be exited and re-entered).
+*/
+
+/*!
+ \property QAbstractTransition::targetStates
+
+ \brief the target states of this transition
+
+ If multiple states are specified, all must be descendants of the same
+ parallel group state.
+*/
+
+/*!
+ \property QAbstractTransition::transitionType
+
+ \brief indicates whether this transition is an internal transition, or an external transition.
+
+ Internal and external transitions behave the same, except for the case of a transition whose
+ source state is a compound state and whose target(s) is a descendant of the source. In such a
+ case, an internal transition will not exit and re-enter its source state, while an external one
+ will.
+
+ By default, the type is an external transition.
+*/
+
+/*!
+ \enum QAbstractTransition::TransitionType
+
+ This enum specifies the kind of transition. By default, the type is an external transition.
+
+ \value ExternalTransition Any state that is the source state of a transition (which is not a
+ target-less transition) is left, and re-entered when necessary.
+ \value InternalTransition If the target state of a transition is a sub-state of a compound state,
+ and that compound state is the source state, an internal transition will
+ not leave the source state.
+
+ \sa QAbstractTransition::transitionType
+*/
+
+QStateMachine *QAbstractTransitionPrivate::machine() const
+{
+ if (QState *source = sourceState())
+ return source->machine();
+ Q_Q(const QAbstractTransition);
+ if (QHistoryState *parent = qobject_cast<QHistoryState *>(q->parent()))
+ return parent->machine();
+ return nullptr;
+}
+
+bool QAbstractTransitionPrivate::callEventTest(QEvent *e)
+{
+ Q_Q(QAbstractTransition);
+ return q->eventTest(e);
+}
+
+void QAbstractTransitionPrivate::callOnTransition(QEvent *e)
+{
+ Q_Q(QAbstractTransition);
+ q->onTransition(e);
+}
+
+QState *QAbstractTransitionPrivate::sourceState() const
+{
+ return qobject_cast<QState*>(parent);
+}
+
+void QAbstractTransitionPrivate::emitTriggered()
+{
+ Q_Q(QAbstractTransition);
+ emit q->triggered(QAbstractTransition::QPrivateSignal());
+}
+
+/*!
+ Constructs a new QAbstractTransition object with the given \a sourceState.
+*/
+QAbstractTransition::QAbstractTransition(QState *sourceState)
+ : QObject(*new QAbstractTransitionPrivate, sourceState)
+{
+}
+
+/*!
+ \internal
+*/
+QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd,
+ QState *parent)
+ : QObject(dd, parent)
+{
+}
+
+/*!
+ Destroys this transition.
+*/
+QAbstractTransition::~QAbstractTransition()
+{
+}
+
+/*!
+ Returns the source state of this transition, or \nullptr 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 \nullptr if the
+ transition has no target.
+*/
+QAbstractState *QAbstractTransition::targetState() const
+{
+ Q_D(const QAbstractTransition);
+ if (d->targetStates.isEmpty())
+ return nullptr;
+ return d->targetStates.first().data();
+}
+
+/*!
+ Sets the \a target state of this transition.
+*/
+void QAbstractTransition::setTargetState(QAbstractState* target)
+{
+ Q_D(QAbstractTransition);
+ if ((d->targetStates.size() == 1 && target == d->targetStates.at(0).data()) ||
+ (d->targetStates.isEmpty() && target == nullptr)) {
+ return;
+ }
+ if (!target)
+ d->targetStates.clear();
+ else
+ setTargetStates(QList<QAbstractState*>() << target);
+ emit targetStateChanged(QPrivateSignal());
+}
+
+/*!
+ Returns the target states of this transition, or an empty list if this
+ transition has no target states.
+*/
+QList<QAbstractState*> QAbstractTransition::targetStates() const
+{
+ Q_D(const QAbstractTransition);
+ QList<QAbstractState*> result;
+ for (int i = 0; i < d->targetStates.size(); ++i) {
+ QAbstractState *target = d->targetStates.at(i).data();
+ if (target)
+ result.append(target);
+ }
+ return result;
+}
+
+/*!
+ Sets the target states of this transition to be the given \a targets.
+*/
+void QAbstractTransition::setTargetStates(const QList<QAbstractState*> &targets)
+{
+ Q_D(QAbstractTransition);
+
+ // Verify if any of the new target states is a null-pointer:
+ for (int i = 0; i < targets.size(); ++i) {
+ if (targets.at(i) == nullptr) {
+ qWarning("QAbstractTransition::setTargetStates: target state(s) cannot be null");
+ return;
+ }
+ }
+
+ // First clean out any target states that got destroyed, but for which we still have a QPointer
+ // around.
+ for (int i = 0; i < d->targetStates.size(); ) {
+ if (d->targetStates.at(i).isNull()) {
+ d->targetStates.remove(i);
+ } else {
+ ++i;
+ }
+ }
+
+ // Easy check: if both lists are empty, we're done.
+ if (targets.isEmpty() && d->targetStates.isEmpty())
+ return;
+
+ bool sameList = true;
+
+ if (targets.size() != d->targetStates.size()) {
+ // If the sizes of the lists are different, we don't need to be smart: they're different. So
+ // we can just set the new list as the targetStates.
+ sameList = false;
+ } else {
+ QList<QPointer<QAbstractState>> copy(d->targetStates);
+ for (int i = 0; i < targets.size(); ++i) {
+ sameList &= copy.removeOne(targets.at(i));
+ if (!sameList)
+ break; // ok, we now know the lists are not the same, so stop the loop.
+ }
+
+ sameList &= copy.isEmpty();
+ }
+
+ if (sameList)
+ return;
+
+ d->targetStates.resize(targets.size());
+ for (int i = 0; i < targets.size(); ++i) {
+ d->targetStates[i] = targets.at(i);
+ }
+
+ emit targetStatesChanged(QPrivateSignal());
+}
+
+/*!
+ Returns the type of the transition.
+*/
+QAbstractTransition::TransitionType QAbstractTransition::transitionType() const
+{
+ Q_D(const QAbstractTransition);
+ return d->transitionType;
+}
+
+/*!
+ Sets the type of the transition to \a type.
+*/
+void QAbstractTransition::setTransitionType(TransitionType type)
+{
+ Q_D(QAbstractTransition);
+ d->transitionType = type;
+}
+
+QBindable<QAbstractTransition::TransitionType> QAbstractTransition::bindableTransitionType()
+{
+ Q_D(QAbstractTransition);
+ return &d->transitionType;
+}
+
+/*!
+ Returns the state machine that this transition is part of, or
+ \nullptr if the transition is not part of a state machine.
+*/
+QStateMachine *QAbstractTransition::machine() const
+{
+ Q_D(const QAbstractTransition);
+ return d->machine();
+}
+
+#if QT_CONFIG(animation)
+
+/*!
+ Adds the given \a animation to this transition.
+ The transition does not take ownership of the animation.
+
+ \sa removeAnimation(), animations()
+*/
+void QAbstractTransition::addAnimation(QAbstractAnimation *animation)
+{
+ Q_D(QAbstractTransition);
+ if (!animation) {
+ qWarning("QAbstractTransition::addAnimation: cannot add null animation");
+ return;
+ }
+ d->animations.append(animation);
+}
+
+/*!
+ Removes the given \a animation from this transition.
+
+ \sa addAnimation()
+*/
+void QAbstractTransition::removeAnimation(QAbstractAnimation *animation)
+{
+ Q_D(QAbstractTransition);
+ if (!animation) {
+ qWarning("QAbstractTransition::removeAnimation: cannot remove null animation");
+ return;
+ }
+ d->animations.removeOne(animation);
+}
+
+/*!
+ Returns the list of animations associated with this transition, or an empty
+ list if it has no animations.
+
+ \sa addAnimation()
+*/
+QList<QAbstractAnimation*> QAbstractTransition::animations() const
+{
+ Q_D(const QAbstractTransition);
+ return d->animations;
+}
+
+#endif
+
+/*!
+ \fn QAbstractTransition::eventTest(QEvent *event)
+
+ This function is called to determine whether the given \a event should cause
+ this transition to trigger. Reimplement this function and return true if the
+ event should trigger the transition, otherwise return false.
+*/
+
+/*!
+ \fn QAbstractTransition::onTransition(QEvent *event)
+
+ This function is called when the transition is triggered. The given \a event
+ is what caused the transition to trigger. Reimplement this function to
+ perform custom processing when the transition is triggered.
+*/
+
+/*!
+ \fn QAbstractTransition::triggered()
+
+ This signal is emitted when the transition has been triggered (after
+ onTransition() has been called).
+*/
+
+/*!
+ \fn QAbstractTransition::targetStateChanged()
+ \since 5.4
+
+ This signal is emitted when the targetState property is changed.
+
+ \sa QAbstractTransition::targetState
+*/
+
+/*!
+ \fn QAbstractTransition::targetStatesChanged()
+ \since 5.4
+
+ This signal is emitted when the targetStates property is changed.
+
+ \sa QAbstractTransition::targetStates
+*/
+
+/*!
+ \reimp
+*/
+bool QAbstractTransition::event(QEvent *e)
+{
+ return QObject::event(e);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qabstracttransition.cpp"
diff --git a/src/statemachine/qabstracttransition.h b/src/statemachine/qabstracttransition.h
new file mode 100644
index 0000000..816c1b0
--- /dev/null
+++ b/src/statemachine/qabstracttransition.h
@@ -0,0 +1,84 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QABSTRACTTRANSITION_H
+#define QABSTRACTTRANSITION_H
+
+#include <QtCore/qlist.h>
+#include <QtCore/qobject.h>
+
+#include <QtStateMachine/qstatemachineglobal.h>
+
+QT_REQUIRE_CONFIG(statemachine);
+
+QT_BEGIN_NAMESPACE
+
+class QEvent;
+class QAbstractState;
+class QState;
+class QStateMachine;
+
+#if QT_CONFIG(animation)
+class QAbstractAnimation;
+#endif
+
+class QAbstractTransitionPrivate;
+class Q_STATEMACHINE_EXPORT QAbstractTransition : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QState* sourceState READ sourceState)
+ Q_PROPERTY(QAbstractState* targetState READ targetState WRITE setTargetState NOTIFY targetStateChanged)
+ Q_PROPERTY(QList<QAbstractState*> targetStates READ targetStates WRITE setTargetStates NOTIFY targetStatesChanged)
+ Q_PROPERTY(TransitionType transitionType READ transitionType WRITE setTransitionType
+ BINDABLE bindableTransitionType REVISION(1, 1))
+public:
+ enum TransitionType {
+ ExternalTransition,
+ InternalTransition
+ };
+ Q_ENUM(TransitionType)
+
+ QAbstractTransition(QState *sourceState = nullptr);
+ virtual ~QAbstractTransition();
+
+ QState *sourceState() const;
+ QAbstractState *targetState() const;
+ void setTargetState(QAbstractState* target);
+ QList<QAbstractState*> targetStates() const;
+ void setTargetStates(const QList<QAbstractState*> &targets);
+
+ TransitionType transitionType() const;
+ void setTransitionType(TransitionType type);
+ QBindable<QAbstractTransition::TransitionType> bindableTransitionType();
+
+ QStateMachine *machine() const;
+
+#if QT_CONFIG(animation)
+ void addAnimation(QAbstractAnimation *animation);
+ void removeAnimation(QAbstractAnimation *animation);
+ QList<QAbstractAnimation*> animations() const;
+#endif
+
+Q_SIGNALS:
+ void triggered(QPrivateSignal);
+ void targetStateChanged(QPrivateSignal);
+ void targetStatesChanged(QPrivateSignal);
+
+protected:
+ virtual bool eventTest(QEvent *event) = 0;
+
+ virtual void onTransition(QEvent *event) = 0;
+
+ bool event(QEvent *e) override;
+
+protected:
+ QAbstractTransition(QAbstractTransitionPrivate &dd, QState *parent);
+
+private:
+ Q_DISABLE_COPY(QAbstractTransition)
+ Q_DECLARE_PRIVATE(QAbstractTransition)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/qabstracttransition_p.h b/src/statemachine/qabstracttransition_p.h
new file mode 100644
index 0000000..08ac45d
--- /dev/null
+++ b/src/statemachine/qabstracttransition_p.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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>
+#include <QtCore/qsharedpointer.h>
+
+QT_REQUIRE_CONFIG(statemachine);
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractState;
+class QState;
+class QStateMachine;
+
+class QAbstractTransition;
+class Q_STATEMACHINE_EXPORT QAbstractTransitionPrivate
+ : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QAbstractTransition)
+public:
+ QAbstractTransitionPrivate() = default;
+
+ static QAbstractTransitionPrivate *get(QAbstractTransition *q)
+ { return q->d_func(); }
+
+ bool callEventTest(QEvent *e);
+ virtual void callOnTransition(QEvent *e);
+ QState *sourceState() const;
+ QStateMachine *machine() const;
+ void emitTriggered();
+
+ QList<QPointer<QAbstractState>> targetStates;
+
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QAbstractTransitionPrivate,
+ QAbstractTransition::TransitionType, transitionType,
+ QAbstractTransition::ExternalTransition);
+
+#if QT_CONFIG(animation)
+ QList<QAbstractAnimation*> animations;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/qeventtransition.cpp b/src/statemachine/qeventtransition.cpp
new file mode 100644
index 0000000..460010a
--- /dev/null
+++ b/src/statemachine/qeventtransition.cpp
@@ -0,0 +1,230 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qeventtransition.h"
+#include "qeventtransition_p.h"
+#include "qstate.h"
+#include "qstate_p.h"
+#include "qstatemachine.h"
+#include "qstatemachine_p.h"
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QEventTransition
+ \inmodule QtStateMachine
+
+ \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{Qt State Machine Overview}{Qt State Machine Framework}.
+
+ Example:
+
+ \code
+ QPushButton *button = ...;
+ QState *s1 = ...;
+ QState *s2 = ...;
+ // If in s1 and the button receives an Enter event, transition to s2
+ QEventTransition *enterTransition = new QEventTransition(button, QEvent::Enter);
+ enterTransition->setTargetState(s2);
+ s1->addTransition(enterTransition);
+ // If in s2 and the button receives an Exit event, transition back to s1
+ QEventTransition *leaveTransition = new QEventTransition(button, QEvent::Leave);
+ leaveTransition->setTargetState(s1);
+ s2->addTransition(leaveTransition);
+ \endcode
+
+ \section1 Subclassing
+
+ When reimplementing the eventTest() function, you should first call the base
+ implementation to verify that the event is a QStateMachine::WrappedEvent for
+ the proper object and event type. You may then cast the event to a
+ QStateMachine::WrappedEvent and get the original event by calling
+ QStateMachine::WrappedEvent::event(), and perform additional checks on that
+ object.
+
+ \sa QState::addTransition()
+*/
+
+/*!
+ \property QEventTransition::eventSource
+
+ \brief the event source that this event transition is associated with
+*/
+
+/*!
+ \property QEventTransition::eventType
+
+ \brief the type of event that this event transition is associated with
+*/
+
+QEventTransitionPrivate::~QEventTransitionPrivate()
+{
+}
+
+void QEventTransitionPrivate::unregister()
+{
+ Q_Q(QEventTransition);
+ if (!registered || !machine())
+ return;
+ QStateMachinePrivate::get(machine())->unregisterEventTransition(q);
+}
+
+void QEventTransitionPrivate::maybeRegister()
+{
+ Q_Q(QEventTransition);
+ if (QStateMachine *mach = machine())
+ QStateMachinePrivate::get(mach)->maybeRegisterEventTransition(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.setValueBypassingBindings(object);
+ d->eventType.setValueBypassingBindings(type);
+ d->maybeRegister();
+}
+
+/*!
+ \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.setValueBypassingBindings(object);
+ d->eventType.setValueBypassingBindings(type);
+ d->maybeRegister();
+}
+
+/*!
+ 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);
+ d->eventType.removeBindingUnlessInWrapper();
+ if (d->eventType.valueBypassingBindings() == type)
+ return;
+ d->unregister();
+ d->eventType.setValueBypassingBindings(type);
+ d->maybeRegister();
+ d->eventType.notify();
+}
+
+QBindable<QEvent::Type> QEventTransition::bindableEventType()
+{
+ Q_D(QEventTransition);
+ return &d->eventType;
+}
+
+/*!
+ Returns the event source associated with this event transition.
+*/
+QObject *QEventTransition::eventSource() const
+{
+ Q_D(const QEventTransition);
+ return d->object;
+}
+
+/*!
+ Sets the event source associated with this event transition to be the given
+ \a object.
+*/
+void QEventTransition::setEventSource(QObject *object)
+{
+ Q_D(QEventTransition);
+ d->object.removeBindingUnlessInWrapper();
+ if (d->object.valueBypassingBindings() == object)
+ return;
+ d->unregister();
+ d->object.setValueBypassingBindings(object);
+ d->maybeRegister();
+ d->object.notify();
+}
+
+QBindable<QObject*> QEventTransition::bindableEventSource()
+{
+ Q_D(QEventTransition);
+ return &d->object;
+}
+
+/*!
+ \reimp
+*/
+bool QEventTransition::eventTest(QEvent *event)
+{
+ Q_D(const QEventTransition);
+ if (event->type() == QEvent::StateMachineWrapped) {
+ QStateMachine::WrappedEvent *we = static_cast<QStateMachine::WrappedEvent*>(event);
+ return (we->object() == d->object)
+ && (we->event()->type() == d->eventType.value());
+ }
+ return false;
+}
+
+/*!
+ \reimp
+*/
+void QEventTransition::onTransition(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+bool QEventTransition::event(QEvent *e)
+{
+ return QAbstractTransition::event(e);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qeventtransition.cpp"
diff --git a/src/statemachine/qeventtransition.h b/src/statemachine/qeventtransition.h
new file mode 100644
index 0000000..0de066e
--- /dev/null
+++ b/src/statemachine/qeventtransition.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QEVENTTRANSITION_H
+#define QEVENTTRANSITION_H
+
+#include <QtCore/qcoreevent.h>
+#include <QtStateMachine/qabstracttransition.h>
+
+QT_REQUIRE_CONFIG(qeventtransition);
+
+QT_BEGIN_NAMESPACE
+
+class QEventTransitionPrivate;
+class Q_STATEMACHINE_EXPORT QEventTransition : public QAbstractTransition
+{
+ Q_OBJECT
+ Q_PROPERTY(QObject* eventSource READ eventSource WRITE setEventSource
+ BINDABLE bindableEventSource)
+ Q_PROPERTY(QEvent::Type eventType READ eventType WRITE setEventType
+ BINDABLE bindableEventType)
+public:
+ QEventTransition(QState *sourceState = nullptr);
+ QEventTransition(QObject *object, QEvent::Type type, QState *sourceState = nullptr);
+ ~QEventTransition();
+
+ QObject *eventSource() const;
+ void setEventSource(QObject *object);
+ QBindable<QObject*> bindableEventSource();
+
+ QEvent::Type eventType() const;
+ void setEventType(QEvent::Type type);
+ QBindable<QEvent::Type> bindableEventType();
+
+protected:
+ bool eventTest(QEvent *event) override;
+ void onTransition(QEvent *event) override;
+
+ bool event(QEvent *e) override;
+
+protected:
+ QEventTransition(QEventTransitionPrivate &dd, QState *parent);
+ QEventTransition(QEventTransitionPrivate &dd, QObject *object,
+ QEvent::Type type, QState *parent);
+
+private:
+ Q_DISABLE_COPY(QEventTransition)
+ Q_DECLARE_PRIVATE(QEventTransition)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/qeventtransition_p.h b/src/statemachine/qeventtransition_p.h
new file mode 100644
index 0000000..a632394
--- /dev/null
+++ b/src/statemachine/qeventtransition_p.h
@@ -0,0 +1,57 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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"
+#include <QtCore/private/qproperty_p.h>
+
+QT_REQUIRE_CONFIG(qeventtransition);
+
+QT_BEGIN_NAMESPACE
+
+class QEventTransition;
+class Q_STATEMACHINE_EXPORT QEventTransitionPrivate : public QAbstractTransitionPrivate
+{
+ Q_DECLARE_PUBLIC(QEventTransition)
+public:
+ QEventTransitionPrivate() = default;
+ ~QEventTransitionPrivate();
+
+ static QEventTransitionPrivate *get(QEventTransition *q)
+ { return q->d_func(); }
+
+ void unregister();
+ void maybeRegister();
+
+ void setEventSource(QObject* eventSource)
+ {
+ q_func()->setEventSource(eventSource);
+ }
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QEventTransitionPrivate, QObject*, object,
+ &QEventTransitionPrivate::setEventSource, nullptr);
+
+ void setEventType(QEvent::Type eventType)
+ {
+ q_func()->setEventType(eventType);
+ }
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QEventTransitionPrivate, QEvent::Type, eventType,
+ &QEventTransitionPrivate::setEventType, QEvent::None);
+ bool registered = false;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/qfinalstate.cpp b/src/statemachine/qfinalstate.cpp
new file mode 100644
index 0000000..8dace42
--- /dev/null
+++ b/src/statemachine/qfinalstate.cpp
@@ -0,0 +1,105 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qfinalstate_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QFinalState
+ \inmodule QtStateMachine
+
+ \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{Qt State Machine Overview}{Qt 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 QState::finished()
+*/
+
+QFinalStatePrivate::QFinalStatePrivate()
+ : QAbstractStatePrivate(FinalState)
+{
+}
+
+QFinalStatePrivate::~QFinalStatePrivate()
+{
+ // to prevent vtables being generated in every file that includes the private header
+}
+
+/*!
+ Constructs a new QFinalState object with the given \a parent state.
+*/
+QFinalState::QFinalState(QState *parent)
+ : QAbstractState(*new QFinalStatePrivate, parent)
+{
+}
+
+/*!
+ \internal
+ */
+QFinalState::QFinalState(QFinalStatePrivate &dd, QState *parent)
+ : QAbstractState(dd, 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
+
+#include "moc_qfinalstate.cpp"
diff --git a/src/statemachine/qfinalstate.h b/src/statemachine/qfinalstate.h
new file mode 100644
index 0000000..78b6758
--- /dev/null
+++ b/src/statemachine/qfinalstate.h
@@ -0,0 +1,37 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFINALSTATE_H
+#define QFINALSTATE_H
+
+#include <QtStateMachine/qabstractstate.h>
+
+QT_REQUIRE_CONFIG(statemachine);
+
+QT_BEGIN_NAMESPACE
+
+class QFinalStatePrivate;
+class Q_STATEMACHINE_EXPORT QFinalState : public QAbstractState
+{
+ Q_OBJECT
+public:
+ QFinalState(QState *parent = nullptr);
+ ~QFinalState();
+
+protected:
+ void onEntry(QEvent *event) override;
+ void onExit(QEvent *event) override;
+
+ bool event(QEvent *e) override;
+
+protected:
+ explicit QFinalState(QFinalStatePrivate &dd, QState *parent);
+
+private:
+ Q_DISABLE_COPY(QFinalState)
+ Q_DECLARE_PRIVATE(QFinalState)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/qfinalstate_p.h b/src/statemachine/qfinalstate_p.h
new file mode 100644
index 0000000..4aa8aec
--- /dev/null
+++ b/src/statemachine/qfinalstate_p.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFINALSTATE_P_H
+#define QFINALSTATE_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 "qfinalstate.h"
+#include "private/qabstractstate_p.h"
+
+QT_REQUIRE_CONFIG(statemachine);
+
+QT_BEGIN_NAMESPACE
+
+class Q_STATEMACHINE_EXPORT QFinalStatePrivate : public QAbstractStatePrivate
+{
+ Q_DECLARE_PUBLIC(QFinalState)
+
+public:
+ QFinalStatePrivate();
+ ~QFinalStatePrivate();
+};
+
+QT_END_NAMESPACE
+
+#endif // QFINALSTATE_P_H
diff --git a/src/statemachine/qhistorystate.cpp b/src/statemachine/qhistorystate.cpp
new file mode 100644
index 0000000..19eab72
--- /dev/null
+++ b/src/statemachine/qhistorystate.cpp
@@ -0,0 +1,310 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qhistorystate.h"
+#include "qhistorystate_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QHistoryState
+ \inmodule QtStateMachine
+
+ \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 or more
+ other child states of the parent state. QHistoryState is part of \l{Qt State Machine Overview}
+ {Qt 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
+
+ If more than one default state has to be entered, or if the transition to the default state(s)
+ has to be acted upon, the defaultTransition should be set instead. Note that the eventTest()
+ method of that transition will never be called: the selection and execution of the transition is
+ done automatically when entering the history state.
+
+ 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::defaultTransition
+
+ \brief the default transition of this history state
+*/
+
+/*!
+ \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.
+*/
+
+namespace {
+class DefaultStateTransition: public QAbstractTransition
+{
+ Q_OBJECT
+
+public:
+ DefaultStateTransition(QHistoryState *source, QAbstractState *target);
+
+protected:
+ // It doesn't matter whether this transition matches any event or not. It is always associated
+ // with a QHistoryState, and as soon as the state-machine detects that it enters a history
+ // state, it will handle this transition as a special case. The history state itself is never
+ // entered either: either the stored configuration will be used, or the target(s) of this
+ // transition are used.
+ bool eventTest(QEvent *event) override { Q_UNUSED(event); return false; }
+ void onTransition(QEvent *event) override { Q_UNUSED(event); }
+};
+}
+
+QHistoryStatePrivate::QHistoryStatePrivate()
+ : QAbstractStatePrivate(HistoryState)
+ , defaultTransition(nullptr)
+ , historyType(QHistoryState::ShallowHistory)
+{
+}
+
+DefaultStateTransition::DefaultStateTransition(QHistoryState *source, QAbstractState *target)
+ : QAbstractTransition()
+{
+ setParent(source);
+ setTargetState(target);
+}
+
+/*!
+ Constructs a new shallow history state with the given \a parent state.
+*/
+QHistoryState::QHistoryState(QState *parent)
+ : QAbstractState(*new QHistoryStatePrivate, parent)
+{
+}
+/*!
+ Constructs a new history state of the given \a type, with the given \a
+ parent state.
+*/
+QHistoryState::QHistoryState(HistoryType type, QState *parent)
+ : QAbstractState(*new QHistoryStatePrivate, parent)
+{
+ Q_D(QHistoryState);
+ d->historyType = type;
+}
+
+/*!
+ Destroys this history state.
+*/
+QHistoryState::~QHistoryState()
+{
+}
+
+/*!
+ Returns this history state's default transition. The default transition is
+ taken when the history state has never been entered before. The target states
+ of the default transition therefore make up the default state.
+
+ \since 5.6
+*/
+QAbstractTransition *QHistoryState::defaultTransition() const
+{
+ Q_D(const QHistoryState);
+ return d->defaultTransition;
+}
+
+/*!
+ Sets this history state's default transition to be the given \a transition.
+ This will set the source state of the \a transition to the history state.
+
+ Note that the eventTest method of the \a transition will never be called.
+
+ \since 5.6
+*/
+void QHistoryState::setDefaultTransition(QAbstractTransition *transition)
+{
+ Q_D(QHistoryState);
+ d->defaultTransition.removeBindingUnlessInWrapper();
+ if (d->defaultTransition.valueBypassingBindings() == transition)
+ return;
+ d->defaultTransition.setValueBypassingBindings(transition);
+ if (transition)
+ transition->setParent(this);
+ d->defaultTransition.notify();
+ emit defaultTransitionChanged(QHistoryState::QPrivateSignal());
+}
+
+QBindable<QAbstractTransition*> QHistoryState::bindableDefaultTransition()
+{
+ Q_D(QHistoryState);
+ return &d->defaultTransition;
+}
+
+/*!
+ 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->defaultTransition.value() ? d->defaultTransition->targetState() : nullptr;
+}
+
+static inline bool isSoleEntry(const QList<QAbstractState*> &states, const QAbstractState * state)
+{
+ return states.size() == 1 && states.first() == state;
+}
+
+/*!
+ Sets this history state's default state to be the given \a state.
+ \a state must be a sibling of this history state.
+
+ Note that this function does not set \a state as the initial state
+ of its parent.
+*/
+void QHistoryState::setDefaultState(QAbstractState *state)
+{
+ Q_D(QHistoryState);
+ if (state && state->parentState() != parentState()) {
+ qWarning("QHistoryState::setDefaultState: state %p does not belong "
+ "to this history state's group (%p)", state, parentState());
+ return;
+ }
+ // evaluate the binding once
+ auto *defaultTransition = d->defaultTransition.value();
+ if (!defaultTransition || !isSoleEntry(defaultTransition->targetStates(), state)) {
+ if (!defaultTransition || !qobject_cast<DefaultStateTransition*>(defaultTransition))
+ d->defaultTransition.setValue(new DefaultStateTransition(this, state));
+ else
+ defaultTransition->setTargetState(state);
+ emit defaultStateChanged(QHistoryState::QPrivateSignal());
+ }
+}
+
+/*!
+ 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;
+}
+
+QBindable<QHistoryState::HistoryType> QHistoryState::bindableHistoryType()
+{
+ Q_D(QHistoryState);
+ return &d->historyType;
+}
+
+/*!
+ \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);
+}
+
+/*!
+ \fn QHistoryState::defaultStateChanged()
+ \since 5.4
+
+ This signal is emitted when the defaultState property is changed.
+
+ \sa QHistoryState::defaultState
+*/
+
+/*!
+ \fn QHistoryState::historyTypeChanged()
+ \since 5.4
+
+ This signal is emitted when the historyType property is changed.
+
+ \sa QHistoryState::historyType
+*/
+
+/*!
+ \fn QHistoryState::defaultTransitionChanged()
+ \since 5.6
+
+ This signal is emitted when the defaultTransition property is changed.
+
+ \sa QHistoryState::defaultTransition
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qhistorystate.cpp"
+#include "qhistorystate.moc"
diff --git a/src/statemachine/qhistorystate.h b/src/statemachine/qhistorystate.h
new file mode 100644
index 0000000..a089f88
--- /dev/null
+++ b/src/statemachine/qhistorystate.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QHISTORYSTATE_H
+#define QHISTORYSTATE_H
+
+#include <QtStateMachine/qabstractstate.h>
+
+QT_REQUIRE_CONFIG(statemachine);
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractTransition;
+class QHistoryStatePrivate;
+class Q_STATEMACHINE_EXPORT QHistoryState : public QAbstractState
+{
+ Q_OBJECT
+ Q_PROPERTY(QAbstractState* defaultState READ defaultState WRITE setDefaultState NOTIFY defaultStateChanged)
+ Q_PROPERTY(QAbstractTransition* defaultTransition READ defaultTransition
+ WRITE setDefaultTransition NOTIFY defaultTransitionChanged
+ BINDABLE bindableDefaultTransition)
+ Q_PROPERTY(HistoryType historyType READ historyType WRITE setHistoryType
+ NOTIFY historyTypeChanged BINDABLE bindableHistoryType)
+public:
+ enum HistoryType {
+ ShallowHistory,
+ DeepHistory
+ };
+ Q_ENUM(HistoryType)
+
+ QHistoryState(QState *parent = nullptr);
+ QHistoryState(HistoryType type, QState *parent = nullptr);
+ ~QHistoryState();
+
+ QAbstractTransition *defaultTransition() const;
+ void setDefaultTransition(QAbstractTransition *transition);
+ QBindable<QAbstractTransition*> bindableDefaultTransition();
+
+ QAbstractState *defaultState() const;
+ void setDefaultState(QAbstractState *state);
+
+ HistoryType historyType() const;
+ void setHistoryType(HistoryType type);
+ QBindable<QHistoryState::HistoryType> bindableHistoryType();
+
+Q_SIGNALS:
+ void defaultTransitionChanged(QPrivateSignal);
+ void defaultStateChanged(QPrivateSignal);
+ void historyTypeChanged(QPrivateSignal);
+
+protected:
+ void onEntry(QEvent *event) override;
+ void onExit(QEvent *event) override;
+
+ bool event(QEvent *e) override;
+
+private:
+ Q_DISABLE_COPY(QHistoryState)
+ Q_DECLARE_PRIVATE(QHistoryState)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/qhistorystate_p.h b/src/statemachine/qhistorystate_p.h
new file mode 100644
index 0000000..d6958b6
--- /dev/null
+++ b/src/statemachine/qhistorystate_p.h
@@ -0,0 +1,60 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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>
+
+#include <QtStateMachine/qabstracttransition.h>
+#include <QtStateMachine/qhistorystate.h>
+#include <QtCore/private/qproperty_p.h>
+
+QT_REQUIRE_CONFIG(statemachine);
+
+QT_BEGIN_NAMESPACE
+
+class QHistoryStatePrivate : public QAbstractStatePrivate
+{
+ Q_DECLARE_PUBLIC(QHistoryState)
+
+public:
+ QHistoryStatePrivate();
+
+ static QHistoryStatePrivate *get(QHistoryState *q)
+ { return q->d_func(); }
+
+ void setDefaultTransition(QAbstractTransition* transition)
+ {
+ q_func()->setDefaultTransition(transition);
+ }
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QHistoryStatePrivate,
+ QAbstractTransition*, defaultTransition,
+ &QHistoryStatePrivate::setDefaultTransition, nullptr);
+
+ void historyTypeChanged()
+ {
+ emit q_func()->historyTypeChanged(QHistoryState::QPrivateSignal());
+ }
+ Q_OBJECT_BINDABLE_PROPERTY(QHistoryStatePrivate, QHistoryState::HistoryType,
+ historyType, &QHistoryStatePrivate::historyTypeChanged);
+
+ QList<QAbstractState*> configuration;
+};
+
+QT_END_NAMESPACE
+
+#endif // QHISTORYSTATE_P_H
diff --git a/src/statemachine/qsignaleventgenerator_p.h b/src/statemachine/qsignaleventgenerator_p.h
new file mode 100644
index 0000000..0a46b1c
--- /dev/null
+++ b/src/statemachine/qsignaleventgenerator_p.h
@@ -0,0 +1,44 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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/private/qglobal_p.h>
+#include <QtCore/qobject.h>
+
+#include <QtStateMachine/qstatemachineglobal.h>
+
+QT_REQUIRE_CONFIG(statemachine);
+
+QT_BEGIN_NAMESPACE
+
+class QStateMachine;
+
+class QSignalEventGenerator : public QObject
+{
+ Q_OBJECT
+public:
+ QSignalEventGenerator(QStateMachine *parent);
+
+private Q_SLOTS:
+ void execute(QMethodRawArguments a);
+
+private:
+ Q_DISABLE_COPY_MOVE(QSignalEventGenerator)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/qsignaltransition.cpp b/src/statemachine/qsignaltransition.cpp
new file mode 100644
index 0000000..46e67dc
--- /dev/null
+++ b/src/statemachine/qsignaltransition.cpp
@@ -0,0 +1,266 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qsignaltransition.h"
+#include "qsignaltransition_p.h"
+#include "qstate.h"
+#include "qstate_p.h"
+#include "qstatemachine.h"
+#include "qstatemachine_p.h"
+#include <qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QSignalTransition
+ \inmodule QtStateMachine
+
+ \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{Qt State Machine Overview}
+ {Qt State Machine Framework}.
+
+ You can subclass QSignalTransition and reimplement eventTest() to make a
+ signal transition conditional; the event object passed to eventTest() will
+ be a QStateMachine::SignalEvent object. Example:
+
+ \code
+ class CheckedTransition : public QSignalTransition
+ {
+ public:
+ CheckedTransition(QCheckBox *check)
+ : QSignalTransition(check, SIGNAL(stateChanged(int))) {}
+ protected:
+ bool eventTest(QEvent *e) {
+ if (!QSignalTransition::eventTest(e))
+ return false;
+ QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(e);
+ return (se->arguments().at(0).toInt() == Qt::Checked);
+ }
+ };
+
+ ...
+
+ QCheckBox *check = new QCheckBox();
+ check->setTristate(true);
+
+ QState *s1 = new QState();
+ QState *s2 = new QState();
+ CheckedTransition *t1 = new CheckedTransition(check);
+ t1->setTargetState(s2);
+ s1->addTransition(t1);
+ \endcode
+*/
+
+/*!
+ \property QSignalTransition::senderObject
+
+ \brief the sender object that this signal transition is associated with
+*/
+
+/*!
+ \property QSignalTransition::signal
+
+ \brief the signal that this signal transition is associated with
+*/
+
+QSignalTransitionPrivate::QSignalTransitionPrivate()
+{
+ signalIndex = -1;
+}
+
+void QSignalTransitionPrivate::unregister()
+{
+ Q_Q(QSignalTransition);
+ if ((signalIndex == -1) || !machine())
+ return;
+ QStateMachinePrivate::get(machine())->unregisterSignalTransition(q);
+}
+
+void QSignalTransitionPrivate::maybeRegister()
+{
+ Q_Q(QSignalTransition);
+ if (QStateMachine *mach = machine())
+ QStateMachinePrivate::get(mach)->maybeRegisterSignalTransition(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(const QObject *sender, const char *signal,
+ QState *sourceState)
+ : QAbstractTransition(*new QSignalTransitionPrivate, sourceState)
+{
+ Q_D(QSignalTransition);
+ d->senderObject.setValueBypassingBindings(sender);
+ d->signal.setValueBypassingBindings(signal);
+ d->maybeRegister();
+}
+
+/*!
+ \fn template <typename PointerToMemberFunction> QSignalTransition::QSignalTransition(const QObject *sender, PointerToMemberFunction signal, QState *sourceState)
+ \since 5.7
+ \overload
+
+ Constructs a new signal transition associated with the given \a signal of
+ the given \a sender object and with the given \a sourceState.
+ This constructor is enabled if the compiler supports delegating constructors,
+ as indicated by the presence of the macro Q_COMPILER_DELEGATING_CONSTRUCTORS.
+*/
+
+/*!
+ Destroys this signal transition.
+*/
+QSignalTransition::~QSignalTransition()
+{
+}
+
+/*!
+ Returns the sender object associated with this signal transition.
+*/
+const QObject *QSignalTransition::senderObject() const
+{
+ Q_D(const QSignalTransition);
+ return d->senderObject;
+}
+
+/*!
+ Sets the \a sender object associated with this signal transition.
+*/
+void QSignalTransition::setSenderObject(const QObject *sender)
+{
+ Q_D(QSignalTransition);
+ d->senderObject.removeBindingUnlessInWrapper();
+ if (sender == d->senderObject.valueBypassingBindings())
+ return;
+ d->unregister();
+ d->senderObject.setValueBypassingBindings(sender);
+ d->maybeRegister();
+ d->senderObject.notify();
+ emit senderObjectChanged(QPrivateSignal());
+}
+
+QBindable<const QObject*> QSignalTransition::bindableSenderObject()
+{
+ Q_D(QSignalTransition);
+ return &d->senderObject;
+}
+
+/*!
+ 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);
+ d->signal.removeBindingUnlessInWrapper();
+ if (signal == d->signal.valueBypassingBindings())
+ return;
+ d->unregister();
+ d->signal.setValueBypassingBindings(signal);
+ d->maybeRegister();
+ d->signal.notify();
+ emit signalChanged(QPrivateSignal());
+}
+
+QBindable<QByteArray> QSignalTransition::bindableSignal()
+{
+ Q_D(QSignalTransition);
+ return &d->signal;
+}
+
+/*!
+ \reimp
+
+ The default implementation returns \c true if the \a event is a
+ QStateMachine::SignalEvent object and the event's sender and signal index
+ match this transition, and returns \c false otherwise.
+*/
+bool QSignalTransition::eventTest(QEvent *event)
+{
+ Q_D(const QSignalTransition);
+ if (event->type() == QEvent::StateMachineSignal) {
+ if (d->signalIndex == -1)
+ return false;
+ QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(event);
+ return (se->sender() == d->senderObject.value())
+ && (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);
+}
+
+/*!
+ \fn QSignalTransition::senderObjectChanged()
+ \since 5.4
+
+ This signal is emitted when the senderObject property is changed.
+
+ \sa QSignalTransition::senderObject
+*/
+
+/*!
+ \fn QSignalTransition::signalChanged()
+ \since 5.4
+
+ This signal is emitted when the signal property is changed.
+
+ \sa QSignalTransition::signal
+*/
+
+void QSignalTransitionPrivate::callOnTransition(QEvent *e)
+{
+ Q_Q(QSignalTransition);
+
+ if (e->type() == QEvent::StateMachineSignal) {
+ QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent *>(e);
+ int savedSignalIndex = se->m_signalIndex;
+ se->m_signalIndex = originalSignalIndex;
+ q->onTransition(e);
+ se->m_signalIndex = savedSignalIndex;
+ } else {
+ q->onTransition(e);
+ }
+}
+
+
+QT_END_NAMESPACE
+
+#include "moc_qsignaltransition.cpp"
diff --git a/src/statemachine/qsignaltransition.h b/src/statemachine/qsignaltransition.h
new file mode 100644
index 0000000..2bfe191
--- /dev/null
+++ b/src/statemachine/qsignaltransition.h
@@ -0,0 +1,67 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSIGNALTRANSITION_H
+#define QSIGNALTRANSITION_H
+
+#include <QtCore/qmetaobject.h>
+#include <QtStateMachine/qabstracttransition.h>
+
+QT_REQUIRE_CONFIG(statemachine);
+
+QT_BEGIN_NAMESPACE
+
+class QSignalTransitionPrivate;
+class Q_STATEMACHINE_EXPORT QSignalTransition : public QAbstractTransition
+{
+ Q_OBJECT
+ Q_PROPERTY(const QObject* senderObject READ senderObject WRITE setSenderObject
+ NOTIFY senderObjectChanged BINDABLE bindableSenderObject)
+ Q_PROPERTY(QByteArray signal READ signal WRITE setSignal
+ NOTIFY signalChanged BINDABLE bindableSignal)
+
+public:
+ QSignalTransition(QState *sourceState = nullptr);
+ QSignalTransition(const QObject *sender, const char *signal,
+ QState *sourceState = nullptr);
+#ifdef Q_QDOC
+ template<typename PointerToMemberFunction>
+ QSignalTransition(const QObject *object, PointerToMemberFunction signal,
+ QState *sourceState = nullptr);
+#elif defined(Q_COMPILER_DELEGATING_CONSTRUCTORS)
+ template <typename Func>
+ QSignalTransition(const typename QtPrivate::FunctionPointer<Func>::Object *obj,
+ Func sig, QState *srcState = nullptr)
+ : QSignalTransition(obj, QMetaMethod::fromSignal(sig).methodSignature().constData(), srcState)
+ {
+ }
+#endif
+
+ ~QSignalTransition();
+
+ const QObject *senderObject() const;
+ void setSenderObject(const QObject *sender);
+ QBindable<const QObject*> bindableSenderObject();
+
+ QByteArray signal() const;
+ void setSignal(const QByteArray &signal);
+ QBindable<QByteArray> bindableSignal();
+
+protected:
+ bool eventTest(QEvent *event) override;
+ void onTransition(QEvent *event) override;
+
+ bool event(QEvent *e) override;
+
+Q_SIGNALS:
+ void senderObjectChanged(QPrivateSignal);
+ void signalChanged(QPrivateSignal);
+
+private:
+ Q_DISABLE_COPY(QSignalTransition)
+ Q_DECLARE_PRIVATE(QSignalTransition)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/qsignaltransition_p.h b/src/statemachine/qsignaltransition_p.h
new file mode 100644
index 0000000..2da77e6
--- /dev/null
+++ b/src/statemachine/qsignaltransition_p.h
@@ -0,0 +1,60 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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"
+#include <QtCore/private/qproperty_p.h>
+
+QT_REQUIRE_CONFIG(statemachine);
+
+QT_BEGIN_NAMESPACE
+
+class QSignalTransition;
+class QSignalTransitionPrivate : public QAbstractTransitionPrivate
+{
+ Q_DECLARE_PUBLIC(QSignalTransition)
+public:
+ QSignalTransitionPrivate();
+
+ static QSignalTransitionPrivate *get(QSignalTransition *q)
+ { return q->d_func(); }
+
+ void unregister();
+ void maybeRegister();
+
+ void callOnTransition(QEvent *e) override;
+
+ void setSenderObject(const QObject* sender)
+ {
+ q_func()->setSenderObject(sender);
+ }
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QSignalTransitionPrivate, const QObject*,
+ senderObject, &QSignalTransitionPrivate::setSenderObject,
+ nullptr);
+
+ void setSignal(const QByteArray& signal)
+ {
+ q_func()->setSignal(signal);
+ }
+ Q_OBJECT_COMPAT_PROPERTY(QSignalTransitionPrivate, QByteArray,
+ signal, &QSignalTransitionPrivate::setSignal);
+ int signalIndex;
+ int originalSignalIndex;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/qstate.cpp b/src/statemachine/qstate.cpp
new file mode 100644
index 0000000..dc8d173
--- /dev/null
+++ b/src/statemachine/qstate.cpp
@@ -0,0 +1,574 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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
+ \inmodule QtStateMachine
+
+ \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{Qt State Machine Overview}{Qt State Machine Framework}.
+
+ The addTransition() function adds a transition. The removeTransition()
+ function removes a transition. The transitions() function returns the
+ state's outgoing transitions.
+
+ The assignProperty() function is used for defining property assignments that
+ should be performed when a state is entered.
+
+ Top-level states must be passed a QStateMachine object as their parent
+ state, or added to a state machine using QStateMachine::addState().
+
+ \section1 States with Child States
+
+ The childMode property determines how child states are treated. For
+ non-parallel state groups, the setInitialState() function must be called to
+ set the initial state. The child states are mutually exclusive states, and
+ the state machine needs to know which child state to enter when the parent
+ state is the target of a transition.
+
+ The state emits the QState::finished() signal when a final child state
+ (QFinalState) is entered.
+
+ The setErrorState() sets the state's error state. The error state is the
+ state that the state machine will transition to if an error is detected when
+ attempting to enter the state (e.g. because no initial state has been set).
+
+*/
+
+/*!
+ \property QState::initialState
+
+ \brief the initial state of this state (one of its child states)
+*/
+
+/*!
+ \property QState::errorState
+
+ \brief the error state of this state
+*/
+
+/*!
+ \property QState::childMode
+
+ \brief the child mode of this state
+
+ The default value of this property is QState::ExclusiveStates.
+*/
+
+/*!
+ \enum QState::ChildMode
+
+ This enum specifies how a state's child states are treated.
+
+ \value ExclusiveStates The child states are mutually exclusive and an
+ initial state must be set by calling QState::setInitialState().
+
+ \value ParallelStates The child states are parallel. When the parent state
+ is entered, all its child states are entered in parallel.
+*/
+
+/*!
+ \enum QState::RestorePolicy
+
+ This enum specifies the restore policy type. The restore policy
+ takes effect when the machine enters a state which sets one or more
+ properties. If the restore policy is set to RestoreProperties,
+ the state machine will save the original value of the property before the
+ new value is set.
+
+ Later, when the machine either enters a state which does not set
+ a value for the given property, the property will automatically be restored
+ to its initial value.
+
+ Only one initial value will be saved for any given property. If a value for a property has
+ already been saved by the state machine, it will not be overwritten until the property has been
+ successfully restored.
+
+ \value DontRestoreProperties The state machine should not save the initial values of properties
+ and restore them later.
+ \value RestoreProperties The state machine should save the initial values of properties
+ and restore them later.
+
+ \sa QStateMachine::globalRestorePolicy, QState::assignProperty()
+*/
+
+QStatePrivate::QStatePrivate()
+ : QAbstractStatePrivate(StandardState),
+ childStatesListNeedsRefresh(true), transitionsListNeedsRefresh(true)
+{
+}
+
+QStatePrivate::~QStatePrivate()
+{
+}
+
+void QStatePrivate::emitFinished()
+{
+ Q_Q(QState);
+ emit q->finished(QState::QPrivateSignal());
+}
+
+void QStatePrivate::emitPropertiesAssigned()
+{
+ Q_Q(QState);
+ emit q->propertiesAssigned(QState::QPrivateSignal());
+}
+
+/*!
+ Constructs a new state with the given \a parent state.
+*/
+QState::QState(QState *parent)
+ : QAbstractState(*new QStatePrivate, parent)
+{
+}
+
+/*!
+ Constructs a new state with the given \a childMode and the given \a parent
+ state.
+*/
+QState::QState(ChildMode childMode, QState *parent)
+ : QAbstractState(*new QStatePrivate, parent)
+{
+ Q_D(QState);
+ d->childMode = childMode;
+}
+
+/*!
+ \internal
+*/
+QState::QState(QStatePrivate &dd, QState *parent)
+ : QAbstractState(dd, parent)
+{
+}
+
+/*!
+ Destroys this state.
+*/
+QState::~QState()
+{
+}
+
+QList<QAbstractState*> QStatePrivate::childStates() const
+{
+ if (childStatesListNeedsRefresh) {
+ childStatesList.clear();
+ QList<QObject*>::const_iterator it;
+ for (it = children.constBegin(); it != children.constEnd(); ++it) {
+ QAbstractState *s = qobject_cast<QAbstractState*>(*it);
+ if (!s || qobject_cast<QHistoryState*>(s))
+ continue;
+ childStatesList.append(s);
+ }
+ childStatesListNeedsRefresh = false;
+ }
+ return childStatesList;
+}
+
+QList<QHistoryState*> QStatePrivate::historyStates() const
+{
+ QList<QHistoryState*> result;
+ QList<QObject*>::const_iterator it;
+ for (it = children.constBegin(); it != children.constEnd(); ++it) {
+ QHistoryState *h = qobject_cast<QHistoryState*>(*it);
+ if (h)
+ result.append(h);
+ }
+ return result;
+}
+
+QList<QAbstractTransition*> QStatePrivate::transitions() const
+{
+ if (transitionsListNeedsRefresh) {
+ transitionsList.clear();
+ QList<QObject*>::const_iterator it;
+ for (it = children.constBegin(); it != children.constEnd(); ++it) {
+ QAbstractTransition *t = qobject_cast<QAbstractTransition*>(*it);
+ if (t)
+ transitionsList.append(t);
+ }
+ transitionsListNeedsRefresh = false;
+ }
+ return transitionsList;
+}
+
+#ifndef QT_NO_PROPERTIES
+
+/*!
+ Instructs this state to set the property with the given \a name of the given
+ \a object to the given \a value when the state is entered.
+
+ \sa propertiesAssigned()
+*/
+void QState::assignProperty(QObject *object, const char *name,
+ const QVariant &value)
+{
+ Q_D(QState);
+ if (!object) {
+ qWarning("QState::assignProperty: cannot assign property '%s' of null object", name);
+ return;
+ }
+ for (int i = 0; i < d->propertyAssignments.size(); ++i) {
+ QPropertyAssignment &assn = d->propertyAssignments[i];
+ if (assn.hasTarget(object, name)) {
+ assn.value = value;
+ return;
+ }
+ }
+ d->propertyAssignments.append(QPropertyAssignment(object, name, value));
+}
+
+#endif // QT_NO_PROPERTIES
+
+/*!
+ Returns this state's error state.
+
+ \sa QStateMachine::error()
+*/
+QAbstractState *QState::errorState() const
+{
+ Q_D(const QState);
+ return d->errorState;
+}
+
+/*!
+ Sets this state's error state to be the given \a state. If the error state
+ is not set, or if it is set to \nullptr, the state will inherit its parent's error
+ state recursively. If no error state is set for the state itself or any of
+ its ancestors, an error will cause the machine to stop executing and an error
+ will be printed to the console.
+*/
+void QState::setErrorState(QAbstractState *state)
+{
+ Q_D(QState);
+ if (state != nullptr && qobject_cast<QStateMachine*>(state)) {
+ qWarning("QStateMachine::setErrorState: root state cannot be error state");
+ return;
+ }
+ if (state != nullptr && (!state->machine() || ((state->machine() != machine()) && !qobject_cast<QStateMachine*>(this)))) {
+ qWarning("QState::setErrorState: error state cannot belong "
+ "to a different state machine");
+ return;
+ }
+ d->errorState = state;
+}
+
+QBindable<QAbstractState*> QState::bindableErrorState()
+{
+ Q_D(QState);
+ return &d->errorState;
+}
+
+/*!
+ Adds the given \a transition. The transition has this state as the source.
+ This state takes ownership of the transition.
+*/
+void QState::addTransition(QAbstractTransition *transition)
+{
+ Q_D(QState);
+ if (!transition) {
+ qWarning("QState::addTransition: cannot add null transition");
+ return ;
+ }
+
+ transition->setParent(this);
+ const QList<QPointer<QAbstractState>> &targets = QAbstractTransitionPrivate::get(transition)->targetStates;
+ for (int i = 0; i < targets.size(); ++i) {
+ QAbstractState *t = targets.at(i).data();
+ if (!t) {
+ qWarning("QState::addTransition: cannot add transition to null state");
+ return ;
+ }
+ if ((QAbstractStatePrivate::get(t)->machine() != d->machine())
+ && QAbstractStatePrivate::get(t)->machine() && d->machine()) {
+ qWarning("QState::addTransition: cannot add transition "
+ "to a state in a different state machine");
+ return ;
+ }
+ }
+ if (QStateMachine *mach = machine())
+ QStateMachinePrivate::get(mach)->maybeRegisterTransition(transition);
+}
+
+/*!
+ \fn template <typename PointerToMemberFunction> QState::addTransition(const QObject *sender, PointerToMemberFunction signal, QAbstractState *target);
+ \since 5.5
+ \overload
+
+ 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.
+*/
+
+/*!
+ 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(const QObject *sender, const char *signal,
+ QAbstractState *target)
+{
+ if (!sender) {
+ qWarning("QState::addTransition: sender cannot be null");
+ return nullptr;
+ }
+ if (!signal) {
+ qWarning("QState::addTransition: signal cannot be null");
+ return nullptr;
+ }
+ if (!target) {
+ qWarning("QState::addTransition: cannot add transition to null state");
+ return nullptr;
+ }
+ int offset = (*signal == '0'+QSIGNAL_CODE) ? 1 : 0;
+ const QMetaObject *meta = sender->metaObject();
+ if (meta->indexOfSignal(signal+offset) == -1) {
+ if (meta->indexOfSignal(QMetaObject::normalizedSignature(signal+offset)) == -1) {
+ qWarning("QState::addTransition: no such signal %s::%s",
+ meta->className(), signal+offset);
+ return nullptr;
+ }
+ }
+ QSignalTransition *trans = new QSignalTransition(sender, signal);
+ trans->setTargetState(target);
+ addTransition(trans);
+ return trans;
+}
+
+namespace {
+
+// ### Make public?
+class UnconditionalTransition : public QAbstractTransition
+{
+public:
+ UnconditionalTransition(QAbstractState *target)
+ : QAbstractTransition()
+ { setTargetState(target); }
+protected:
+ void onTransition(QEvent *) override {}
+ bool eventTest(QEvent *) override { 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 nullptr;
+ }
+ UnconditionalTransition *trans = new UnconditionalTransition(target);
+ addTransition(trans);
+ return trans;
+}
+
+/*!
+ Removes the given \a transition from this state. The state releases
+ ownership of the transition.
+
+ \sa addTransition()
+*/
+void QState::removeTransition(QAbstractTransition *transition)
+{
+ Q_D(QState);
+ if (!transition) {
+ qWarning("QState::removeTransition: cannot remove null transition");
+ return;
+ }
+ if (transition->sourceState() != this) {
+ qWarning("QState::removeTransition: transition %p's source state (%p)"
+ " is different from this state (%p)",
+ transition, transition->sourceState(), this);
+ return;
+ }
+ QStateMachinePrivate *mach = QStateMachinePrivate::get(d->machine());
+ if (mach)
+ mach->unregisterTransition(transition);
+ transition->setParent(nullptr);
+}
+
+/*!
+ \since 4.7
+
+ Returns this state's outgoing transitions (i.e. transitions where
+ this state is the \l [CPP] {QAbstractTransition::sourceState()}{source
+ state}), or an empty list if this state has no outgoing transitions.
+
+ \sa addTransition()
+*/
+QList<QAbstractTransition*> QState::transitions() const
+{
+ Q_D(const QState);
+ return d->transitions();
+}
+
+/*!
+ \reimp
+*/
+void QState::onEntry(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+*/
+void QState::onExit(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ Returns this state's initial state, or \nullptr 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;
+}
+
+QBindable<QAbstractState*> QState::bindableInitialState()
+{
+ Q_D(QState);
+ return &d->initialState;
+}
+
+/*!
+ 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);
+
+ if (mode == QState::ParallelStates && d->initialState.value()) {
+ qWarning("QState::setChildMode: setting the child-mode of state %p to "
+ "parallel removes the initial state", this);
+ d->initialState.setValue(nullptr);
+ }
+ d->childMode = mode;
+}
+
+QBindable<QState::ChildMode> QState::bindableChildMode()
+{
+ Q_D(QState);
+ return &d->childMode;
+}
+
+/*!
+ \reimp
+*/
+bool QState::event(QEvent *e)
+{
+ Q_D(QState);
+ if ((e->type() == QEvent::ChildAdded) || (e->type() == QEvent::ChildRemoved)) {
+ d->childStatesListNeedsRefresh = true;
+ d->transitionsListNeedsRefresh = true;
+ if ((e->type() == QEvent::ChildRemoved)
+ && (static_cast<QChildEvent *>(e)->child() == d->initialState.value())) {
+ d->initialState.setValue(nullptr);
+ }
+ }
+ return QAbstractState::event(e);
+}
+
+/*!
+ \fn QState::finished()
+
+ This signal is emitted when a final child state of this state is entered.
+
+ \sa QFinalState
+*/
+
+/*!
+ \fn QState::propertiesAssigned()
+
+ This signal is emitted when all properties have been assigned their final value. If the state
+ assigns a value to one or more properties for which an animation exists (either set on the
+ transition or as a default animation on the state machine), then the signal will not be emitted
+ until all such animations have finished playing.
+
+ If there are no relevant animations, or no property assignments defined for the state, then
+ the signal will be emitted immediately before the state is entered.
+
+ \b {See also} \l QState::assignProperty() and \l [CPP] {QAbstractTransition::addAnimation()}
+*/
+
+/*!
+ \fn QState::childModeChanged()
+ \since 5.4
+
+ This signal is emitted when the childMode property is changed.
+
+ \sa QState::childMode
+*/
+
+/*!
+ \fn QState::initialStateChanged()
+ \since 5.4
+
+ This signal is emitted when the initialState property is changed.
+
+ \sa QState::initialState
+*/
+
+/*!
+ \fn QState::errorStateChanged()
+ \since 5.4
+
+ This signal is emitted when the errorState property is changed.
+
+ \sa QState::errorState
+*/
+
+QT_END_NAMESPACE
+
+#include "moc_qstate.cpp"
diff --git a/src/statemachine/qstate.h b/src/statemachine/qstate.h
new file mode 100644
index 0000000..312c995
--- /dev/null
+++ b/src/statemachine/qstate.h
@@ -0,0 +1,105 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSTATE_H
+#define QSTATE_H
+
+#include <QtCore/qlist.h>
+#include <QtCore/qmetaobject.h>
+
+#include <QtStateMachine/qabstractstate.h>
+
+QT_REQUIRE_CONFIG(statemachine);
+
+QT_BEGIN_NAMESPACE
+
+class QAbstractTransition;
+class QSignalTransition;
+
+class QStatePrivate;
+class Q_STATEMACHINE_EXPORT QState : public QAbstractState
+{
+ Q_OBJECT
+ Q_PROPERTY(QAbstractState* initialState READ initialState WRITE setInitialState
+ NOTIFY initialStateChanged BINDABLE bindableInitialState)
+ Q_PROPERTY(QAbstractState* errorState READ errorState WRITE setErrorState
+ NOTIFY errorStateChanged BINDABLE bindableErrorState)
+ Q_PROPERTY(ChildMode childMode READ childMode WRITE setChildMode
+ NOTIFY childModeChanged BINDABLE bindableChildMode)
+public:
+ enum ChildMode {
+ ExclusiveStates,
+ ParallelStates
+ };
+ Q_ENUM(ChildMode)
+
+ enum RestorePolicy {
+ DontRestoreProperties,
+ RestoreProperties
+ };
+ Q_ENUM(RestorePolicy)
+
+ QState(QState *parent = nullptr);
+ QState(ChildMode childMode, QState *parent = nullptr);
+ ~QState();
+
+ QAbstractState *errorState() const;
+ void setErrorState(QAbstractState *state);
+ QBindable<QAbstractState*> bindableErrorState();
+
+ void addTransition(QAbstractTransition *transition);
+ QSignalTransition *addTransition(const QObject *sender, const char *signal, QAbstractState *target);
+#ifdef Q_QDOC
+ template<typename PointerToMemberFunction>
+ QSignalTransition *addTransition(const QObject *sender, PointerToMemberFunction signal,
+ QAbstractState *target);
+#else
+ template <typename Func>
+ QSignalTransition *addTransition(const typename QtPrivate::FunctionPointer<Func>::Object *obj,
+ Func signal, QAbstractState *target)
+ {
+ const QMetaMethod signalMetaMethod = QMetaMethod::fromSignal(signal);
+ return addTransition(obj, signalMetaMethod.methodSignature().constData(), target);
+ }
+#endif // Q_QDOC
+ QAbstractTransition *addTransition(QAbstractState *target);
+ void removeTransition(QAbstractTransition *transition);
+ QList<QAbstractTransition*> transitions() const;
+
+ QAbstractState *initialState() const;
+ void setInitialState(QAbstractState *state);
+ QBindable<QAbstractState*> bindableInitialState();
+
+ ChildMode childMode() const;
+ void setChildMode(ChildMode mode);
+ QBindable<QState::ChildMode> bindableChildMode();
+
+#ifndef QT_NO_PROPERTIES
+ void assignProperty(QObject *object, const char *name,
+ const QVariant &value);
+#endif
+
+Q_SIGNALS:
+ void finished(QPrivateSignal);
+ void propertiesAssigned(QPrivateSignal);
+ void childModeChanged(QPrivateSignal);
+ void initialStateChanged(QPrivateSignal);
+ void errorStateChanged(QPrivateSignal);
+
+protected:
+ void onEntry(QEvent *event) override;
+ void onExit(QEvent *event) override;
+
+ bool event(QEvent *e) override;
+
+protected:
+ QState(QStatePrivate &dd, QState *parent);
+
+private:
+ Q_DISABLE_COPY(QState)
+ Q_DECLARE_PRIVATE(QState)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/qstate_p.h b/src/statemachine/qstate_p.h
new file mode 100644
index 0000000..7d54d6d
--- /dev/null
+++ b/src/statemachine/qstate_p.h
@@ -0,0 +1,110 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#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 "qstate.h"
+#include "private/qabstractstate_p.h"
+
+#include <QtCore/qlist.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/private/qproperty_p.h>
+
+QT_REQUIRE_CONFIG(statemachine);
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_PROPERTIES
+
+struct QPropertyAssignment
+{
+ QPropertyAssignment()
+ : object(nullptr), explicitlySet(true) {}
+ QPropertyAssignment(QObject *o, const QByteArray &n,
+ const QVariant &v, bool es = true)
+ : object(o), propertyName(n), value(v), explicitlySet(es)
+ {}
+
+ bool objectDeleted() const { return !object; }
+ void write() const { Q_ASSERT(object != nullptr); object->setProperty(propertyName, value); }
+ bool hasTarget(QObject *o, const QByteArray &pn) const
+ { return object == o && propertyName == pn; }
+
+ QPointer<QObject> object;
+ QByteArray propertyName;
+ QVariant value;
+ bool explicitlySet; // false means the property is being restored to its old value
+};
+Q_DECLARE_TYPEINFO(QPropertyAssignment, Q_RELOCATABLE_TYPE);
+
+#endif // QT_NO_PROPERTIES
+
+class QAbstractTransition;
+class QHistoryState;
+
+class QState;
+class Q_STATEMACHINE_EXPORT QStatePrivate : public QAbstractStatePrivate
+{
+ Q_DECLARE_PUBLIC(QState)
+public:
+ QStatePrivate();
+ ~QStatePrivate();
+
+ static QStatePrivate *get(QState *q) { return q ? q->d_func() : nullptr; }
+ static const QStatePrivate *get(const QState *q) { return q? q->d_func() : nullptr; }
+
+ QList<QAbstractState*> childStates() const;
+ QList<QHistoryState*> historyStates() const;
+ QList<QAbstractTransition*> transitions() const;
+
+ void emitFinished();
+ void emitPropertiesAssigned();
+
+ void initialStateChanged()
+ {
+ emit q_func()->initialStateChanged(QState::QPrivateSignal());
+ }
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QStatePrivate, QAbstractState*, initialState,
+ nullptr, &QStatePrivate::initialStateChanged);
+
+ void errorStateChanged()
+ {
+ emit q_func()->errorStateChanged(QState::QPrivateSignal());
+ }
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QStatePrivate, QAbstractState*, errorState,
+ nullptr, &QStatePrivate::errorStateChanged);
+
+ void childModeChanged()
+ {
+ emit q_func()->childModeChanged(QState::QPrivateSignal());
+ }
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QStatePrivate, QState::ChildMode, childMode,
+ QState::ExclusiveStates, &QStatePrivate::childModeChanged);
+
+ mutable bool childStatesListNeedsRefresh;
+ mutable bool transitionsListNeedsRefresh;
+ mutable QList<QAbstractState*> childStatesList;
+ mutable QList<QAbstractTransition*> transitionsList;
+
+#ifndef QT_NO_PROPERTIES
+ QList<QPropertyAssignment> propertyAssignments;
+#endif
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/qstatemachine.cpp b/src/statemachine/qstatemachine.cpp
new file mode 100644
index 0000000..acf5b4b
--- /dev/null
+++ b/src/statemachine/qstatemachine.cpp
@@ -0,0 +1,3188 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qstatemachine.h"
+#include "qstate.h"
+#include "qstate_p.h"
+#include "qstatemachine_p.h"
+#include "qabstracttransition.h"
+#include "qabstracttransition_p.h"
+#include "qsignaltransition.h"
+#include "qsignaltransition_p.h"
+#include "qsignaleventgenerator_p.h"
+#include "qabstractstate.h"
+#include "qabstractstate_p.h"
+#include "qfinalstate.h"
+#include "qhistorystate.h"
+#include "qhistorystate_p.h"
+
+#include "private/qcoreapplication_p.h"
+#include "private/qobject_p.h"
+#include "private/qthread_p.h"
+
+#if QT_CONFIG(qeventtransition)
+#include "qeventtransition.h"
+#include "qeventtransition_p.h"
+#endif
+
+#if QT_CONFIG(animation)
+#include "qpropertyanimation.h"
+#include "qanimationgroup.h"
+#include <private/qvariantanimation_p.h>
+#endif
+
+#include <QtCore/qmetaobject.h>
+#include <qdebug.h>
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+enum {
+ Offset0 = 0x00000000,
+ Offset1 = 0x00008000,
+ Offset2 = 0x00080000,
+ Offset3 = 0x00800000,
+
+ Size0 = Offset1 - Offset0,
+ Size1 = Offset2 - Offset1,
+ Size2 = Offset3 - Offset2,
+ Size3 = QStateMachinePrivate::FreeListDefaultConstants::MaxIndex - Offset3
+};
+
+const int QStateMachinePrivate::FreeListDefaultConstants::Sizes[FreeListDefaultConstants::BlockCount] = {
+ Size0,
+ Size1,
+ Size2,
+ Size3
+};
+
+/*!
+ \class QStateMachine
+ \inmodule QtStateMachine
+ \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{http://www.wisdom.weizmann.ac.il/~dharel/SCANNED.PAPERS/Statecharts.pdf}{Statecharts}.
+ QStateMachine is part of \l{Qt State Machine Overview}{Qt 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{http://www.w3.org/TR/scxml/}{State Chart XML (SCXML)}
+ algorithm. The framework's \l{Qt State Machine Overview}{overview} gives several state
+ graphs and the code to build them.
+
+ Use the addState() function to add a top-level state to the state machine.
+ States are removed with the removeState() function. Removing states while
+ the machine is running is discouraged.
+
+ Before the machine can be started, the \l{initialState}{initial
+ state} must be set. The initial state is the state that the
+ machine enters when started. You can then start() the state
+ machine. The started() signal is emitted when the initial state is
+ entered.
+
+ The machine is event driven and keeps its own event loop. Events
+ are posted to the machine through postEvent(). Note that this
+ means that it executes asynchronously, and that it will not
+ progress without a running event loop. You will normally not have
+ to post events to the machine directly as Qt's transitions, e.g.,
+ QEventTransition and its subclasses, handle this. But for custom
+ transitions triggered by events, postEvent() is useful.
+
+ The state machine processes events and takes transitions until a
+ top-level final state is entered; the state machine then emits the
+ finished() signal. You can also stop() the state machine
+ explicitly. The stopped() signal is emitted in this case.
+
+ The following snippet shows a state machine that will finish when a button
+ is clicked:
+
+ \snippet code/src_corelib_statemachine_qstatemachine.cpp simple state machine
+
+ This code example uses QState, which inherits QAbstractState. The
+ QState class provides a state that you can use to set properties
+ and invoke methods on \l{QObject}s when the state is entered or
+ exited. It also contains convenience functions for adding
+ transitions, e.g., \l{QSignalTransition}s as in this example. See
+ the QState class description for further details.
+
+ If an error is encountered, the machine will look for an
+ \l{errorState}{error state}, and if one is available, it will
+ enter this state. The types of errors possible are described by the
+ \l{QStateMachine::}{Error} enum. After the error state is entered,
+ the type of the error can be retrieved with error(). The execution
+ of the state graph will not stop when the error state is entered. If
+ no error state applies to the erroneous state, the machine will stop
+ executing and an error message will be printed to the console.
+
+ \note Important: setting the \l{ChildMode} of a state machine to parallel (\l{ParallelStates})
+ results in an invalid state machine. It can only be set to (or kept as)
+ \l{ExclusiveStates}.
+
+ \sa QAbstractState, QAbstractTransition, QState, {Qt State Machine Overview}
+*/
+
+/*!
+ \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
+ QState::DontRestoreProperties.
+*/
+
+/*!
+ \property QStateMachine::running
+ \since 5.4
+
+ \brief the running state of this state machine
+
+ \sa start(), stop(), started(), stopped(), runningChanged()
+*/
+
+#if QT_CONFIG(animation)
+/*!
+ \property QStateMachine::animated
+
+ \brief whether animations are enabled
+
+ The default value of this property is \c true.
+
+ \b {See also} \l [CPP] QAbstractTransition::addAnimation()
+*/
+#endif
+
+// #define QSTATEMACHINE_DEBUG
+// #define QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
+
+struct CalculationCache {
+ struct TransitionInfo {
+ QList<QAbstractState*> effectiveTargetStates;
+ QSet<QAbstractState*> exitSet;
+ QAbstractState *transitionDomain;
+
+ bool effectiveTargetStatesIsKnown: 1;
+ bool exitSetIsKnown : 1;
+ bool transitionDomainIsKnown : 1;
+
+ TransitionInfo()
+ : transitionDomain(nullptr)
+ , effectiveTargetStatesIsKnown(false)
+ , exitSetIsKnown(false)
+ , transitionDomainIsKnown(false)
+ {}
+ };
+
+ typedef QHash<QAbstractTransition *, TransitionInfo> TransitionInfoCache;
+ TransitionInfoCache cache;
+
+ bool effectiveTargetStates(QAbstractTransition *t, QList<QAbstractState *> *targets) const
+ {
+ Q_ASSERT(targets);
+
+ TransitionInfoCache::const_iterator cacheIt = cache.find(t);
+ if (cacheIt == cache.end() || !cacheIt->effectiveTargetStatesIsKnown)
+ return false;
+
+ *targets = cacheIt->effectiveTargetStates;
+ return true;
+ }
+
+ void insert(QAbstractTransition *t, const QList<QAbstractState *> &targets)
+ {
+ TransitionInfoCache::iterator cacheIt = cache.find(t);
+ TransitionInfo &ti = cacheIt == cache.end()
+ ? *cache.insert(t, TransitionInfo())
+ : *cacheIt;
+
+ Q_ASSERT(!ti.effectiveTargetStatesIsKnown);
+ ti.effectiveTargetStates = targets;
+ ti.effectiveTargetStatesIsKnown = true;
+ }
+
+ bool exitSet(QAbstractTransition *t, QSet<QAbstractState *> *exits) const
+ {
+ Q_ASSERT(exits);
+
+ TransitionInfoCache::const_iterator cacheIt = cache.find(t);
+ if (cacheIt == cache.end() || !cacheIt->exitSetIsKnown)
+ return false;
+
+ *exits = cacheIt->exitSet;
+ return true;
+ }
+
+ void insert(QAbstractTransition *t, const QSet<QAbstractState *> &exits)
+ {
+ TransitionInfoCache::iterator cacheIt = cache.find(t);
+ TransitionInfo &ti = cacheIt == cache.end()
+ ? *cache.insert(t, TransitionInfo())
+ : *cacheIt;
+
+ Q_ASSERT(!ti.exitSetIsKnown);
+ ti.exitSet = exits;
+ ti.exitSetIsKnown = true;
+ }
+
+ bool transitionDomain(QAbstractTransition *t, QAbstractState **domain) const
+ {
+ Q_ASSERT(domain);
+
+ TransitionInfoCache::const_iterator cacheIt = cache.find(t);
+ if (cacheIt == cache.end() || !cacheIt->transitionDomainIsKnown)
+ return false;
+
+ *domain = cacheIt->transitionDomain;
+ return true;
+ }
+
+ void insert(QAbstractTransition *t, QAbstractState *domain)
+ {
+ TransitionInfoCache::iterator cacheIt = cache.find(t);
+ TransitionInfo &ti = cacheIt == cache.end()
+ ? *cache.insert(t, TransitionInfo())
+ : *cacheIt;
+
+ Q_ASSERT(!ti.transitionDomainIsKnown);
+ ti.transitionDomain = domain;
+ ti.transitionDomainIsKnown = true;
+ }
+};
+
+/* The function as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ :
+
+function isDescendant(state1, state2)
+
+Returns 'true' if state1 is a descendant of state2 (a child, or a child of a child, or a child of a
+child of a child, etc.) Otherwise returns 'false'.
+*/
+static inline bool isDescendant(const QAbstractState *state1, const QAbstractState *state2)
+{
+ Q_ASSERT(state1 != nullptr);
+
+ for (QAbstractState *it = state1->parentState(); it != nullptr; it = it->parentState()) {
+ if (it == state2)
+ return true;
+ }
+
+ return false;
+}
+
+static bool containsDecendantOf(const QSet<QAbstractState *> &states, const QAbstractState *node)
+{
+ for (QAbstractState *s : states)
+ if (isDescendant(s, node))
+ return true;
+
+ return false;
+}
+
+static int descendantDepth(const QAbstractState *state, const QAbstractState *ancestor)
+{
+ int depth = 0;
+ for (const QAbstractState *it = state; it != nullptr; it = it->parentState()) {
+ if (it == ancestor)
+ break;
+ ++depth;
+ }
+ return depth;
+}
+
+/* The function as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ :
+
+function getProperAncestors(state1, state2)
+
+If state2 is null, returns the set of all ancestors of state1 in ancestry order (state1's parent
+followed by the parent's parent, etc. up to an including the <scxml> element). If state2 is
+non-null, returns in ancestry order the set of all ancestors of state1, up to but not including
+state2. (A "proper ancestor" of a state is its parent, or the parent's parent, or the parent's
+parent's parent, etc.))If state2 is state1's parent, or equal to state1, or a descendant of state1,
+this returns the empty set.
+*/
+static QList<QState *> getProperAncestors(const QAbstractState *state, const QAbstractState *upperBound)
+{
+ Q_ASSERT(state != nullptr);
+ QList<QState *> result;
+ result.reserve(16);
+ for (QState *it = state->parentState(); it && it != upperBound; it = it->parentState()) {
+ result.append(it);
+ }
+ return result;
+}
+
+/* The function as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ :
+
+function getEffectiveTargetStates(transition)
+
+Returns the states that will be the target when 'transition' is taken, dereferencing any history states.
+
+function getEffectiveTargetStates(transition)
+ targets = new OrderedSet()
+ for s in transition.target
+ if isHistoryState(s):
+ if historyValue[s.id]:
+ targets.union(historyValue[s.id])
+ else:
+ targets.union(getEffectiveTargetStates(s.transition))
+ else:
+ targets.add(s)
+ return targets
+*/
+static QList<QAbstractState *> getEffectiveTargetStates(QAbstractTransition *transition, CalculationCache *cache)
+{
+ Q_ASSERT(cache);
+
+ QList<QAbstractState *> targetsList;
+ if (cache->effectiveTargetStates(transition, &targetsList))
+ return targetsList;
+
+ QSet<QAbstractState *> targets;
+ const auto targetStates = transition->targetStates();
+ for (QAbstractState *s : targetStates) {
+ if (QHistoryState *historyState = QStateMachinePrivate::toHistoryState(s)) {
+ QList<QAbstractState*> historyConfiguration = QHistoryStatePrivate::get(historyState)->configuration;
+ if (!historyConfiguration.isEmpty()) {
+ // There is a saved history, so apply that.
+ targets.unite(QSet<QAbstractState *>(historyConfiguration.constBegin(), historyConfiguration.constEnd()));
+ } else if (QAbstractTransition *defaultTransition = historyState->defaultTransition()) {
+ // No saved history, take all default transition targets.
+ const auto &targetStates = defaultTransition->targetStates();
+ targets.unite(QSet<QAbstractState *>(targetStates.constBegin(), targetStates.constEnd()));
+ } else {
+ // Woops, we found a history state without a default state. That's not valid!
+ QStateMachinePrivate *m = QStateMachinePrivate::get(historyState->machine());
+ m->setError(QStateMachine::NoDefaultStateInHistoryStateError, historyState);
+ }
+ } else {
+ targets.insert(s);
+ }
+ }
+
+ targetsList = targets.values();
+ cache->insert(transition, targetsList);
+ return targetsList;
+}
+
+QStateMachinePrivate::QStateMachinePrivate()
+{
+ isMachine = true;
+
+ state = NotRunning;
+ processing = false;
+ processingScheduled = false;
+ stop = false;
+ stopProcessingReason = EventQueueEmpty;
+ error = QStateMachine::NoError;
+ signalEventGenerator = nullptr;
+}
+
+QStateMachinePrivate::~QStateMachinePrivate()
+{
+ qDeleteAll(internalEventQueue);
+ qDeleteAll(externalEventQueue);
+
+ for (QHash<int, DelayedEvent>::const_iterator it = delayedEvents.cbegin(), eit = delayedEvents.cend(); it != eit; ++it) {
+ delete it.value().event;
+ }
+}
+
+QState *QStateMachinePrivate::rootState() const
+{
+ return const_cast<QStateMachine*>(q_func());
+}
+
+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) || isDescendant(desc, c)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+bool QStateMachinePrivate::transitionStateEntryLessThan(QAbstractTransition *t1, QAbstractTransition *t2)
+{
+ QState *s1 = t1->sourceState(), *s2 = t2->sourceState();
+ if (s1 == s2) {
+ QList<QAbstractTransition*> transitions = QStatePrivate::get(s1)->transitions();
+ return transitions.indexOf(t1) < transitions.indexOf(t2);
+ } else if (isDescendant(s1, s2)) {
+ return true;
+ } else if (isDescendant(s2, s1)) {
+ return false;
+ } else {
+ Q_ASSERT(s1->machine() != nullptr);
+ QStateMachinePrivate *mach = QStateMachinePrivate::get(s1->machine());
+ QState *lca = mach->findLCA(QList<QAbstractState*>() << s1 << s2);
+ Q_ASSERT(lca != nullptr);
+ int s1Depth = descendantDepth(s1, lca);
+ int s2Depth = descendantDepth(s2, lca);
+ if (s1Depth == s2Depth)
+ return (indexOfDescendant(lca, s1) < indexOfDescendant(lca, s2));
+ else
+ return s1Depth > s2Depth;
+ }
+}
+
+bool QStateMachinePrivate::stateEntryLessThan(QAbstractState *s1, QAbstractState *s2)
+{
+ if (s1->parent() == s2->parent()) {
+ return s1->parent()->children().indexOf(s1)
+ < s2->parent()->children().indexOf(s2);
+ } else if (isDescendant(s1, s2)) {
+ return false;
+ } else if (isDescendant(s2, s1)) {
+ return true;
+ } else {
+ Q_ASSERT(s1->machine() != nullptr);
+ QStateMachinePrivate *mach = QStateMachinePrivate::get(s1->machine());
+ QState *lca = mach->findLCA(QList<QAbstractState*>() << s1 << s2);
+ Q_ASSERT(lca != nullptr);
+ return (indexOfDescendant(lca, s1) < indexOfDescendant(lca, s2));
+ }
+}
+
+bool QStateMachinePrivate::stateExitLessThan(QAbstractState *s1, QAbstractState *s2)
+{
+ if (s1->parent() == s2->parent()) {
+ return s2->parent()->children().indexOf(s2)
+ < s1->parent()->children().indexOf(s1);
+ } else if (isDescendant(s1, s2)) {
+ return true;
+ } else if (isDescendant(s2, s1)) {
+ return false;
+ } else {
+ Q_ASSERT(s1->machine() != nullptr);
+ QStateMachinePrivate *mach = QStateMachinePrivate::get(s1->machine());
+ QState *lca = mach->findLCA(QList<QAbstractState*>() << s1 << s2);
+ Q_ASSERT(lca != nullptr);
+ return (indexOfDescendant(lca, s2) < indexOfDescendant(lca, s1));
+ }
+}
+
+QState *QStateMachinePrivate::findLCA(const QList<QAbstractState*> &states, bool onlyCompound)
+{
+ if (states.isEmpty())
+ return nullptr;
+ QList<QState *> ancestors = getProperAncestors(states.at(0), rootState()->parentState());
+ for (int i = 0; i < ancestors.size(); ++i) {
+ QState *anc = ancestors.at(i);
+ if (onlyCompound && !isCompound(anc))
+ continue;
+
+ bool ok = true;
+ for (int j = states.size() - 1; (j > 0) && ok; --j) {
+ const QAbstractState *s = states.at(j);
+ if (!isDescendant(s, anc))
+ ok = false;
+ }
+ if (ok)
+ return anc;
+ }
+
+ // Oops, this should never happen! The state machine itself is a common ancestor of all states,
+ // no matter what. But, for the onlyCompound case: we probably have a state machine whose
+ // childMode is set to parallel, which is illegal. However, we're stuck with it (and with
+ // exposing this invalid/dangerous API to users), so recover in the least horrible way.
+ setError(QStateMachine::StateMachineChildModeSetToParallelError, q_func());
+ return q_func(); // make the statemachine the LCA/LCCA (which it should have been anyway)
+}
+
+QState *QStateMachinePrivate::findLCCA(const QList<QAbstractState*> &states)
+{
+ return findLCA(states, true);
+}
+
+QList<QAbstractTransition*> QStateMachinePrivate::selectTransitions(QEvent *event, CalculationCache *cache)
+{
+ Q_ASSERT(cache);
+ Q_Q(const QStateMachine);
+
+ QVarLengthArray<QAbstractState *> configuration_sorted;
+ for (QAbstractState *s : std::as_const(configuration)) {
+ if (isAtomic(s))
+ configuration_sorted.append(s);
+ }
+ std::sort(configuration_sorted.begin(), configuration_sorted.end(), stateEntryLessThan);
+
+ QList<QAbstractTransition*> enabledTransitions;
+ const_cast<QStateMachine *>(q)->beginSelectTransitions(event);
+ for (QAbstractState *state : std::as_const(configuration_sorted)) {
+ QList<QState *> lst = getProperAncestors(state, nullptr);
+ if (QState *grp = toStandardState(state))
+ lst.prepend(grp);
+ bool found = false;
+ for (int j = 0; (j < lst.size()) && !found; ++j) {
+ QState *s = lst.at(j);
+ QList<QAbstractTransition*> transitions = QStatePrivate::get(s)->transitions();
+ for (int k = 0; k < transitions.size(); ++k) {
+ QAbstractTransition *t = transitions.at(k);
+ if (QAbstractTransitionPrivate::get(t)->callEventTest(event)) {
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": selecting transition" << t;
+#endif
+ enabledTransitions.append(t);
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!enabledTransitions.isEmpty()) {
+ removeConflictingTransitions(enabledTransitions, cache);
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": enabled transitions after removing conflicts:" << enabledTransitions;
+#endif
+ }
+ const_cast<QStateMachine*>(q)->endSelectTransitions(event);
+ return enabledTransitions;
+}
+
+/* The function as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ :
+
+function removeConflictingTransitions(enabledTransitions):
+ filteredTransitions = new OrderedSet()
+ // toList sorts the transitions in the order of the states that selected them
+ for t1 in enabledTransitions.toList():
+ t1Preempted = false;
+ transitionsToRemove = new OrderedSet()
+ for t2 in filteredTransitions.toList():
+ if computeExitSet([t1]).hasIntersection(computeExitSet([t2])):
+ if isDescendant(t1.source, t2.source):
+ transitionsToRemove.add(t2)
+ else:
+ t1Preempted = true
+ break
+ if not t1Preempted:
+ for t3 in transitionsToRemove.toList():
+ filteredTransitions.delete(t3)
+ filteredTransitions.add(t1)
+
+ return filteredTransitions
+
+Note: the implementation below does not build the transitionsToRemove, but removes them in-place.
+*/
+void QStateMachinePrivate::removeConflictingTransitions(QList<QAbstractTransition*> &enabledTransitions, CalculationCache *cache)
+{
+ Q_ASSERT(cache);
+
+ if (enabledTransitions.size() < 2)
+ return; // There is no transition to conflict with.
+
+ QList<QAbstractTransition*> filteredTransitions;
+ filteredTransitions.reserve(enabledTransitions.size());
+ std::sort(enabledTransitions.begin(), enabledTransitions.end(), transitionStateEntryLessThan);
+
+ for (QAbstractTransition *t1 : std::as_const(enabledTransitions)) {
+ bool t1Preempted = false;
+ const QSet<QAbstractState*> exitSetT1 = computeExitSet_Unordered(t1, cache);
+ QList<QAbstractTransition*>::iterator t2It = filteredTransitions.begin();
+ while (t2It != filteredTransitions.end()) {
+ QAbstractTransition *t2 = *t2It;
+ if (t1 == t2) {
+ // Special case: someone added the same transition object to a state twice. In this
+ // case, t2 (which is already in the list) "preempts" t1.
+ t1Preempted = true;
+ break;
+ }
+
+ QSet<QAbstractState*> exitSetT2 = computeExitSet_Unordered(t2, cache);
+ if (!exitSetT1.intersects(exitSetT2)) {
+ // No conflict, no cry. Next patient please.
+ ++t2It;
+ } else {
+ // Houston, we have a conflict. Check which transition can be removed.
+ if (isDescendant(t1->sourceState(), t2->sourceState())) {
+ // t1 preempts t2, so we can remove t2
+ t2It = filteredTransitions.erase(t2It);
+ } else {
+ // t2 preempts t1, so there's no use in looking further and we don't need to add
+ // t1 to the list.
+ t1Preempted = true;
+ break;
+ }
+ }
+ }
+ if (!t1Preempted)
+ filteredTransitions.append(t1);
+ }
+
+ enabledTransitions = filteredTransitions;
+}
+
+void QStateMachinePrivate::microstep(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions,
+ CalculationCache *cache)
+{
+ Q_ASSERT(cache);
+
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ": begin microstep( enabledTransitions:" << enabledTransitions << ')';
+ qDebug() << q_func() << ": configuration before exiting states:" << configuration;
+#endif
+ QList<QAbstractState*> exitedStates = computeExitSet(enabledTransitions, cache);
+ QHash<RestorableId, QVariant> pendingRestorables = computePendingRestorables(exitedStates);
+
+ QSet<QAbstractState*> statesForDefaultEntry;
+ QList<QAbstractState*> enteredStates = computeEntrySet(enabledTransitions, statesForDefaultEntry, cache);
+
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ": computed exit set:" << exitedStates;
+ qDebug() << q_func() << ": computed entry set:" << enteredStates;
+#endif
+
+ QHash<QAbstractState *, QList<QPropertyAssignment>> assignmentsForEnteredStates =
+ computePropertyAssignments(enteredStates, pendingRestorables);
+ if (!pendingRestorables.isEmpty()) {
+ // Add "implicit" assignments for restored properties to the first
+ // (outermost) entered state
+ Q_ASSERT(!enteredStates.isEmpty());
+ QAbstractState *s = enteredStates.constFirst();
+ assignmentsForEnteredStates[s] << restorablesToPropertyList(pendingRestorables);
+ }
+
+ exitStates(event, exitedStates, assignmentsForEnteredStates);
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ": configuration after exiting states:" << configuration;
+#endif
+
+ executeTransitionContent(event, enabledTransitions);
+
+#if QT_CONFIG(animation)
+ QList<QAbstractAnimation *> selectedAnimations = selectAnimations(enabledTransitions);
+#endif
+
+ enterStates(event, exitedStates, enteredStates, statesForDefaultEntry, assignmentsForEnteredStates
+#if QT_CONFIG(animation)
+ , selectedAnimations
+#endif
+ );
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ": configuration after entering states:" << configuration;
+ qDebug() << q_func() << ": end microstep";
+#endif
+}
+
+/* The function as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ :
+
+procedure computeExitSet(enabledTransitions)
+
+For each transition t in enabledTransitions, if t is targetless then do nothing, else compute the
+transition's domain. (This will be the source state in the case of internal transitions) or the
+least common compound ancestor state of the source state and target states of t (in the case of
+external transitions. Add to the statesToExit set all states in the configuration that are
+descendants of the domain.
+
+function computeExitSet(transitions)
+ statesToExit = new OrderedSet
+ for t in transitions:
+ if (t.target):
+ domain = getTransitionDomain(t)
+ for s in configuration:
+ if isDescendant(s,domain):
+ statesToExit.add(s)
+ return statesToExit
+*/
+QList<QAbstractState*> QStateMachinePrivate::computeExitSet(const QList<QAbstractTransition*> &enabledTransitions,
+ CalculationCache *cache)
+{
+ Q_ASSERT(cache);
+
+ QList<QAbstractState*> statesToExit_sorted = computeExitSet_Unordered(enabledTransitions, cache).values();
+ std::sort(statesToExit_sorted.begin(), statesToExit_sorted.end(), stateExitLessThan);
+ return statesToExit_sorted;
+}
+
+QSet<QAbstractState*> QStateMachinePrivate::computeExitSet_Unordered(const QList<QAbstractTransition*> &enabledTransitions,
+ CalculationCache *cache)
+{
+ Q_ASSERT(cache);
+
+ QSet<QAbstractState*> statesToExit;
+ for (QAbstractTransition *t : enabledTransitions)
+ statesToExit.unite(computeExitSet_Unordered(t, cache));
+ return statesToExit;
+}
+
+QSet<QAbstractState*> QStateMachinePrivate::computeExitSet_Unordered(QAbstractTransition *t,
+ CalculationCache *cache)
+{
+ Q_ASSERT(cache);
+
+ QSet<QAbstractState*> statesToExit;
+ if (cache->exitSet(t, &statesToExit))
+ return statesToExit;
+
+ QList<QAbstractState *> effectiveTargetStates = getEffectiveTargetStates(t, cache);
+ QAbstractState *domain = getTransitionDomain(t, effectiveTargetStates, cache);
+ if (domain == nullptr && !t->targetStates().isEmpty()) {
+ // So we didn't find the least common ancestor for the source and target states of the
+ // transition. If there were not target states, that would be fine: then the transition
+ // will fire any events or signals, but not exit the state.
+ //
+ // However, there are target states, so it's either a node without a parent (or parent's
+ // parent, etc), or the state belongs to a different state machine. Either way, this
+ // makes the state machine invalid.
+ if (error == QStateMachine::NoError)
+ setError(QStateMachine::NoCommonAncestorForTransitionError, t->sourceState());
+ QList<QAbstractState *> lst = pendingErrorStates.values();
+ lst.prepend(t->sourceState());
+
+ domain = findLCCA(lst);
+ Q_ASSERT(domain != nullptr);
+ }
+
+ for (QAbstractState* s : std::as_const(configuration)) {
+ if (isDescendant(s, domain))
+ statesToExit.insert(s);
+ }
+
+ cache->insert(t, statesToExit);
+ return statesToExit;
+}
+
+void QStateMachinePrivate::exitStates(QEvent *event, const QList<QAbstractState *> &statesToExit_sorted,
+ const QHash<QAbstractState *, QList<QPropertyAssignment>> &assignmentsForEnteredStates)
+{
+ for (int i = 0; i < statesToExit_sorted.size(); ++i) {
+ QAbstractState *s = statesToExit_sorted.at(i);
+ if (QState *grp = toStandardState(s)) {
+ QList<QHistoryState*> hlst = QStatePrivate::get(grp)->historyStates();
+ for (int j = 0; j < hlst.size(); ++j) {
+ QHistoryState *h = hlst.at(j);
+ QHistoryStatePrivate::get(h)->configuration.clear();
+ QSet<QAbstractState*>::const_iterator it;
+ for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
+ QAbstractState *s0 = *it;
+ if (QHistoryStatePrivate::get(h)->historyType == QHistoryState::DeepHistory) {
+ if (isAtomic(s0) && isDescendant(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);
+
+#if QT_CONFIG(animation)
+ terminateActiveAnimations(s, assignmentsForEnteredStates);
+#else
+ Q_UNUSED(assignmentsForEnteredStates);
+#endif
+
+ configuration.remove(s);
+ QAbstractStatePrivate::get(s)->emitExited();
+ }
+}
+
+void QStateMachinePrivate::executeTransitionContent(QEvent *event, const QList<QAbstractTransition*> &enabledTransitions)
+{
+ for (int i = 0; i < enabledTransitions.size(); ++i) {
+ QAbstractTransition *t = enabledTransitions.at(i);
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ": triggering" << t;
+#endif
+ QAbstractTransitionPrivate::get(t)->callOnTransition(event);
+ QAbstractTransitionPrivate::get(t)->emitTriggered();
+ }
+}
+
+QList<QAbstractState*> QStateMachinePrivate::computeEntrySet(const QList<QAbstractTransition *> &enabledTransitions,
+ QSet<QAbstractState *> &statesForDefaultEntry,
+ CalculationCache *cache)
+{
+ Q_ASSERT(cache);
+
+ QSet<QAbstractState*> statesToEnter;
+ if (pendingErrorStates.isEmpty()) {
+ for (QAbstractTransition *t : enabledTransitions) {
+ const auto targetStates = t->targetStates();
+ for (QAbstractState *s : targetStates)
+ addDescendantStatesToEnter(s, statesToEnter, statesForDefaultEntry);
+
+ const QList<QAbstractState *> effectiveTargetStates = getEffectiveTargetStates(t, cache);
+ QAbstractState *ancestor = getTransitionDomain(t, effectiveTargetStates, cache);
+ for (QAbstractState *s : effectiveTargetStates)
+ addAncestorStatesToEnter(s, ancestor, 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.values();
+ std::sort(statesToEnter_sorted.begin(), statesToEnter_sorted.end(), stateEntryLessThan);
+ return statesToEnter_sorted;
+}
+
+/* The algorithm as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ :
+
+function getTransitionDomain(transition)
+
+Return the compound state such that 1) all states that are exited or entered as a result of taking
+'transition' are descendants of it 2) no descendant of it has this property.
+
+function getTransitionDomain(t)
+ tstates = getEffectiveTargetStates(t)
+ if not tstates:
+ return null
+ elif t.type == "internal" and isCompoundState(t.source) and tstates.every(lambda s: isDescendant(s,t.source)):
+ return t.source
+ else:
+ return findLCCA([t.source].append(tstates))
+*/
+QAbstractState *QStateMachinePrivate::getTransitionDomain(QAbstractTransition *t,
+ const QList<QAbstractState *> &effectiveTargetStates,
+ CalculationCache *cache)
+{
+ Q_ASSERT(cache);
+
+ if (effectiveTargetStates.isEmpty())
+ return nullptr;
+
+ QAbstractState *domain = nullptr;
+ if (cache->transitionDomain(t, &domain))
+ return domain;
+
+ if (t->transitionType() == QAbstractTransition::InternalTransition) {
+ if (QState *tSource = t->sourceState()) {
+ if (isCompound(tSource)) {
+ bool allDescendants = true;
+ for (QAbstractState *s : effectiveTargetStates) {
+ if (!isDescendant(s, tSource)) {
+ allDescendants = false;
+ break;
+ }
+ }
+
+ if (allDescendants)
+ return tSource;
+ }
+ }
+ }
+
+ QList<QAbstractState *> states(effectiveTargetStates);
+ if (QAbstractState *src = t->sourceState())
+ states.prepend(src);
+ domain = findLCCA(states);
+ cache->insert(t, domain);
+ return domain;
+}
+
+void QStateMachinePrivate::enterStates(QEvent *event, const QList<QAbstractState *> &exitedStates_sorted,
+ const QList<QAbstractState *> &statesToEnter_sorted,
+ const QSet<QAbstractState *> &statesForDefaultEntry,
+ QHash<QAbstractState *, QList<QPropertyAssignment>> &propertyAssignmentsForState
+#if QT_CONFIG(animation)
+ , const QList<QAbstractAnimation *> &selectedAnimations
+#endif
+ )
+{
+#ifdef QSTATEMACHINE_DEBUG
+ Q_Q(QStateMachine);
+#endif
+ 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);
+
+#if QT_CONFIG(animation)
+ initializeAnimations(s, selectedAnimations, exitedStates_sorted, propertyAssignmentsForState);
+#endif
+
+ // Immediately set the properties that are not animated.
+ {
+ const auto assignments = propertyAssignmentsForState.value(s);
+ for (const auto &assn : assignments) {
+ if (globalRestorePolicy == QState::RestoreProperties) {
+ if (assn.explicitlySet) {
+ if (!hasRestorable(s, assn.object, assn.propertyName)) {
+ QVariant value = savedValueForRestorable(exitedStates_sorted, assn.object, assn.propertyName);
+ unregisterRestorables(exitedStates_sorted, assn.object, assn.propertyName);
+ registerRestorable(s, assn.object, assn.propertyName, value);
+ }
+ } else {
+ // The property is being restored, hence no need to
+ // save the current value. Discard any saved values in
+ // exited states, since those are now stale.
+ unregisterRestorables(exitedStates_sorted, assn.object, assn.propertyName);
+ }
+ }
+ assn.write();
+ }
+ }
+
+ QAbstractStatePrivate::get(s)->callOnEntry(event);
+ QAbstractStatePrivate::get(s)->emitEntered();
+
+ // FIXME:
+ // See the "initial transitions" comment in addDescendantStatesToEnter first, then implement:
+// if (statesForDefaultEntry.contains(s)) {
+// // ### executeContent(s.initial.transition.children())
+// }
+ Q_UNUSED(statesForDefaultEntry);
+
+ if (QHistoryState *h = toHistoryState(s))
+ QAbstractTransitionPrivate::get(h->defaultTransition())->callOnTransition(event);
+
+ // Emit propertiesAssigned signal if the state has no animated properties.
+ {
+ QState *ss = toStandardState(s);
+ if (ss
+ #if QT_CONFIG(animation)
+ && !animationsForState.contains(s)
+ #endif
+ )
+ QStatePrivate::get(ss)->emitPropertiesAssigned();
+ }
+
+ if (isFinal(s)) {
+ QState *parent = s->parentState();
+ if (parent) {
+ if (parent != rootState()) {
+ QFinalState *finalState = qobject_cast<QFinalState *>(s);
+ Q_ASSERT(finalState);
+ emitStateFinished(parent, finalState);
+ }
+ QState *grandparent = parent->parentState();
+ if (grandparent && isParallel(grandparent)) {
+ bool allChildStatesFinal = true;
+ QList<QAbstractState*> childStates = QStatePrivate::get(grandparent)->childStates();
+ for (int j = 0; j < childStates.size(); ++j) {
+ QAbstractState *cs = childStates.at(j);
+ if (!isInFinalState(cs)) {
+ allChildStatesFinal = false;
+ break;
+ }
+ }
+ if (allChildStatesFinal && (grandparent != rootState())) {
+ QFinalState *finalState = qobject_cast<QFinalState *>(s);
+ Q_ASSERT(finalState);
+ emitStateFinished(grandparent, finalState);
+ }
+ }
+ }
+ }
+ }
+ {
+ QSet<QAbstractState*>::const_iterator it;
+ for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
+ if (isFinal(*it)) {
+ QState *parent = (*it)->parentState();
+ if (((parent == rootState())
+ && (rootState()->childMode() == QState::ExclusiveStates))
+ || ((parent->parentState() == rootState())
+ && (rootState()->childMode() == QState::ParallelStates)
+ && isInFinalState(rootState()))) {
+ processing = false;
+ stopProcessingReason = Finished;
+ break;
+ }
+ }
+ }
+ }
+// qDebug() << "configuration:" << configuration.toList();
+}
+
+/* The algorithm as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ has a bug. See
+ * QTBUG-44963 for details. The algorithm here is as described in
+ * http://www.w3.org/Voice/2013/scxml-irp/SCXML.htm as of Friday March 13, 2015.
+
+procedure addDescendantStatesToEnter(state,statesToEnter,statesForDefaultEntry, defaultHistoryContent):
+ if isHistoryState(state):
+ if historyValue[state.id]:
+ for s in historyValue[state.id]:
+ addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
+ for s in historyValue[state.id]:
+ addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
+ else:
+ defaultHistoryContent[state.parent.id] = state.transition.content
+ for s in state.transition.target:
+ addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
+ for s in state.transition.target:
+ addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
+ else:
+ statesToEnter.add(state)
+ if isCompoundState(state):
+ statesForDefaultEntry.add(state)
+ for s in state.initial.transition.target:
+ addDescendantStatesToEnter(s,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
+ for s in state.initial.transition.target:
+ addAncestorStatesToEnter(s, state, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
+ else:
+ if isParallelState(state):
+ for child in getChildStates(state):
+ if not statesToEnter.some(lambda s: isDescendant(s,child)):
+ addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
+*/
+void QStateMachinePrivate::addDescendantStatesToEnter(QAbstractState *state,
+ QSet<QAbstractState*> &statesToEnter,
+ QSet<QAbstractState*> &statesForDefaultEntry)
+{
+ if (QHistoryState *h = toHistoryState(state)) {
+ const QList<QAbstractState*> historyConfiguration = QHistoryStatePrivate::get(h)->configuration;
+ if (!historyConfiguration.isEmpty()) {
+ for (QAbstractState *s : historyConfiguration)
+ addDescendantStatesToEnter(s, statesToEnter, statesForDefaultEntry);
+ for (QAbstractState *s : historyConfiguration)
+ addAncestorStatesToEnter(s, state->parentState(), statesToEnter, statesForDefaultEntry);
+
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ": restoring"
+ << ((QHistoryStatePrivate::get(h)->historyType == QHistoryState::DeepHistory) ? "deep" : "shallow")
+ << "history from" << state << ':' << historyConfiguration;
+#endif
+ } else {
+ QList<QAbstractState*> defaultHistoryContent;
+ if (QAbstractTransition *t = QHistoryStatePrivate::get(h)->defaultTransition)
+ defaultHistoryContent = t->targetStates();
+
+ if (defaultHistoryContent.isEmpty()) {
+ setError(QStateMachine::NoDefaultStateInHistoryStateError, h);
+ } else {
+ for (QAbstractState *s : std::as_const(defaultHistoryContent))
+ addDescendantStatesToEnter(s, statesToEnter, statesForDefaultEntry);
+ for (QAbstractState *s : std::as_const(defaultHistoryContent))
+ addAncestorStatesToEnter(s, state->parentState(), statesToEnter, statesForDefaultEntry);
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ": initial history targets for" << state << ':' << defaultHistoryContent;
+#endif
+ }
+ }
+ } else {
+ if (state == rootState()) {
+ // Error has already been set by exitStates().
+ Q_ASSERT(error != QStateMachine::NoError);
+ return;
+ }
+ statesToEnter.insert(state);
+ if (isCompound(state)) {
+ statesForDefaultEntry.insert(state);
+ if (QAbstractState *initial = toStandardState(state)->initialState()) {
+ Q_ASSERT(initial->machine() == q_func());
+
+ // FIXME:
+ // Qt does not support initial transitions (which is a problem for parallel states).
+ // The way it simulates this for other states, is by having a single initial state.
+ // See also the FIXME in enterStates.
+ statesForDefaultEntry.insert(initial);
+
+ addDescendantStatesToEnter(initial, statesToEnter, statesForDefaultEntry);
+ addAncestorStatesToEnter(initial, state, statesToEnter, statesForDefaultEntry);
+ } else {
+ setError(QStateMachine::NoInitialStateError, state);
+ return;
+ }
+ } else if (isParallel(state)) {
+ QState *grp = toStandardState(state);
+ const auto childStates = QStatePrivate::get(grp)->childStates();
+ for (QAbstractState *child : childStates) {
+ if (!containsDecendantOf(statesToEnter, child))
+ addDescendantStatesToEnter(child, statesToEnter, statesForDefaultEntry);
+ }
+ }
+ }
+}
+
+
+/* The algorithm as described in http://www.w3.org/TR/2014/WD-scxml-20140529/ :
+
+procedure addAncestorStatesToEnter(state, ancestor, statesToEnter, statesForDefaultEntry, defaultHistoryContent)
+ for anc in getProperAncestors(state,ancestor):
+ statesToEnter.add(anc)
+ if isParallelState(anc):
+ for child in getChildStates(anc):
+ if not statesToEnter.some(lambda s: isDescendant(s,child)):
+ addDescendantStatesToEnter(child,statesToEnter,statesForDefaultEntry, defaultHistoryContent)
+*/
+void QStateMachinePrivate::addAncestorStatesToEnter(QAbstractState *s, QAbstractState *ancestor,
+ QSet<QAbstractState*> &statesToEnter,
+ QSet<QAbstractState*> &statesForDefaultEntry)
+{
+ const auto properAncestors = getProperAncestors(s, ancestor);
+ for (QState *anc : properAncestors) {
+ if (!anc->parentState())
+ continue;
+ statesToEnter.insert(anc);
+ if (isParallel(anc)) {
+ const auto childStates = QStatePrivate::get(anc)->childStates();
+ for (QAbstractState *child : childStates) {
+ if (!containsDecendantOf(statesToEnter, child))
+ addDescendantStatesToEnter(child, statesToEnter, statesForDefaultEntry);
+ }
+ }
+ }
+}
+
+bool QStateMachinePrivate::isFinal(const QAbstractState *s)
+{
+ return s && (QAbstractStatePrivate::get(s)->stateType == QAbstractStatePrivate::FinalState);
+}
+
+bool QStateMachinePrivate::isParallel(const QAbstractState *s)
+{
+ const QState *ss = toStandardState(s);
+ return ss && (QStatePrivate::get(ss)->childMode == QState::ParallelStates);
+}
+
+bool QStateMachinePrivate::isCompound(const QAbstractState *s) const
+{
+ const QState *group = toStandardState(s);
+ if (!group)
+ return false;
+ bool isMachine = QStatePrivate::get(group)->isMachine;
+ // Don't treat the machine as compound if it's a sub-state of this machine
+ if (isMachine && (group != rootState()))
+ return false;
+ return (!isParallel(group) && !QStatePrivate::get(group)->childStates().isEmpty());
+}
+
+bool QStateMachinePrivate::isAtomic(const QAbstractState *s) const
+{
+ const QState *ss = toStandardState(s);
+ return (ss && QStatePrivate::get(ss)->childStates().isEmpty())
+ || isFinal(s)
+ // Treat the machine as atomic if it's a sub-state of this machine
+ || (ss && QStatePrivate::get(ss)->isMachine && (ss != rootState()));
+}
+
+QState *QStateMachinePrivate::toStandardState(QAbstractState *state)
+{
+ if (state && (QAbstractStatePrivate::get(state)->stateType == QAbstractStatePrivate::StandardState))
+ return static_cast<QState*>(state);
+ return nullptr;
+}
+
+const QState *QStateMachinePrivate::toStandardState(const QAbstractState *state)
+{
+ if (state && (QAbstractStatePrivate::get(state)->stateType == QAbstractStatePrivate::StandardState))
+ return static_cast<const QState*>(state);
+ return nullptr;
+}
+
+QFinalState *QStateMachinePrivate::toFinalState(QAbstractState *state)
+{
+ if (state && (QAbstractStatePrivate::get(state)->stateType == QAbstractStatePrivate::FinalState))
+ return static_cast<QFinalState*>(state);
+ return nullptr;
+}
+
+QHistoryState *QStateMachinePrivate::toHistoryState(QAbstractState *state)
+{
+ if (state && (QAbstractStatePrivate::get(state)->stateType == QAbstractStatePrivate::HistoryState))
+ return static_cast<QHistoryState*>(state);
+ return nullptr;
+}
+
+bool QStateMachinePrivate::isInFinalState(QAbstractState* s) const
+{
+ if (isCompound(s)) {
+ QState *grp = toStandardState(s);
+ QList<QAbstractState*> lst = QStatePrivate::get(grp)->childStates();
+ for (int i = 0; i < lst.size(); ++i) {
+ QAbstractState *cs = lst.at(i);
+ if (isFinal(cs) && configuration.contains(cs))
+ return true;
+ }
+ return false;
+ } else if (isParallel(s)) {
+ QState *grp = toStandardState(s);
+ QList<QAbstractState*> lst = QStatePrivate::get(grp)->childStates();
+ for (int i = 0; i < lst.size(); ++i) {
+ QAbstractState *cs = lst.at(i);
+ if (!isInFinalState(cs))
+ return false;
+ }
+ return true;
+ }
+ else
+ return false;
+}
+
+#ifndef QT_NO_PROPERTIES
+
+/*!
+ \internal
+ Returns \c true if the given state has saved the value of the given property,
+ otherwise returns \c false.
+*/
+bool QStateMachinePrivate::hasRestorable(QAbstractState *state, QObject *object,
+ const QByteArray &propertyName) const
+{
+ RestorableId id(object, propertyName);
+ return registeredRestorablesForState.value(state).contains(id);
+}
+
+/*!
+ \internal
+ Returns the value to save for the property identified by \a id.
+ If an exited state (member of \a exitedStates_sorted) has saved a value for
+ the property, the saved value from the last (outermost) state that will be
+ exited is returned (in practice carrying the saved value on to the next
+ state). Otherwise, the current value of the property is returned.
+*/
+QVariant QStateMachinePrivate::savedValueForRestorable(const QList<QAbstractState*> &exitedStates_sorted,
+ QObject *object, const QByteArray &propertyName) const
+{
+#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
+ qDebug() << q_func() << ": savedValueForRestorable(" << exitedStates_sorted << object << propertyName << ')';
+#endif
+ for (int i = exitedStates_sorted.size() - 1; i >= 0; --i) {
+ QAbstractState *s = exitedStates_sorted.at(i);
+ QHash<RestorableId, QVariant> restorables = registeredRestorablesForState.value(s);
+ QHash<RestorableId, QVariant>::const_iterator it = restorables.constFind(RestorableId(object, propertyName));
+ if (it != restorables.constEnd()) {
+#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
+ qDebug() << q_func() << ": using" << it.value() << "from" << s;
+#endif
+ return it.value();
+ }
+ }
+#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
+ qDebug() << q_func() << ": falling back to current value";
+#endif
+ return object->property(propertyName);
+}
+
+void QStateMachinePrivate::registerRestorable(QAbstractState *state, QObject *object, const QByteArray &propertyName,
+ const QVariant &value)
+{
+#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
+ qDebug() << q_func() << ": registerRestorable(" << state << object << propertyName << value << ')';
+#endif
+ RestorableId id(object, propertyName);
+ QHash<RestorableId, QVariant> &restorables = registeredRestorablesForState[state];
+ if (!restorables.contains(id))
+ restorables.insert(id, value);
+#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
+ else
+ qDebug() << q_func() << ": (already registered)";
+#endif
+}
+
+void QStateMachinePrivate::unregisterRestorables(const QList<QAbstractState *> &states, QObject *object,
+ const QByteArray &propertyName)
+{
+#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
+ qDebug() << q_func() << ": unregisterRestorables(" << states << object << propertyName << ')';
+#endif
+ RestorableId id(object, propertyName);
+ for (int i = 0; i < states.size(); ++i) {
+ QAbstractState *s = states.at(i);
+ QHash<QAbstractState*, QHash<RestorableId, QVariant> >::iterator it;
+ it = registeredRestorablesForState.find(s);
+ if (it == registeredRestorablesForState.end())
+ continue;
+ QHash<RestorableId, QVariant> &restorables = it.value();
+ const auto it2 = restorables.constFind(id);
+ if (it2 == restorables.cend())
+ continue;
+#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
+ qDebug() << q_func() << ": unregistered for" << s;
+#endif
+ restorables.erase(it2);
+ if (restorables.isEmpty())
+ registeredRestorablesForState.erase(it);
+ }
+}
+
+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) {
+ const RestorableId &id = it.key();
+ if (!id.object()) {
+ // Property object was deleted
+ continue;
+ }
+#ifdef QSTATEMACHINE_RESTORE_PROPERTIES_DEBUG
+ qDebug() << q_func() << ": restoring" << id.object() << id.proertyName() << "to" << it.value();
+#endif
+ result.append(QPropertyAssignment(id.object(), id.propertyName(), it.value(), /*explicitlySet=*/false));
+ }
+ return result;
+}
+
+/*!
+ \internal
+ Computes the set of properties whose values should be restored given that
+ the states \a statesToExit_sorted will be exited.
+
+ If a particular (object, propertyName) pair occurs more than once (i.e.,
+ because nested states are being exited), the value from the last (outermost)
+ exited state takes precedence.
+
+ The result of this function must be filtered according to the explicit
+ property assignments (QState::assignProperty()) of the entered states
+ before the property restoration is actually performed; i.e., if an entered
+ state assigns to a property that would otherwise be restored, that property
+ should not be restored after all, but the saved value from the exited state
+ should be remembered by the entered state (see registerRestorable()).
+*/
+QHash<QStateMachinePrivate::RestorableId, QVariant> QStateMachinePrivate::computePendingRestorables(
+ const QList<QAbstractState*> &statesToExit_sorted) const
+{
+ QHash<QStateMachinePrivate::RestorableId, QVariant> restorables;
+ for (int i = statesToExit_sorted.size() - 1; i >= 0; --i) {
+ QAbstractState *s = statesToExit_sorted.at(i);
+ QHash<QStateMachinePrivate::RestorableId, QVariant> rs = registeredRestorablesForState.value(s);
+ QHash<QStateMachinePrivate::RestorableId, QVariant>::const_iterator it;
+ for (it = rs.constBegin(); it != rs.constEnd(); ++it) {
+ if (!restorables.contains(it.key()))
+ restorables.insert(it.key(), it.value());
+ }
+ }
+ return restorables;
+}
+
+/*!
+ \internal
+ Computes the ordered sets of property assignments for the states to be
+ entered, \a statesToEnter_sorted. Also filters \a pendingRestorables (removes
+ properties that should not be restored because they are assigned by an
+ entered state).
+*/
+QHash<QAbstractState *, QList<QPropertyAssignment>> QStateMachinePrivate::computePropertyAssignments(
+ const QList<QAbstractState*> &statesToEnter_sorted, QHash<RestorableId, QVariant> &pendingRestorables) const
+{
+ QHash<QAbstractState *, QList<QPropertyAssignment>> assignmentsForState;
+ for (int i = 0; i < statesToEnter_sorted.size(); ++i) {
+ QState *s = toStandardState(statesToEnter_sorted.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 (assn.objectDeleted()) {
+ assignments.removeAt(j--);
+ } else {
+ pendingRestorables.remove(RestorableId(assn.object, assn.propertyName));
+ assignmentsForState[s].append(assn);
+ }
+ }
+ }
+ return assignmentsForState;
+}
+
+#endif // QT_NO_PROPERTIES
+
+QAbstractState *QStateMachinePrivate::findErrorState(QAbstractState *context)
+{
+ // Find error state recursively in parent hierarchy if not set explicitly for context state
+ QAbstractState *errorState = nullptr;
+ if (context != nullptr) {
+ QState *s = toStandardState(context);
+ if (s != nullptr)
+ errorState = s->errorState();
+
+ if (errorState == nullptr)
+ errorState = findErrorState(context->parentState());
+ }
+
+ return errorState;
+}
+
+void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractState *currentContext)
+{
+ Q_Q(QStateMachine);
+
+ error = errorCode;
+ switch (errorCode) {
+ case QStateMachine::NoInitialStateError:
+ Q_ASSERT(currentContext != nullptr);
+
+ errorString = QStateMachine::tr("Missing initial state in compound state '%1'")
+ .arg(currentContext->objectName());
+
+ break;
+ case QStateMachine::NoDefaultStateInHistoryStateError:
+ Q_ASSERT(currentContext != nullptr);
+
+ errorString = QStateMachine::tr("Missing default state in history state '%1'")
+ .arg(currentContext->objectName());
+ break;
+
+ case QStateMachine::NoCommonAncestorForTransitionError:
+ Q_ASSERT(currentContext != nullptr);
+
+ errorString = QStateMachine::tr("No common ancestor for targets and source of transition from state '%1'")
+ .arg(currentContext->objectName());
+ break;
+
+ case QStateMachine::StateMachineChildModeSetToParallelError:
+ Q_ASSERT(currentContext != nullptr);
+
+ errorString = QStateMachine::tr("Child mode of state machine '%1' is not 'ExclusiveStates'.")
+ .arg(currentContext->objectName());
+ break;
+
+ default:
+ errorString = QStateMachine::tr("Unknown error");
+ };
+
+ pendingErrorStates.clear();
+ pendingErrorStatesForDefaultEntry.clear();
+
+ QAbstractState *currentErrorState = findErrorState(currentContext);
+
+ // Avoid infinite loop if the error state itself has an error
+ if (currentContext == currentErrorState)
+ currentErrorState = nullptr;
+
+ Q_ASSERT(currentErrorState != rootState());
+
+ if (currentErrorState != nullptr) {
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": entering error state" << currentErrorState << "from" << currentContext;
+#endif
+ pendingErrorStates.insert(currentErrorState);
+ addDescendantStatesToEnter(currentErrorState, pendingErrorStates, pendingErrorStatesForDefaultEntry);
+ addAncestorStatesToEnter(currentErrorState, rootState(), pendingErrorStates, pendingErrorStatesForDefaultEntry);
+ pendingErrorStates -= configuration;
+ } else {
+ qWarning("Unrecoverable error detected in running state machine: %ls",
+ qUtf16Printable(errorString));
+ q->stop();
+ }
+}
+
+#if QT_CONFIG(animation)
+
+QStateMachinePrivate::InitializeAnimationResult
+QStateMachinePrivate::initializeAnimation(QAbstractAnimation *abstractAnimation,
+ const QPropertyAssignment &prop)
+{
+ InitializeAnimationResult result;
+ QAnimationGroup *group = qobject_cast<QAnimationGroup*>(abstractAnimation);
+ if (group) {
+ for (int i = 0; i < group->animationCount(); ++i) {
+ QAbstractAnimation *animationChild = group->animationAt(i);
+ const auto ret = initializeAnimation(animationChild, prop);
+ result.handledAnimations << ret.handledAnimations;
+ result.localResetEndValues << ret.localResetEndValues;
+ }
+ } else {
+ QPropertyAnimation *animation = qobject_cast<QPropertyAnimation *>(abstractAnimation);
+ if (animation != nullptr
+ && prop.object == animation->targetObject()
+ && prop.propertyName == animation->propertyName()) {
+
+ // Only change end value if it is undefined
+ if (!animation->endValue().isValid()) {
+ animation->setEndValue(prop.value);
+ result.localResetEndValues.append(animation);
+ }
+ result.handledAnimations.append(animation);
+ }
+ }
+ return result;
+}
+
+void QStateMachinePrivate::_q_animationFinished()
+{
+ Q_Q(QStateMachine);
+ QAbstractAnimation *anim = qobject_cast<QAbstractAnimation*>(q->sender());
+ Q_ASSERT(anim != nullptr);
+ QObject::disconnect(anim, SIGNAL(finished()), q, SLOT(_q_animationFinished()));
+ if (resetAnimationEndValues.contains(anim)) {
+ qobject_cast<QVariantAnimation*>(anim)->setEndValue(QVariant()); // ### generalize
+ resetAnimationEndValues.remove(anim);
+ }
+
+ QAbstractState *state = stateForAnimation.take(anim);
+ Q_ASSERT(state != nullptr);
+
+#ifndef QT_NO_PROPERTIES
+ // Set the final property value.
+ QPropertyAssignment assn = propertyForAnimation.take(anim);
+ assn.write();
+ if (!assn.explicitlySet)
+ unregisterRestorables(QList<QAbstractState*>() << state, assn.object, assn.propertyName);
+#endif
+
+ QHash<QAbstractState*, QList<QAbstractAnimation*> >::iterator it;
+ it = animationsForState.find(state);
+ Q_ASSERT(it != animationsForState.end());
+ QList<QAbstractAnimation*> &animations = it.value();
+ animations.removeOne(anim);
+ if (animations.isEmpty()) {
+ animationsForState.erase(it);
+ QStatePrivate::get(toStandardState(state))->emitPropertiesAssigned();
+ }
+}
+
+QList<QAbstractAnimation *> QStateMachinePrivate::selectAnimations(const QList<QAbstractTransition *> &transitionList) const
+{
+ QList<QAbstractAnimation *> selectedAnimations;
+ if (animated) {
+ for (int i = 0; i < transitionList.size(); ++i) {
+ QAbstractTransition *transition = transitionList.at(i);
+
+ selectedAnimations << transition->animations();
+ selectedAnimations << defaultAnimationsForSource.values(transition->sourceState());
+
+ QList<QAbstractState *> targetStates = transition->targetStates();
+ for (int j=0; j<targetStates.size(); ++j)
+ selectedAnimations << defaultAnimationsForTarget.values(targetStates.at(j));
+ }
+ selectedAnimations << defaultAnimations;
+ }
+ return selectedAnimations;
+}
+
+void QStateMachinePrivate::terminateActiveAnimations(QAbstractState *state,
+ const QHash<QAbstractState *, QList<QPropertyAssignment>> &assignmentsForEnteredStates)
+{
+ Q_Q(QStateMachine);
+ QList<QAbstractAnimation*> animations = animationsForState.take(state);
+ for (int i = 0; i < animations.size(); ++i) {
+ QAbstractAnimation *anim = animations.at(i);
+ 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 != nullptr);
+ // If there is no property assignment that sets this property,
+ // set the property to its target value.
+ bool found = false;
+ for (auto it = assignmentsForEnteredStates.constBegin(); it != assignmentsForEnteredStates.constEnd(); ++it) {
+ const QList<QPropertyAssignment> &assignments = it.value();
+ for (int j = 0; j < assignments.size(); ++j) {
+ if (assignments.at(j).hasTarget(assn.object, assn.propertyName)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ assn.write();
+ if (!assn.explicitlySet)
+ unregisterRestorables(QList<QAbstractState*>() << state, assn.object, assn.propertyName);
+ }
+ }
+}
+
+void QStateMachinePrivate::initializeAnimations(QAbstractState *state, const QList<QAbstractAnimation *> &selectedAnimations,
+ const QList<QAbstractState*> &exitedStates_sorted,
+ QHash<QAbstractState *, QList<QPropertyAssignment>> &assignmentsForEnteredStates)
+{
+ Q_Q(QStateMachine);
+ if (!assignmentsForEnteredStates.contains(state))
+ return;
+ QList<QPropertyAssignment> &assignments = assignmentsForEnteredStates[state];
+ for (int i = 0; i < selectedAnimations.size(); ++i) {
+ QAbstractAnimation *anim = selectedAnimations.at(i);
+ for (auto it = assignments.begin(); it != assignments.end(); ) {
+ const QPropertyAssignment &assn = *it;
+ const auto ret = initializeAnimation(anim, assn);
+ if (!ret.handledAnimations.isEmpty()) {
+ for (int j = 0; j < ret.handledAnimations.size(); ++j) {
+ QAbstractAnimation *a = ret.handledAnimations.at(j);
+ propertyForAnimation.insert(a, assn);
+ stateForAnimation.insert(a, state);
+ animationsForState[state].append(a);
+ // ### connect to just the top-level animation?
+ QObject::connect(a, SIGNAL(finished()), q, SLOT(_q_animationFinished()), Qt::UniqueConnection);
+ }
+ if ((globalRestorePolicy == QState::RestoreProperties)
+ && !hasRestorable(state, assn.object, assn.propertyName)) {
+ QVariant value = savedValueForRestorable(exitedStates_sorted, assn.object, assn.propertyName);
+ unregisterRestorables(exitedStates_sorted, assn.object, assn.propertyName);
+ registerRestorable(state, assn.object, assn.propertyName, value);
+ }
+ it = assignments.erase(it);
+ } else {
+ ++it;
+ }
+ for (int j = 0; j < ret.localResetEndValues.size(); ++j)
+ resetAnimationEndValues.insert(ret.localResetEndValues.at(j));
+ }
+ // We require that at least one animation is valid.
+ // ### generalize
+ QList<QVariantAnimation*> variantAnims = anim->findChildren<QVariantAnimation*>();
+ if (QVariantAnimation *va = qobject_cast<QVariantAnimation*>(anim))
+ variantAnims.append(va);
+
+ bool hasValidEndValue = false;
+ for (int j = 0; j < variantAnims.size(); ++j) {
+ if (variantAnims.at(j)->endValue().isValid()) {
+ hasValidEndValue = true;
+ break;
+ }
+ }
+
+ if (hasValidEndValue) {
+ if (anim->state() == QAbstractAnimation::Running) {
+ // The animation is still running. This can happen if the
+ // animation is a group, and one of its children just finished,
+ // and that caused a state to emit its propertiesAssigned() signal, and
+ // that triggered a transition in the machine.
+ // Just stop the animation so it is correctly restarted again.
+ anim->stop();
+ }
+ anim->start();
+ }
+
+ if (assignments.isEmpty()) {
+ assignmentsForEnteredStates.remove(state);
+ break;
+ }
+ }
+}
+
+#endif // animation
+
+QAbstractTransition *QStateMachinePrivate::createInitialTransition() const
+{
+ class InitialTransition : public QAbstractTransition
+ {
+ public:
+ InitialTransition(const QList<QAbstractState *> &targets)
+ : QAbstractTransition()
+ { setTargetStates(targets); }
+ protected:
+ bool eventTest(QEvent *) override { return true; }
+ void onTransition(QEvent *) override {}
+ };
+
+ QState *root = rootState();
+ Q_ASSERT(root != nullptr);
+ QList<QAbstractState *> targets;
+ switch (root->childMode()) {
+ case QState::ExclusiveStates:
+ targets.append(root->initialState());
+ break;
+ case QState::ParallelStates:
+ targets = QStatePrivate::get(root)->childStates();
+ break;
+ }
+ return new InitialTransition(targets);
+}
+
+void QStateMachinePrivate::clearHistory()
+{
+ Q_Q(QStateMachine);
+ QList<QHistoryState*> historyStates = q->findChildren<QHistoryState*>();
+ for (int i = 0; i < historyStates.size(); ++i) {
+ QHistoryState *h = historyStates.at(i);
+ QHistoryStatePrivate::get(h)->configuration.clear();
+ }
+}
+
+/*!
+ \internal
+
+ Registers all signal transitions whose sender object lives in another thread.
+
+ Normally, signal transitions are lazily registered (when a state becomes
+ active). But if the sender is in a different thread, the transition must be
+ registered early to keep the state machine from "dropping" signals; e.g.,
+ a second (transition-bound) signal could be emitted on the sender thread
+ before the state machine gets to process the first signal.
+*/
+void QStateMachinePrivate::registerMultiThreadedSignalTransitions()
+{
+ Q_Q(QStateMachine);
+ QList<QSignalTransition*> transitions = rootState()->findChildren<QSignalTransition*>();
+ for (int i = 0; i < transitions.size(); ++i) {
+ QSignalTransition *t = transitions.at(i);
+ if ((t->machine() == q) && t->senderObject() && (t->senderObject()->thread() != q->thread()))
+ registerSignalTransition(t);
+ }
+}
+
+void QStateMachinePrivate::_q_start()
+{
+ Q_Q(QStateMachine);
+ Q_ASSERT(state == Starting);
+ // iterate over a copy, since we emit signals which may cause
+ // 'configuration' to change, resulting in undefined behavior when
+ // iterating at the same time:
+ const auto config = configuration;
+ for (QAbstractState *state : config) {
+ QAbstractStatePrivate *abstractStatePrivate = QAbstractStatePrivate::get(state);
+ abstractStatePrivate->active.setValue(false);
+ }
+ configuration.clear();
+ qDeleteAll(internalEventQueue);
+ internalEventQueue.clear();
+ qDeleteAll(externalEventQueue);
+ externalEventQueue.clear();
+ clearHistory();
+
+ registerMultiThreadedSignalTransitions();
+
+ startupHook();
+
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": starting";
+#endif
+ state = Running;
+ processingScheduled = true; // we call _q_process() below
+
+ QList<QAbstractTransition*> transitions;
+ CalculationCache calculationCache;
+ QAbstractTransition *initialTransition = createInitialTransition();
+ transitions.append(initialTransition);
+
+ QEvent nullEvent(QEvent::None);
+ executeTransitionContent(&nullEvent, transitions);
+ QList<QAbstractState*> exitedStates = QList<QAbstractState*>();
+ QSet<QAbstractState*> statesForDefaultEntry;
+ QList<QAbstractState*> enteredStates = computeEntrySet(transitions, statesForDefaultEntry, &calculationCache);
+ QHash<RestorableId, QVariant> pendingRestorables;
+ QHash<QAbstractState *, QList<QPropertyAssignment>> assignmentsForEnteredStates =
+ computePropertyAssignments(enteredStates, pendingRestorables);
+#if QT_CONFIG(animation)
+ QList<QAbstractAnimation *> selectedAnimations = selectAnimations(transitions);
+#endif
+ // enterStates() will set stopProcessingReason to Finished if a final
+ // state is entered.
+ stopProcessingReason = EventQueueEmpty;
+ enterStates(&nullEvent, exitedStates, enteredStates, statesForDefaultEntry,
+ assignmentsForEnteredStates
+#if QT_CONFIG(animation)
+ , selectedAnimations
+#endif
+ );
+ delete initialTransition;
+
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": initial configuration:" << configuration;
+#endif
+
+ emit q->started(QStateMachine::QPrivateSignal());
+ emit q->runningChanged(true);
+
+ if (stopProcessingReason == Finished) {
+ // The state machine immediately reached a final state.
+ processingScheduled = false;
+ state = NotRunning;
+ unregisterAllTransitions();
+ emitFinished();
+ emit q->runningChanged(false);
+ exitInterpreter();
+ } else {
+ _q_process();
+ }
+}
+
+void QStateMachinePrivate::_q_process()
+{
+ Q_Q(QStateMachine);
+ Q_ASSERT(state == Running);
+ Q_ASSERT(!processing);
+ processing = true;
+ processingScheduled = false;
+ beginMacrostep();
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": starting the event processing loop";
+#endif
+ bool didChange = false;
+ while (processing) {
+ if (stop) {
+ processing = false;
+ break;
+ }
+ QList<QAbstractTransition*> enabledTransitions;
+ CalculationCache calculationCache;
+
+ QEvent *e = new QEvent(QEvent::None);
+ enabledTransitions = selectTransitions(e, &calculationCache);
+ if (enabledTransitions.isEmpty()) {
+ delete e;
+ e = nullptr;
+ }
+ while (enabledTransitions.isEmpty() && ((e = dequeueInternalEvent()) != nullptr)) {
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": dequeued internal event" << e << "of type" << e->type();
+#endif
+ enabledTransitions = selectTransitions(e, &calculationCache);
+ if (enabledTransitions.isEmpty()) {
+ delete e;
+ e = nullptr;
+ }
+ }
+ while (enabledTransitions.isEmpty() && ((e = dequeueExternalEvent()) != nullptr)) {
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": dequeued external event" << e << "of type" << e->type();
+#endif
+ enabledTransitions = selectTransitions(e, &calculationCache);
+ if (enabledTransitions.isEmpty()) {
+ delete e;
+ e = nullptr;
+ }
+ }
+ if (enabledTransitions.isEmpty()) {
+ if (isInternalEventQueueEmpty()) {
+ processing = false;
+ stopProcessingReason = EventQueueEmpty;
+ noMicrostep();
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": no transitions enabled";
+#endif
+ }
+ } else {
+ didChange = true;
+ q->beginMicrostep(e);
+ microstep(e, enabledTransitions, &calculationCache);
+ q->endMicrostep(e);
+ }
+ delete e;
+ }
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": finished the event processing loop";
+#endif
+ if (stop) {
+ stop = false;
+ stopProcessingReason = Stopped;
+ }
+
+ switch (stopProcessingReason) {
+ case EventQueueEmpty:
+ processedPendingEvents(didChange);
+ break;
+ case Finished:
+ state = NotRunning;
+ cancelAllDelayedEvents();
+ unregisterAllTransitions();
+ emitFinished();
+ emit q->runningChanged(false);
+ break;
+ case Stopped:
+ state = NotRunning;
+ cancelAllDelayedEvents();
+ unregisterAllTransitions();
+ emit q->stopped(QStateMachine::QPrivateSignal());
+ emit q->runningChanged(false);
+ break;
+ }
+ endMacrostep(didChange);
+ if (stopProcessingReason == Finished)
+ exitInterpreter();
+}
+
+void QStateMachinePrivate::_q_startDelayedEventTimer(int id, int delay)
+{
+ Q_Q(QStateMachine);
+ QMutexLocker locker(&delayedEventsMutex);
+ QHash<int, DelayedEvent>::iterator it = delayedEvents.find(id);
+ if (it != delayedEvents.end()) {
+ DelayedEvent &e = it.value();
+ Q_ASSERT(!e.timerId);
+ e.timerId = q->startTimer(delay);
+ if (!e.timerId) {
+ qWarning("QStateMachine::postDelayedEvent: failed to start timer (id=%d, delay=%d)", id, delay);
+ delete e.event;
+ delayedEvents.erase(it);
+ delayedEventIdFreeList.release(id);
+ } else {
+ timerIdToDelayedEventId.insert(e.timerId, id);
+ }
+ } else {
+ // It's been cancelled already
+ delayedEventIdFreeList.release(id);
+ }
+}
+
+void QStateMachinePrivate::_q_killDelayedEventTimer(int id, int timerId)
+{
+ Q_Q(QStateMachine);
+ q->killTimer(timerId);
+ QMutexLocker locker(&delayedEventsMutex);
+ delayedEventIdFreeList.release(id);
+}
+
+void QStateMachinePrivate::postInternalEvent(QEvent *e)
+{
+ QMutexLocker locker(&internalEventMutex);
+ internalEventQueue.append(e);
+}
+
+void QStateMachinePrivate::postExternalEvent(QEvent *e)
+{
+ QMutexLocker locker(&externalEventMutex);
+ externalEventQueue.append(e);
+}
+
+QEvent *QStateMachinePrivate::dequeueInternalEvent()
+{
+ QMutexLocker locker(&internalEventMutex);
+ if (internalEventQueue.isEmpty())
+ return nullptr;
+ return internalEventQueue.takeFirst();
+}
+
+QEvent *QStateMachinePrivate::dequeueExternalEvent()
+{
+ QMutexLocker locker(&externalEventMutex);
+ if (externalEventQueue.isEmpty())
+ return nullptr;
+ return externalEventQueue.takeFirst();
+}
+
+bool QStateMachinePrivate::isInternalEventQueueEmpty()
+{
+ QMutexLocker locker(&internalEventMutex);
+ return internalEventQueue.isEmpty();
+}
+
+bool QStateMachinePrivate::isExternalEventQueueEmpty()
+{
+ QMutexLocker locker(&externalEventMutex);
+ return externalEventQueue.isEmpty();
+}
+
+void QStateMachinePrivate::processEvents(EventProcessingMode processingMode)
+{
+ Q_Q(QStateMachine);
+ if ((state != Running) || processing || processingScheduled)
+ return;
+ switch (processingMode) {
+ case DirectProcessing:
+ if (QThread::currentThread() == q->thread()) {
+ _q_process();
+ break;
+ }
+ // processing must be done in the machine thread, so:
+ Q_FALLTHROUGH();
+ case QueuedProcessing:
+ processingScheduled = true;
+ QMetaObject::invokeMethod(q, "_q_process", Qt::QueuedConnection);
+ break;
+ }
+}
+
+void QStateMachinePrivate::cancelAllDelayedEvents()
+{
+ Q_Q(QStateMachine);
+ QMutexLocker locker(&delayedEventsMutex);
+ QHash<int, DelayedEvent>::const_iterator it;
+ for (it = delayedEvents.constBegin(); it != delayedEvents.constEnd(); ++it) {
+ const DelayedEvent &e = it.value();
+ if (e.timerId) {
+ timerIdToDelayedEventId.remove(e.timerId);
+ q->killTimer(e.timerId);
+ delayedEventIdFreeList.release(it.key());
+ } else {
+ // Cancellation will be detected in pending _q_startDelayedEventTimer() call
+ }
+ delete e.event;
+ }
+ delayedEvents.clear();
+}
+
+/*
+ This function is called when the state machine is performing no
+ microstep because no transition is enabled (i.e. an event is ignored).
+
+ The default implementation does nothing.
+*/
+void QStateMachinePrivate::noMicrostep()
+{ }
+
+/*
+ This function is called when the state machine has reached a stable
+ state (no pending events), and has not finished yet.
+ For each event the state machine receives it is guaranteed that
+ 1) beginMacrostep is called
+ 2) selectTransition is called at least once
+ 3) begin/endMicrostep is called at least once or noMicrostep is called
+ at least once (possibly both, but at least one)
+ 4) the state machine either enters an infinite loop, or stops (runningChanged(false),
+ and either finished or stopped are emitted), or processedPendingEvents() is called.
+ 5) if the machine is not in an infinite loop endMacrostep is called
+ 6) when the machine is finished and all processing (like signal emission) is done,
+ exitInterpreter() is called. (This is the same name as the SCXML specification uses.)
+
+ didChange is set to true if at least one microstep was performed, it is possible
+ that the machine returned to exactly the same state as before, but some transitions
+ were triggered.
+
+ The default implementation does nothing.
+*/
+void QStateMachinePrivate::processedPendingEvents(bool didChange)
+{
+ Q_UNUSED(didChange);
+}
+
+void QStateMachinePrivate::beginMacrostep()
+{ }
+
+void QStateMachinePrivate::endMacrostep(bool didChange)
+{
+ Q_UNUSED(didChange);
+}
+
+void QStateMachinePrivate::exitInterpreter()
+{
+}
+
+void QStateMachinePrivate::emitStateFinished(QState *forState, QFinalState *guiltyState)
+{
+ Q_UNUSED(guiltyState);
+ Q_ASSERT(guiltyState);
+
+#ifdef QSTATEMACHINE_DEBUG
+ Q_Q(QStateMachine);
+ qDebug() << q << ": emitting finished signal for" << forState;
+#endif
+
+ QStatePrivate::get(forState)->emitFinished();
+}
+
+void QStateMachinePrivate::startupHook()
+{
+}
+
+namespace _QStateMachine_Internal{
+
+class GoToStateTransition : public QAbstractTransition
+{
+ Q_OBJECT
+public:
+ GoToStateTransition(QAbstractState *target)
+ : QAbstractTransition()
+ { setTargetState(target); }
+protected:
+ void onTransition(QEvent *) override { deleteLater(); }
+ bool eventTest(QEvent *) override { return true; }
+};
+
+} // namespace
+// mingw compiler tries to export QObject::findChild<GoToStateTransition>(),
+// which doesn't work if its in an anonymous namespace.
+using namespace _QStateMachine_Internal;
+/*!
+ \internal
+
+ Causes this state machine to unconditionally transition to the given
+ \a targetState.
+
+ Provides a backdoor for using the state machine "imperatively"; i.e. rather
+ than defining explicit transitions, you drive the machine's execution by
+ calling this function. It breaks the whole integrity of the
+ transition-driven model, but is provided for pragmatic reasons.
+*/
+void QStateMachinePrivate::goToState(QAbstractState *targetState)
+{
+ if (!targetState) {
+ qWarning("QStateMachine::goToState(): cannot go to null state");
+ return;
+ }
+
+ if (configuration.contains(targetState))
+ return;
+
+ Q_ASSERT(state == Running);
+ QState *sourceState = nullptr;
+ QSet<QAbstractState*>::const_iterator it;
+ for (it = configuration.constBegin(); it != configuration.constEnd(); ++it) {
+ sourceState = toStandardState(*it);
+ if (sourceState != nullptr)
+ break;
+ }
+
+ Q_ASSERT(sourceState != nullptr);
+ // Reuse previous GoToStateTransition in case of several calls to
+ // goToState() in a row.
+ GoToStateTransition *trans = sourceState->findChild<GoToStateTransition*>();
+ if (!trans) {
+ trans = new GoToStateTransition(targetState);
+ sourceState->addTransition(trans);
+ } else {
+ trans->setTargetState(targetState);
+ }
+
+ processEvents(QueuedProcessing);
+}
+
+void QStateMachinePrivate::registerTransitions(QAbstractState *state)
+{
+ QState *group = toStandardState(state);
+ if (!group)
+ return;
+ QList<QAbstractTransition*> transitions = QStatePrivate::get(group)->transitions();
+ for (int i = 0; i < transitions.size(); ++i) {
+ QAbstractTransition *t = transitions.at(i);
+ registerTransition(t);
+ }
+}
+
+void QStateMachinePrivate::maybeRegisterTransition(QAbstractTransition *transition)
+{
+ if (QSignalTransition *st = qobject_cast<QSignalTransition*>(transition)) {
+ maybeRegisterSignalTransition(st);
+ }
+#if QT_CONFIG(qeventtransition)
+ else if (QEventTransition *et = qobject_cast<QEventTransition*>(transition)) {
+ maybeRegisterEventTransition(et);
+ }
+#endif
+}
+
+void QStateMachinePrivate::registerTransition(QAbstractTransition *transition)
+{
+ if (QSignalTransition *st = qobject_cast<QSignalTransition*>(transition)) {
+ registerSignalTransition(st);
+ }
+#if QT_CONFIG(qeventtransition)
+ else if (QEventTransition *oet = qobject_cast<QEventTransition*>(transition)) {
+ registerEventTransition(oet);
+ }
+#endif
+}
+
+void QStateMachinePrivate::unregisterTransition(QAbstractTransition *transition)
+{
+ if (QSignalTransition *st = qobject_cast<QSignalTransition*>(transition)) {
+ unregisterSignalTransition(st);
+ }
+#if QT_CONFIG(qeventtransition)
+ else if (QEventTransition *oet = qobject_cast<QEventTransition*>(transition)) {
+ unregisterEventTransition(oet);
+ }
+#endif
+}
+
+void QStateMachinePrivate::maybeRegisterSignalTransition(QSignalTransition *transition)
+{
+ Q_Q(QStateMachine);
+ const QObject *senderObject =
+ QSignalTransitionPrivate::get(transition)->senderObject.valueBypassingBindings();
+ if ((state == Running) && (configuration.contains(transition->sourceState())
+ || (senderObject && (senderObject->thread() != q->thread())))) {
+ registerSignalTransition(transition);
+ }
+}
+
+void QStateMachinePrivate::registerSignalTransition(QSignalTransition *transition)
+{
+ Q_Q(QStateMachine);
+ if (QSignalTransitionPrivate::get(transition)->signalIndex != -1)
+ return; // already registered
+ const QObject *sender =
+ QSignalTransitionPrivate::get(transition)->senderObject.valueBypassingBindings();
+ if (!sender)
+ return;
+ QByteArray signal = QSignalTransitionPrivate::get(transition)->signal.valueBypassingBindings();
+ if (signal.isEmpty())
+ return;
+ if (signal.startsWith('0'+QSIGNAL_CODE))
+ signal.remove(0, 1);
+ const QMetaObject *meta = sender->metaObject();
+ int signalIndex = meta->indexOfSignal(signal);
+ int originalSignalIndex = signalIndex;
+ if (signalIndex == -1) {
+ signalIndex = meta->indexOfSignal(QMetaObject::normalizedSignature(signal));
+ if (signalIndex == -1) {
+ qWarning("QSignalTransition: no such signal: %s::%s",
+ meta->className(), signal.constData());
+ return;
+ }
+ originalSignalIndex = signalIndex;
+ }
+ // The signal index we actually want to connect to is the one
+ // that is going to be sent, i.e. the non-cloned original index.
+ while (meta->method(signalIndex).attributes() & QMetaMethod::Cloned)
+ --signalIndex;
+
+ connectionsMutex.lock();
+ QList<int> &connectedSignalIndexes = connections[sender];
+ if (connectedSignalIndexes.size() <= signalIndex)
+ connectedSignalIndexes.resize(signalIndex+1);
+ if (connectedSignalIndexes.at(signalIndex) == 0) {
+ if (!signalEventGenerator)
+ signalEventGenerator = new QSignalEventGenerator(q);
+ static const int generatorMethodOffset = QSignalEventGenerator::staticMetaObject.methodOffset();
+ bool ok = QMetaObject::connect(sender, signalIndex, signalEventGenerator, generatorMethodOffset);
+ 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];
+ connectionsMutex.unlock();
+
+ QSignalTransitionPrivate::get(transition)->signalIndex = signalIndex;
+ QSignalTransitionPrivate::get(transition)->originalSignalIndex = originalSignalIndex;
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": added signal transition from" << transition->sourceState()
+ << ": ( sender =" << sender << ", signal =" << signal
+ << ", targets =" << transition->targetStates() << ')';
+#endif
+}
+
+void QStateMachinePrivate::unregisterSignalTransition(QSignalTransition *transition)
+{
+ int signalIndex = QSignalTransitionPrivate::get(transition)->signalIndex;
+ if (signalIndex == -1)
+ return; // not registered
+ const QObject *sender =
+ QSignalTransitionPrivate::get(transition)->senderObject.valueBypassingBindings();
+ QSignalTransitionPrivate::get(transition)->signalIndex = -1;
+
+ connectionsMutex.lock();
+ QList<int> &connectedSignalIndexes = connections[sender];
+ Q_ASSERT(connectedSignalIndexes.size() > signalIndex);
+ Q_ASSERT(connectedSignalIndexes.at(signalIndex) != 0);
+ if (--connectedSignalIndexes[signalIndex] == 0) {
+ Q_ASSERT(signalEventGenerator != nullptr);
+ static const int generatorMethodOffset = QSignalEventGenerator::staticMetaObject.methodOffset();
+ QMetaObject::disconnect(sender, signalIndex, signalEventGenerator, generatorMethodOffset);
+ int sum = 0;
+ for (int i = 0; i < connectedSignalIndexes.size(); ++i)
+ sum += connectedSignalIndexes.at(i);
+ if (sum == 0)
+ connections.remove(sender);
+ }
+ connectionsMutex.unlock();
+}
+
+void QStateMachinePrivate::unregisterAllTransitions()
+{
+ Q_Q(QStateMachine);
+ {
+ QList<QSignalTransition*> transitions = rootState()->findChildren<QSignalTransition*>();
+ for (int i = 0; i < transitions.size(); ++i) {
+ QSignalTransition *t = transitions.at(i);
+ if (t->machine() == q)
+ unregisterSignalTransition(t);
+ }
+ }
+#if QT_CONFIG(qeventtransition)
+ {
+ QList<QEventTransition*> transitions = rootState()->findChildren<QEventTransition*>();
+ for (int i = 0; i < transitions.size(); ++i) {
+ QEventTransition *t = transitions.at(i);
+ if (t->machine() == q)
+ unregisterEventTransition(t);
+ }
+ }
+#endif
+}
+
+#if QT_CONFIG(qeventtransition)
+void QStateMachinePrivate::maybeRegisterEventTransition(QEventTransition *transition)
+{
+ if ((state == Running) && configuration.contains(transition->sourceState()))
+ registerEventTransition(transition);
+}
+
+void QStateMachinePrivate::registerEventTransition(QEventTransition *transition)
+{
+ Q_Q(QStateMachine);
+ if (QEventTransitionPrivate::get(transition)->registered)
+ return;
+ const QEvent::Type eventType =
+ QEventTransitionPrivate::get(transition)->eventType.valueBypassingBindings();
+ if (eventType >= QEvent::User) {
+ qWarning("QObject event transitions are not supported for custom types");
+ return;
+ }
+ QObject *object = QEventTransitionPrivate::get(transition)->object.valueBypassingBindings();
+ if (!object)
+ return;
+ QObjectPrivate *od = QObjectPrivate::get(object);
+ if (!od->extraData || !od->extraData->eventFilters.contains(q))
+ object->installEventFilter(q);
+ ++qobjectEvents[object][eventType];
+ QEventTransitionPrivate::get(transition)->registered = true;
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q << ": added event transition from" << transition->sourceState()
+ << ": ( object =" << object << ", event =" << 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.valueBypassingBindings();
+ QHash<QEvent::Type, int> &events = qobjectEvents[object];
+ const QEvent::Type eventType =
+ QEventTransitionPrivate::get(transition)->eventType.valueBypassingBindings();
+ Q_ASSERT(events.value(eventType) > 0);
+ if (--events[eventType] == 0) {
+ events.remove(eventType);
+ int sum = 0;
+ QHash<QEvent::Type, int>::const_iterator it;
+ for (it = events.constBegin(); it != events.constEnd(); ++it)
+ sum += it.value();
+ if (sum == 0) {
+ qobjectEvents.remove(object);
+ object->removeEventFilter(q);
+ }
+ }
+ QEventTransitionPrivate::get(transition)->registered = false;
+}
+
+void QStateMachinePrivate::handleFilteredEvent(QObject *watched, QEvent *event)
+{
+ if (qobjectEvents.value(watched).contains(event->type())) {
+ postInternalEvent(new QStateMachine::WrappedEvent(watched, event->clone()));
+ processEvents(DirectProcessing);
+ }
+}
+#endif
+
+void QStateMachinePrivate::handleTransitionSignal(QObject *sender, int signalIndex,
+ void **argv)
+{
+#ifndef QT_NO_DEBUG
+ connectionsMutex.lock();
+ Q_ASSERT(connections[sender].at(signalIndex) != 0);
+ connectionsMutex.unlock();
+#endif
+ const QMetaObject *meta = sender->metaObject();
+ QMetaMethod method = meta->method(signalIndex);
+ int argc = method.parameterCount();
+ QList<QVariant> vargs;
+ vargs.reserve(argc);
+ for (int i = 0; i < argc; ++i) {
+ auto type = method.parameterMetaType(i);
+ vargs.append(QVariant(type, argv[i+1]));
+ }
+
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << q_func() << ": sending signal event ( sender =" << sender
+ << ", signal =" << method.methodSignature().constData() << ')';
+#endif
+ postInternalEvent(new QStateMachine::SignalEvent(sender, signalIndex, vargs));
+ processEvents(DirectProcessing);
+}
+
+/*!
+ Constructs a new state machine with the given \a parent.
+*/
+QStateMachine::QStateMachine(QObject *parent)
+ : QState(*new QStateMachinePrivate, /*parentState=*/nullptr)
+{
+ // Can't pass the parent to the QState constructor, as it expects a QState
+ // But this works as expected regardless of whether parent is a QState or not
+ setParent(parent);
+}
+
+/*!
+ \since 5.0
+ \deprecated
+
+ Constructs a new state machine with the given \a childMode
+ and \a parent.
+
+ \warning Do not set the \a childMode to anything else than \l{ExclusiveStates}, otherwise the
+ state machine is invalid, and might work incorrectly.
+*/
+QStateMachine::QStateMachine(QState::ChildMode childMode, QObject *parent)
+ : QState(*new QStateMachinePrivate, /*parentState=*/nullptr)
+{
+ Q_D(QStateMachine);
+ d->childMode = childMode;
+ setParent(parent); // See comment in constructor above
+
+ if (childMode != ExclusiveStates) {
+ //### FIXME for Qt6: remove this constructor completely, and hide the childMode property.
+ // Yes, the StateMachine itself is conceptually a state, but it should only expose a limited
+ // number of properties. The execution algorithm (in the URL below) treats a state machine
+ // as a state, but from an API point of view, it's questionable if the QStateMachine should
+ // inherit from QState.
+ //
+ // See function findLCCA in https://www.w3.org/TR/2014/WD-scxml-20140529/#AlgorithmforSCXMLInterpretation
+ // to see where setting childMode to parallel will break down.
+ qWarning() << "Invalid childMode for QStateMachine" << this;
+ }
+}
+
+/*!
+ \internal
+*/
+QStateMachine::QStateMachine(QStateMachinePrivate &dd, QObject *parent)
+ : QState(dd, /*parentState=*/nullptr)
+{
+ setParent(parent);
+}
+
+/*!
+ Destroys this state machine.
+*/
+QStateMachine::~QStateMachine()
+{
+}
+
+/*!
+ \enum QStateMachine::EventPriority
+
+ This enum type specifies the priority of an event posted to the state
+ machine using postEvent().
+
+ Events of high priority are processed before events of normal priority.
+
+ \value NormalPriority The event has normal priority.
+ \value HighPriority The event has high priority.
+*/
+
+/*! \enum QStateMachine::Error
+
+ This enum type defines errors that can occur in the state machine at run time. When the state
+ machine encounters an unrecoverable error at run time, it will set the error code returned
+ by error(), the error message returned by errorString(), and enter an error state based on
+ the context of the error.
+
+ \value NoError No error has occurred.
+ \value NoInitialStateError The machine has entered a QState with children which does not have an
+ initial state set. The context of this error is the state which is missing an initial
+ state.
+ \value NoDefaultStateInHistoryStateError The machine has entered a QHistoryState which does not have
+ a default state set. The context of this error is the QHistoryState which is missing a
+ default state.
+ \value NoCommonAncestorForTransitionError The machine has selected a transition whose source
+ and targets are not part of the same tree of states, and thus are not part of the same
+ state machine. Commonly, this could mean that one of the states has not been given
+ any parent or added to any machine. The context of this error is the source state of
+ the transition.
+ \value StateMachineChildModeSetToParallelError The machine's \l childMode
+ property was set to \l{QState::ParallelStates}. This is illegal.
+ Only states may be declared as parallel, not the state machine
+ itself. This enum value was added in Qt 5.14.
+
+ \sa setErrorState()
+*/
+
+/*!
+ 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->error = NoError;
+ d->errorString = QString();
+}
+
+QBindable<QString> QStateMachine::bindableErrorString() const
+{
+ Q_D(const QStateMachine);
+ return &d->errorString;
+}
+
+/*!
+ Returns the restore policy of the state machine.
+
+ \sa setGlobalRestorePolicy()
+*/
+QState::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 QState::DontRestoreProperties.
+
+ \sa globalRestorePolicy()
+*/
+void QStateMachine::setGlobalRestorePolicy(QState::RestorePolicy restorePolicy)
+{
+ Q_D(QStateMachine);
+ d->globalRestorePolicy = restorePolicy;
+}
+
+QBindable<QState::RestorePolicy> QStateMachine::bindableGlobalRestorePolicy()
+{
+ Q_D(QStateMachine);
+ return &d->globalRestorePolicy;
+}
+
+/*!
+ Adds the given \a state to this state machine. The state becomes a top-level
+ state and the state machine takes ownership of the state.
+
+ If the state is already in a different machine, it will first be removed
+ from its old machine, and then added to this machine.
+
+ \sa removeState(), setInitialState()
+*/
+void QStateMachine::addState(QAbstractState *state)
+{
+ if (!state) {
+ qWarning("QStateMachine::addState: cannot add null state");
+ return;
+ }
+ if (QAbstractStatePrivate::get(state)->machine() == this) {
+ qWarning("QStateMachine::addState: state has already been added to this machine");
+ return;
+ }
+ state->setParent(this);
+}
+
+/*!
+ Removes the given \a state from this state machine. The state machine
+ releases ownership of the state.
+
+ \sa addState()
+*/
+void QStateMachine::removeState(QAbstractState *state)
+{
+ if (!state) {
+ qWarning("QStateMachine::removeState: cannot remove null state");
+ return;
+ }
+ if (QAbstractStatePrivate::get(state)->machine() != this) {
+ qWarning("QStateMachine::removeState: state %p's machine (%p)"
+ " is different from this machine (%p)",
+ state, QAbstractStatePrivate::get(state)->machine(), this);
+ return;
+ }
+ state->setParent(nullptr);
+}
+
+bool QStateMachine::isRunning() const
+{
+ Q_D(const QStateMachine);
+ return (d->state == QStateMachinePrivate::Running);
+}
+
+/*!
+ Starts this state machine. The machine will reset its configuration and
+ transition to the initial state. When a final top-level state (QFinalState)
+ is entered, the machine will emit the finished() signal.
+
+ \note A state machine will not run without a running event loop, such as
+ the main application event loop started with QCoreApplication::exec() or
+ QApplication::exec().
+
+ \sa started(), finished(), stop(), initialState(), setRunning()
+*/
+void QStateMachine::start()
+{
+ Q_D(QStateMachine);
+
+ if ((childMode() == QState::ExclusiveStates) && (initialState() == nullptr)) {
+ 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(), setRunning()
+*/
+void QStateMachine::stop()
+{
+ Q_D(QStateMachine);
+ switch (d->state) {
+ case QStateMachinePrivate::NotRunning:
+ break;
+ case QStateMachinePrivate::Starting:
+ // the machine will exit as soon as it enters the event processing loop
+ d->stop = true;
+ break;
+ case QStateMachinePrivate::Running:
+ d->stop = true;
+ d->processEvents(QStateMachinePrivate::QueuedProcessing);
+ break;
+ }
+}
+
+void QStateMachine::setRunning(bool running)
+{
+ if (running)
+ start();
+ else
+ stop();
+}
+
+/*!
+ \threadsafe
+
+ Posts the given \a event of the given \a priority for processing by this
+ state machine.
+
+ This function returns immediately. The event is added to the state machine's
+ event queue. Events are processed in the order posted. The state machine
+ takes ownership of the event and deletes it once it has been processed.
+
+ You can only post events when the state machine is running or when it is starting up.
+
+ \sa postDelayedEvent()
+*/
+void QStateMachine::postEvent(QEvent *event, EventPriority priority)
+{
+ Q_D(QStateMachine);
+ switch (d->state) {
+ case QStateMachinePrivate::Running:
+ case QStateMachinePrivate::Starting:
+ break;
+ default:
+ qWarning("QStateMachine::postEvent: cannot post event when the state machine is not running");
+ return;
+ }
+ if (!event) {
+ qWarning("QStateMachine::postEvent: cannot post null event");
+ return;
+ }
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << this << ": posting event" << event;
+#endif
+ switch (priority) {
+ case NormalPriority:
+ d->postExternalEvent(event);
+ break;
+ case HighPriority:
+ d->postInternalEvent(event);
+ break;
+ }
+ d->processEvents(QStateMachinePrivate::QueuedProcessing);
+}
+
+/*!
+ \threadsafe
+
+ Posts the given \a event for processing by this state machine, with the
+ given \a delay in milliseconds. Returns an identifier associated with the
+ delayed event, or -1 if the event could not be posted.
+
+ This function returns immediately. When the delay has expired, the event
+ will be added to the state machine's event queue for processing. The state
+ machine takes ownership of the event and deletes it once it has been
+ processed.
+
+ You can only post events when the state machine is running.
+
+ \sa cancelDelayedEvent(), postEvent()
+*/
+int QStateMachine::postDelayedEvent(QEvent *event, int delay)
+{
+ Q_D(QStateMachine);
+ if (d->state != QStateMachinePrivate::Running) {
+ qWarning("QStateMachine::postDelayedEvent: cannot post event when the state machine is not running");
+ return -1;
+ }
+ if (!event) {
+ qWarning("QStateMachine::postDelayedEvent: cannot post null event");
+ return -1;
+ }
+ if (delay < 0) {
+ qWarning("QStateMachine::postDelayedEvent: delay cannot be negative");
+ return -1;
+ }
+#ifdef QSTATEMACHINE_DEBUG
+ qDebug() << this << ": posting event" << event << "with delay" << delay;
+#endif
+ QMutexLocker locker(&d->delayedEventsMutex);
+ int id = d->delayedEventIdFreeList.next();
+ bool inMachineThread = (QThread::currentThread() == thread());
+ int timerId = inMachineThread ? startTimer(delay) : 0;
+ if (inMachineThread && !timerId) {
+ qWarning("QStateMachine::postDelayedEvent: failed to start timer with interval %d", delay);
+ d->delayedEventIdFreeList.release(id);
+ return -1;
+ }
+ QStateMachinePrivate::DelayedEvent delayedEvent(event, timerId);
+ d->delayedEvents.insert(id, delayedEvent);
+ if (timerId) {
+ d->timerIdToDelayedEventId.insert(timerId, id);
+ } else {
+ Q_ASSERT(!inMachineThread);
+ QMetaObject::invokeMethod(this, "_q_startDelayedEventTimer",
+ Qt::QueuedConnection,
+ Q_ARG(int, id),
+ Q_ARG(int, delay));
+ }
+ return id;
+}
+
+/*!
+ \threadsafe
+
+ Cancels the delayed event identified by the given \a id. The id should be a
+ value returned by a call to postDelayedEvent(). Returns \c true if the event
+ was successfully cancelled, otherwise returns \c false.
+
+ \sa postDelayedEvent()
+*/
+bool QStateMachine::cancelDelayedEvent(int id)
+{
+ Q_D(QStateMachine);
+ if (d->state != QStateMachinePrivate::Running) {
+ qWarning("QStateMachine::cancelDelayedEvent: the machine is not running");
+ return false;
+ }
+ QMutexLocker locker(&d->delayedEventsMutex);
+ QStateMachinePrivate::DelayedEvent e = d->delayedEvents.take(id);
+ if (!e.event)
+ return false;
+ if (e.timerId) {
+ d->timerIdToDelayedEventId.remove(e.timerId);
+ bool inMachineThread = (QThread::currentThread() == thread());
+ if (inMachineThread) {
+ killTimer(e.timerId);
+ d->delayedEventIdFreeList.release(id);
+ } else {
+ QMetaObject::invokeMethod(this, "_q_killDelayedEventTimer",
+ Qt::QueuedConnection,
+ Q_ARG(int, id),
+ Q_ARG(int, e.timerId));
+ }
+ } else {
+ // Cancellation will be detected in pending _q_startDelayedEventTimer() call
+ }
+ delete e.event;
+ return true;
+}
+
+/*!
+ Returns the maximal consistent set of states (including parallel and final
+ states) that this state machine is currently in. If a state \c s is in the
+ configuration, it is always the case that the parent of \c s is also in
+ c. Note, however, that the machine itself is not an explicit member of the
+ configuration.
+*/
+QSet<QAbstractState*> QStateMachine::configuration() const
+{
+ Q_D(const QStateMachine);
+ return d->configuration;
+}
+
+/*!
+ \fn QStateMachine::started()
+
+ This signal is emitted when the state machine has entered its initial state
+ (QStateMachine::initialState).
+
+ \sa QStateMachine::finished(), QStateMachine::start()
+*/
+
+/*!
+ \fn QStateMachine::stopped()
+
+ This signal is emitted when the state machine has stopped.
+
+ \sa QStateMachine::stop(), QStateMachine::finished()
+*/
+
+/*!
+ \reimp
+*/
+bool QStateMachine::event(QEvent *e)
+{
+ Q_D(QStateMachine);
+ if (e->type() == QEvent::Timer) {
+ QTimerEvent *te = static_cast<QTimerEvent*>(e);
+ int tid = te->timerId();
+ if (d->state != QStateMachinePrivate::Running) {
+ // This event has been cancelled already
+ QMutexLocker locker(&d->delayedEventsMutex);
+ Q_ASSERT(!d->timerIdToDelayedEventId.contains(tid));
+ return true;
+ }
+ d->delayedEventsMutex.lock();
+ int id = d->timerIdToDelayedEventId.take(tid);
+ QStateMachinePrivate::DelayedEvent ee = d->delayedEvents.take(id);
+ if (ee.event != nullptr) {
+ Q_ASSERT(ee.timerId == tid);
+ killTimer(tid);
+ d->delayedEventIdFreeList.release(id);
+ d->delayedEventsMutex.unlock();
+ d->postExternalEvent(ee.event);
+ d->processEvents(QStateMachinePrivate::DirectProcessing);
+ return true;
+ } else {
+ d->delayedEventsMutex.unlock();
+ }
+ }
+ return QState::event(e);
+}
+
+#if QT_CONFIG(qeventtransition)
+/*!
+ \reimp
+*/
+bool QStateMachine::eventFilter(QObject *watched, QEvent *event)
+{
+ Q_D(QStateMachine);
+ d->handleFilteredEvent(watched, event);
+ return false;
+}
+#endif
+
+/*!
+ \internal
+
+ This function is called when the state machine is about to select
+ transitions based on the given \a event.
+
+ The default implementation does nothing.
+*/
+void QStateMachine::beginSelectTransitions(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \internal
+
+ This function is called when the state machine has finished selecting
+ transitions based on the given \a event.
+
+ The default implementation does nothing.
+*/
+void QStateMachine::endSelectTransitions(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \internal
+
+ This function is called when the state machine is about to do a microstep.
+
+ The default implementation does nothing.
+*/
+void QStateMachine::beginMicrostep(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \internal
+
+ This function is called when the state machine has finished doing a
+ microstep.
+
+ The default implementation does nothing.
+*/
+void QStateMachine::endMicrostep(QEvent *event)
+{
+ Q_UNUSED(event);
+}
+
+/*!
+ \reimp
+ This function will call start() to start the state machine.
+*/
+void QStateMachine::onEntry(QEvent *event)
+{
+ start();
+ QState::onEntry(event);
+}
+
+/*!
+ \reimp
+ This function will call stop() to stop the state machine and
+ subsequently emit the stopped() signal.
+*/
+void QStateMachine::onExit(QEvent *event)
+{
+ stop();
+ QState::onExit(event);
+}
+
+#if QT_CONFIG(animation)
+
+/*!
+ Returns whether animations are enabled for this state machine.
+*/
+bool QStateMachine::isAnimated() const
+{
+ Q_D(const QStateMachine);
+ return d->animated;
+}
+
+/*!
+ Sets whether animations are \a enabled for this state machine.
+*/
+void QStateMachine::setAnimated(bool enabled)
+{
+ Q_D(QStateMachine);
+ d->animated = enabled;
+}
+
+QBindable<bool> QStateMachine::bindableAnimated()
+{
+ Q_D(QStateMachine);
+ return &d->animated;
+}
+
+/*!
+ 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 // animation
+
+void QSignalEventGenerator::execute(QMethodRawArguments a)
+{
+ auto machinePrivate = QStateMachinePrivate::get(qobject_cast<QStateMachine*>(parent()));
+ if (machinePrivate->state != QStateMachinePrivate::Running)
+ return;
+ int signalIndex = senderSignalIndex();
+ Q_ASSERT(signalIndex != -1);
+ machinePrivate->handleTransitionSignal(sender(), signalIndex, a.arguments);
+}
+
+QSignalEventGenerator::QSignalEventGenerator(QStateMachine *parent)
+ : QObject(parent)
+{
+}
+
+/*!
+ \class QStateMachine::SignalEvent
+ \inmodule QtStateMachine
+
+ \brief The SignalEvent class represents a Qt signal event.
+
+ \since 4.6
+ \ingroup statemachine
+
+ A signal event is generated by a QStateMachine in response to a Qt
+ signal. The QSignalTransition class provides a transition associated with a
+ signal event. QStateMachine::SignalEvent is part of \l{Qt State Machine Overview}
+ {Qt State Machine Framework}.
+
+ The sender() function returns the object that generated the signal. The
+ signalIndex() function returns the index of the signal. The arguments()
+ function returns the arguments of the signal.
+
+ \sa QSignalTransition
+*/
+
+/*!
+ \internal
+
+ Constructs a new SignalEvent object with the given \a sender, \a
+ signalIndex and \a arguments.
+*/
+QStateMachine::SignalEvent::SignalEvent(QObject *sender, int signalIndex,
+ const QList<QVariant> &arguments)
+ : QEvent(QEvent::StateMachineSignal), m_sender(sender),
+ m_signalIndex(signalIndex), m_arguments(arguments)
+{
+}
+
+/*!
+ Destroys this SignalEvent.
+*/
+QStateMachine::SignalEvent::~SignalEvent()
+{
+}
+
+/*!
+ \fn QStateMachine::SignalEvent::sender() const
+
+ Returns the object that emitted the signal.
+
+ \sa QObject::sender()
+*/
+
+/*!
+ \fn QStateMachine::SignalEvent::signalIndex() const
+
+ Returns the index of the signal.
+
+ \sa QMetaObject::indexOfSignal(), QMetaObject::method()
+*/
+
+/*!
+ \fn QStateMachine::SignalEvent::arguments() const
+
+ Returns the arguments of the signal.
+*/
+
+
+/*!
+ \class QStateMachine::WrappedEvent
+ \inmodule QtStateMachine
+
+ \brief The WrappedEvent class inherits QEvent and holds a clone of an event associated with a QObject.
+
+ \since 4.6
+ \ingroup statemachine
+
+ A wrapped event is generated by a QStateMachine in response to a Qt
+ event. The QEventTransition class provides a transition associated with a
+ such an event. QStateMachine::WrappedEvent is part of \l{Qt State Machine Overview}.
+
+ The object() function returns the object that generated the event. The
+ event() function returns a clone of the original event.
+
+ \sa QEventTransition
+*/
+
+/*!
+ \internal
+
+ Constructs a new WrappedEvent object with the given \a object
+ and \a event.
+
+ The WrappedEvent object takes ownership of \a event.
+*/
+QStateMachine::WrappedEvent::WrappedEvent(QObject *object, QEvent *event)
+ : QEvent(QEvent::StateMachineWrapped), m_object(object), m_event(event)
+{
+}
+
+/*!
+ Destroys this WrappedEvent.
+*/
+QStateMachine::WrappedEvent::~WrappedEvent()
+{
+ delete m_event;
+}
+
+/*!
+ \fn QStateMachine::WrappedEvent::object() const
+
+ Returns the object that the event is associated with.
+*/
+
+/*!
+ \fn QStateMachine::WrappedEvent::event() const
+
+ Returns a clone of the original event.
+*/
+
+/*!
+ \fn QStateMachine::runningChanged(bool running)
+ \since 5.4
+
+ This signal is emitted when the running property is changed with \a running as argument.
+
+ \sa QStateMachine::running
+*/
+
+/*!
+ \fn QStateMachine::postDelayedEvent(QEvent *event, std::chrono::milliseconds delay)
+ \since 5.15
+ \overload
+ \threadsafe
+
+ Posts the given \a event for processing by this state machine, with the
+ given \a delay in milliseconds. Returns an identifier associated with the
+ delayed event, or -1 if the event could not be posted.
+
+ This function returns immediately. When the delay has expired, the event
+ will be added to the state machine's event queue for processing. The state
+ machine takes ownership of the event and deletes it once it has been
+ processed.
+
+ You can only post events when the state machine is running.
+
+ \sa cancelDelayedEvent(), postEvent()
+*/
+
+QT_END_NAMESPACE
+
+#include "qstatemachine.moc"
+#include "moc_qstatemachine.cpp"
diff --git a/src/statemachine/qstatemachine.h b/src/statemachine/qstatemachine.h
new file mode 100644
index 0000000..4483e80
--- /dev/null
+++ b/src/statemachine/qstatemachine.h
@@ -0,0 +1,168 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSTATEMACHINE_H
+#define QSTATEMACHINE_H
+
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qset.h>
+#include <QtCore/qvariant.h>
+
+#include <QtStateMachine/qstate.h>
+
+#if __has_include(<chrono>)
+# include <chrono>
+#endif
+
+QT_REQUIRE_CONFIG(statemachine);
+
+QT_BEGIN_NAMESPACE
+
+class QStateMachinePrivate;
+class QAbstractAnimation;
+class Q_STATEMACHINE_EXPORT QStateMachine : public QState
+{
+ Q_OBJECT
+ Q_PROPERTY(QString errorString READ errorString BINDABLE bindableErrorString)
+ Q_PROPERTY(QState::RestorePolicy globalRestorePolicy READ globalRestorePolicy
+ WRITE setGlobalRestorePolicy BINDABLE bindableGlobalRestorePolicy)
+ Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
+#if QT_CONFIG(animation)
+ Q_PROPERTY(bool animated READ isAnimated WRITE setAnimated BINDABLE bindableAnimated)
+#endif
+public:
+ class Q_STATEMACHINE_EXPORT SignalEvent : public QEvent
+ {
+ public:
+ SignalEvent(QObject *sender, int signalIndex,
+ const QList<QVariant> &arguments);
+ ~SignalEvent();
+
+ inline QObject *sender() const { return m_sender; }
+ inline int signalIndex() const { return m_signalIndex; }
+ inline QList<QVariant> arguments() const { return m_arguments; }
+
+ private:
+ QObject *m_sender;
+ int m_signalIndex;
+ QList<QVariant> m_arguments;
+
+ friend class QSignalTransitionPrivate;
+ };
+
+ class Q_STATEMACHINE_EXPORT WrappedEvent : public QEvent
+ {
+ public:
+ WrappedEvent(QObject *object, QEvent *event);
+ ~WrappedEvent();
+
+ inline QObject *object() const { return m_object; }
+ inline QEvent *event() const { return m_event; }
+
+ private:
+ QObject *m_object;
+ QEvent *m_event;
+ };
+
+ enum EventPriority {
+ NormalPriority,
+ HighPriority
+ };
+
+ enum Error {
+ NoError,
+ NoInitialStateError,
+ NoDefaultStateInHistoryStateError,
+ NoCommonAncestorForTransitionError,
+ StateMachineChildModeSetToParallelError
+ };
+
+ explicit QStateMachine(QObject *parent = nullptr);
+ explicit QStateMachine(QState::ChildMode childMode, QObject *parent = nullptr);
+ ~QStateMachine();
+
+ void addState(QAbstractState *state);
+ void removeState(QAbstractState *state);
+
+ Error error() const;
+
+ QString errorString() const;
+ void clearError();
+ QBindable<QString> bindableErrorString() const;
+
+ bool isRunning() const;
+
+#if QT_CONFIG(animation)
+ bool isAnimated() const;
+ void setAnimated(bool enabled);
+ QBindable<bool> bindableAnimated();
+
+ void addDefaultAnimation(QAbstractAnimation *animation);
+ QList<QAbstractAnimation *> defaultAnimations() const;
+ void removeDefaultAnimation(QAbstractAnimation *animation);
+#endif // animation
+
+ QState::RestorePolicy globalRestorePolicy() const;
+ void setGlobalRestorePolicy(QState::RestorePolicy restorePolicy);
+ QBindable<QState::RestorePolicy> bindableGlobalRestorePolicy();
+
+ void postEvent(QEvent *event, EventPriority priority = NormalPriority);
+ int postDelayedEvent(QEvent *event, int delay);
+ bool cancelDelayedEvent(int id);
+
+ QSet<QAbstractState*> configuration() const;
+
+#if QT_CONFIG(qeventtransition)
+ bool eventFilter(QObject *watched, QEvent *event) override;
+#endif
+
+#if __has_include(<chrono>) || defined(Q_QDOC)
+ int postDelayedEvent(QEvent *event, std::chrono::milliseconds delay)
+ {
+ return postDelayedEvent(event, int(delay.count()));
+ }
+#endif
+
+public Q_SLOTS:
+ void start();
+ void stop();
+ void setRunning(bool running);
+
+Q_SIGNALS:
+ void started(QPrivateSignal);
+ void stopped(QPrivateSignal);
+ void runningChanged(bool running);
+
+
+protected:
+ void onEntry(QEvent *event) override;
+ void onExit(QEvent *event) override;
+
+ virtual void beginSelectTransitions(QEvent *event);
+ virtual void endSelectTransitions(QEvent *event);
+
+ virtual void beginMicrostep(QEvent *event);
+ virtual void endMicrostep(QEvent *event);
+
+ bool event(QEvent *e) override;
+
+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())
+#if QT_CONFIG(animation)
+ Q_PRIVATE_SLOT(d_func(), void _q_animationFinished())
+#endif
+ Q_PRIVATE_SLOT(d_func(), void _q_startDelayedEventTimer(int, int))
+ Q_PRIVATE_SLOT(d_func(), void _q_killDelayedEventTimer(int, int))
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/qstatemachine_p.h b/src/statemachine/qstatemachine_p.h
new file mode 100644
index 0000000..8f5201e
--- /dev/null
+++ b/src/statemachine/qstatemachine_p.h
@@ -0,0 +1,305 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSTATEMACHINE_P_H
+#define QSTATEMACHINE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qstate_p.h"
+
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qpointer.h>
+#include <QtCore/qset.h>
+
+#include <QtCore/private/qfreelist_p.h>
+
+QT_REQUIRE_CONFIG(statemachine);
+
+QT_BEGIN_NAMESPACE
+
+class QEvent;
+#if QT_CONFIG(qeventtransition)
+class QEventTransition;
+#endif
+class QSignalEventGenerator;
+class QSignalTransition;
+class QAbstractState;
+class QAbstractTransition;
+class QFinalState;
+class QHistoryState;
+class QState;
+
+#if QT_CONFIG(animation)
+class QAbstractAnimation;
+#endif
+
+struct CalculationCache;
+class QStateMachine;
+class Q_STATEMACHINE_EXPORT QStateMachinePrivate : public QStatePrivate
+{
+ Q_DECLARE_PUBLIC(QStateMachine)
+public:
+ enum State {
+ NotRunning,
+ Starting,
+ Running
+ };
+ enum EventProcessingMode {
+ DirectProcessing,
+ QueuedProcessing
+ };
+ enum StopProcessingReason {
+ EventQueueEmpty,
+ Finished,
+ Stopped
+ };
+
+ QStateMachinePrivate();
+ ~QStateMachinePrivate();
+
+ static QStateMachinePrivate *get(QStateMachine *q)
+ { return q ? q->d_func() : nullptr; }
+
+ QState *findLCA(const QList<QAbstractState*> &states, bool onlyCompound = false);
+ QState *findLCCA(const QList<QAbstractState*> &states);
+
+ static bool transitionStateEntryLessThan(QAbstractTransition *t1, QAbstractTransition *t2);
+ 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();
+#if QT_CONFIG(animation)
+ void _q_animationFinished();
+#endif
+ void _q_startDelayedEventTimer(int id, int delay);
+ void _q_killDelayedEventTimer(int id, int timerId);
+
+ QState *rootState() const;
+
+ void clearHistory();
+ QAbstractTransition *createInitialTransition() const;
+
+ void removeConflictingTransitions(QList<QAbstractTransition*> &enabledTransitions, CalculationCache *cache);
+ void microstep(QEvent *event, const QList<QAbstractTransition*> &transitionList, CalculationCache *cache);
+ QList<QAbstractTransition *> selectTransitions(QEvent *event, CalculationCache *cache);
+ virtual void noMicrostep();
+ virtual void processedPendingEvents(bool didChange);
+ virtual void beginMacrostep();
+ virtual void endMacrostep(bool didChange);
+ virtual void exitInterpreter();
+ virtual void exitStates(QEvent *event, const QList<QAbstractState *> &statesToExit_sorted,
+ const QHash<QAbstractState *, QList<QPropertyAssignment>> &assignmentsForEnteredStates);
+ QList<QAbstractState*> computeExitSet(const QList<QAbstractTransition*> &enabledTransitions, CalculationCache *cache);
+ QSet<QAbstractState*> computeExitSet_Unordered(const QList<QAbstractTransition*> &enabledTransitions, CalculationCache *cache);
+ QSet<QAbstractState*> computeExitSet_Unordered(QAbstractTransition *t, CalculationCache *cache);
+ void executeTransitionContent(QEvent *event, const QList<QAbstractTransition*> &transitionList);
+ virtual void enterStates(QEvent *event, const QList<QAbstractState*> &exitedStates_sorted,
+ const QList<QAbstractState*> &statesToEnter_sorted,
+ const QSet<QAbstractState*> &statesForDefaultEntry,
+ QHash<QAbstractState *, QList<QPropertyAssignment>> &propertyAssignmentsForState
+#if QT_CONFIG(animation)
+ , const QList<QAbstractAnimation*> &selectedAnimations
+#endif
+ );
+ QList<QAbstractState*> computeEntrySet(const QList<QAbstractTransition*> &enabledTransitions,
+ QSet<QAbstractState*> &statesForDefaultEntry, CalculationCache *cache);
+ QAbstractState *getTransitionDomain(QAbstractTransition *t,
+ const QList<QAbstractState *> &effectiveTargetStates,
+ CalculationCache *cache);
+ void addDescendantStatesToEnter(QAbstractState *state,
+ QSet<QAbstractState*> &statesToEnter,
+ QSet<QAbstractState*> &statesForDefaultEntry);
+ void addAncestorStatesToEnter(QAbstractState *s, QAbstractState *ancestor,
+ QSet<QAbstractState*> &statesToEnter,
+ QSet<QAbstractState*> &statesForDefaultEntry);
+
+ static QState *toStandardState(QAbstractState *state);
+ static const QState *toStandardState(const QAbstractState *state);
+ static QFinalState *toFinalState(QAbstractState *state);
+ static QHistoryState *toHistoryState(QAbstractState *state);
+
+ bool isInFinalState(QAbstractState *s) const;
+ static bool isFinal(const QAbstractState *s);
+ static bool isParallel(const QAbstractState *s);
+ bool isCompound(const QAbstractState *s) const;
+ bool isAtomic(const QAbstractState *s) const;
+
+ void goToState(QAbstractState *targetState);
+
+ void registerTransitions(QAbstractState *state);
+ void maybeRegisterTransition(QAbstractTransition *transition);
+ void registerTransition(QAbstractTransition *transition);
+ void maybeRegisterSignalTransition(QSignalTransition *transition);
+ void registerSignalTransition(QSignalTransition *transition);
+ void unregisterSignalTransition(QSignalTransition *transition);
+ void registerMultiThreadedSignalTransitions();
+#if QT_CONFIG(qeventtransition)
+ void maybeRegisterEventTransition(QEventTransition *transition);
+ void registerEventTransition(QEventTransition *transition);
+ void unregisterEventTransition(QEventTransition *transition);
+ void handleFilteredEvent(QObject *watched, QEvent *event);
+#endif
+ void unregisterTransition(QAbstractTransition *transition);
+ void unregisterAllTransitions();
+ void handleTransitionSignal(QObject *sender, int signalIndex,
+ void **args);
+
+ void postInternalEvent(QEvent *e);
+ void postExternalEvent(QEvent *e);
+ QEvent *dequeueInternalEvent();
+ QEvent *dequeueExternalEvent();
+ bool isInternalEventQueueEmpty();
+ bool isExternalEventQueueEmpty();
+ void processEvents(EventProcessingMode processingMode);
+ void cancelAllDelayedEvents();
+
+ virtual void emitStateFinished(QState *forState, QFinalState *guiltyState);
+ virtual void startupHook();
+
+#ifndef QT_NO_PROPERTIES
+ class RestorableId {
+ QPointer<QObject> guard;
+ QObject *obj;
+ QByteArray prop;
+ friend size_t qHash(const RestorableId &key, size_t seed)
+ noexcept(noexcept(qHash(std::declval<QByteArray>())))
+ { return qHash(qMakePair(key.obj, key.prop), seed); }
+ friend bool operator==(const RestorableId &lhs, const RestorableId &rhs) noexcept
+ { return lhs.obj == rhs.obj && lhs.prop == rhs.prop; }
+ friend bool operator!=(const RestorableId &lhs, const RestorableId &rhs) noexcept
+ { return !operator==(lhs, rhs); }
+ public:
+ explicit RestorableId(QObject *o, QByteArray p) noexcept : guard(o), obj(o), prop(std::move(p)) {}
+ QObject *object() const noexcept { return guard; }
+ QByteArray propertyName() const noexcept { return prop; }
+ };
+ QHash<QAbstractState*, QHash<RestorableId, QVariant> > registeredRestorablesForState;
+ bool hasRestorable(QAbstractState *state, QObject *object, const QByteArray &propertyName) const;
+ QVariant savedValueForRestorable(const QList<QAbstractState*> &exitedStates_sorted,
+ QObject *object, const QByteArray &propertyName) const;
+ void registerRestorable(QAbstractState *state, QObject *object, const QByteArray &propertyName,
+ const QVariant &value);
+ void unregisterRestorables(const QList<QAbstractState*> &states, QObject *object,
+ const QByteArray &propertyName);
+ QList<QPropertyAssignment> restorablesToPropertyList(const QHash<RestorableId, QVariant> &restorables) const;
+ QHash<RestorableId, QVariant> computePendingRestorables(const QList<QAbstractState*> &statesToExit_sorted) const;
+ QHash<QAbstractState *, QList<QPropertyAssignment>> computePropertyAssignments(
+ const QList<QAbstractState*> &statesToEnter_sorted,
+ QHash<RestorableId, QVariant> &pendingRestorables) const;
+#endif
+
+ State state;
+ bool processing;
+ bool processingScheduled;
+ bool stop;
+ StopProcessingReason stopProcessingReason;
+ QSet<QAbstractState*> configuration;
+ QList<QEvent*> internalEventQueue;
+ QList<QEvent*> externalEventQueue;
+ QMutex internalEventMutex;
+ QMutex externalEventMutex;
+
+ QStateMachine::Error error;
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QStateMachinePrivate, QState::RestorePolicy,
+ globalRestorePolicy, QState::DontRestoreProperties);
+ Q_OBJECT_BINDABLE_PROPERTY(QStateMachinePrivate, QString, errorString);
+
+ QSet<QAbstractState *> pendingErrorStates;
+ QSet<QAbstractState *> pendingErrorStatesForDefaultEntry;
+
+#if QT_CONFIG(animation)
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QStateMachinePrivate, bool, animated, true);
+
+ struct InitializeAnimationResult {
+ QList<QAbstractAnimation*> handledAnimations;
+ QList<QAbstractAnimation*> localResetEndValues;
+
+ void swap(InitializeAnimationResult &other) noexcept
+ {
+ qSwap(handledAnimations, other.handledAnimations);
+ qSwap(localResetEndValues, other.localResetEndValues);
+ }
+ };
+
+ InitializeAnimationResult
+ 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;
+
+ QList<QAbstractAnimation *> selectAnimations(const QList<QAbstractTransition *> &transitionList) const;
+ void terminateActiveAnimations(QAbstractState *state,
+ const QHash<QAbstractState *, QList<QPropertyAssignment>> &assignmentsForEnteredStates);
+ void initializeAnimations(QAbstractState *state, const QList<QAbstractAnimation*> &selectedAnimations,
+ const QList<QAbstractState *> &exitedStates_sorted,
+ QHash<QAbstractState *, QList<QPropertyAssignment>> &assignmentsForEnteredStates);
+#endif // animation
+
+ QSignalEventGenerator *signalEventGenerator;
+
+ QHash<const QObject *, QList<int>> connections;
+ QMutex connectionsMutex;
+#if QT_CONFIG(qeventtransition)
+ QHash<QObject*, QHash<QEvent::Type, int> > qobjectEvents;
+#endif
+ struct FreeListDefaultConstants
+ {
+ // used by QFreeList, make sure to define all of when customizing
+ enum {
+ InitialNextValue = 0,
+ IndexMask = 0x00ffffff,
+ SerialMask = ~IndexMask & ~0x80000000,
+ SerialCounter = IndexMask + 1,
+ MaxIndex = IndexMask,
+ BlockCount = 4
+ };
+
+ static const int Sizes[BlockCount];
+ };
+ QFreeList<void, FreeListDefaultConstants> delayedEventIdFreeList;
+
+ struct DelayedEvent {
+ QEvent *event;
+ int timerId;
+ DelayedEvent(QEvent *e, int tid)
+ : event(e), timerId(tid) {}
+ DelayedEvent()
+ : event(nullptr), timerId(0) {}
+ };
+ QHash<int, DelayedEvent> delayedEvents;
+ QHash<int, int> timerIdToDelayedEventId;
+ QMutex delayedEventsMutex;
+};
+#if QT_CONFIG(animation)
+Q_DECLARE_SHARED(QStateMachinePrivate::InitializeAnimationResult)
+#endif
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachine/qstatemachineglobal.h b/src/statemachine/qstatemachineglobal.h
new file mode 100644
index 0000000..47860b7
--- /dev/null
+++ b/src/statemachine/qstatemachineglobal.h
@@ -0,0 +1,16 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSTATEMACHINEGLOBAL_H
+#define QSTATEMACHINEGLOBAL_H
+
+#include <QtCore/qglobal.h>
+#include <QtStateMachine/qtstatemachine-config.h>
+
+#if defined(BUILD_QSTATEMACHINE)
+# define Q_STATEMACHINE_EXPORT
+#else
+# include <QtStateMachine/qtstatemachineexports.h>
+#endif
+
+#endif // QSTATEMACHINEGLOBAL_H
diff --git a/src/statemachine/qt_cmdline.cmake b/src/statemachine/qt_cmdline.cmake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/statemachine/qt_cmdline.cmake